diff --git a/src/config/card.ts b/src/config/card.ts index ed84f47..0510cd0 100644 --- a/src/config/card.ts +++ b/src/config/card.ts @@ -10,29 +10,40 @@ export enum MODES { SWING = 'swing', } -type ControlItem = - | boolean - | { - name?: string | false - icon?: string | false - } +export type ModeValue = { + name?: string | false + icon?: string | false + include?: boolean +} -export type ControlField = Record & { +/** + * Represents the available mode values for a mode + * + */ +export type ModeControlObject = Record & { _name: string _hide_when_off: boolean } -type ControlObject = { - hvac: boolean | ControlField - fan: boolean | ControlField - preset: boolean | ControlField - swing: boolean | ControlField +/** + * Modes (hvac, fac, preset, swing) + * that might exist as attributes on a climate entity. + * Modes can be set to a value based on a list of options + * that are provided in the attributes of the entity. + * + */ +export type ModeControlValue = boolean | ModeControlObject +type ModeControl = { + hvac: ModeControlValue + fan: ModeControlValue + preset: ModeControlValue + swing: ModeControlValue } interface CardConfig { entity?: string header: false | HeaderConfig - control?: false | ControlObject | string[] + control?: false | ModeControl | string[] sensors?: false | Array setpoints?: Setpoints decimals?: number diff --git a/src/main.ts b/src/main.ts index 8ecdd96..96d67f4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -16,9 +16,16 @@ import parseHeader, { HeaderData, MODE_ICONS } from './config/header' import parseSetpoints from './config/setpoints' import parseService, { Service } from './config/service' -import { CardConfig, ControlField, MODES } from './config/card' +import { CardConfig, ModeValue, ModeControlObject, MODES } from './config/card' -import { ControlMode, LooseObject, Sensor, HASS, HVAC_MODES } from './types' +import { + ControlMode, + ControlModeOption, + LooseObject, + Sensor, + HASS, + HVAC_MODES, +} from './types' const DEBOUNCE_TIMEOUT = 1000 const STEP_SIZE = 0.5 @@ -50,42 +57,30 @@ const DEFAULT_HIDE = { state: false, } -function isIncluded(key: string, values: any) { - if (typeof values === 'undefined') { - return true - } - - if (Array.isArray(values)) { - return values.includes(key) - } - - const type = typeof values[key] - if (type === 'boolean') { - return values[key] - } else if (type === 'object') { - return values[key].include !== false +function shouldShowModeControl( + modeOption: string, + config: Partial +) { + if (typeof config[modeOption] === 'object') { + const obj = config[modeOption] as ModeValue + return obj.include !== false } - return true + return config?.[modeOption] ?? true } function getModeList( type: string, attributes: LooseObject, - config: LooseObject = {} + specification: Partial = {} ) { return attributes[`${type}_modes`] - .filter((name) => isIncluded(name, config)) - .map((name) => { - // Grab all values sans the possible include prop - // and stuff it into an object - const values = typeof config[name] === 'object' ? config[name] : {} - delete values.include + .filter((modeOption) => shouldShowModeControl(modeOption, specification)) + .map((modeOption) => { return { - icon: MODE_ICONS[name], - value: name, - name, - ...values, + icon: MODE_ICONS[modeOption], + value: modeOption, + name: modeOption, } }) } @@ -187,11 +182,12 @@ export default class SimpleThermostat extends LitElement { const buildBasicModes = (items: any) => { return items.filter(supportedModeType).map((type: string) => ({ type, - list: getModeList(type, attributes, {}), + hide_when_off: false, + list: getModeList(type, attributes), })) } - let controlModes: Array = [] + let controlModes: Array> = [] if (this.config.control === false) { controlModes = [] } else if (Array.isArray(this.config.control)) { @@ -201,13 +197,13 @@ export default class SimpleThermostat extends LitElement { if (entries.length > 0) { controlModes = entries .filter(([type]) => supportedModeType(type)) - .map(([type, definition]: [string, ControlField]) => { - const { _name, _hide_when_off, ...config } = definition + .map(([type, definition]: [string, ModeControlObject]) => { + const { _name, _hide_when_off, ...controlField } = definition return { type, hide_when_off: _hide_when_off, name: _name, - list: getModeList(type, attributes, config), + list: getModeList(type, attributes, controlField), } }) } else { @@ -220,9 +216,9 @@ export default class SimpleThermostat extends LitElement { // Decorate mode types with active value and set to this.modes this.modes = controlModes.map((values) => { if (values.type === MODES.HVAC) { - const sortedList: Array = [] - const hvacModeValues = Object.values(HVAC_MODES) - values.list.forEach((item: LooseObject) => { + const sortedList: Array> = [] + const hvacModeValues = Object.values(HVAC_MODES) as Array + values.list.forEach((item: ControlModeOption) => { const index = hvacModeValues.indexOf(item.value) sortedList[index] = item }) @@ -230,10 +226,10 @@ export default class SimpleThermostat extends LitElement { ...values, list: sortedList, mode: entity.state, - } + } as ControlMode } const mode = attributes[`${values.type}_mode`] - return { ...values, mode } + return { ...values, mode } as ControlMode }) if (this.config.step_size) { @@ -416,10 +412,10 @@ export default class SimpleThermostat extends LitElement { return 3 } - getUnit(): string | boolean | undefined { + getUnit(): string | boolean { if (['boolean', 'string'].includes(typeof this.config.unit)) { return this.config?.unit } - return this._hass.config.unit_system.temperature + return this._hass.config?.unit_system?.temperature ?? false } } diff --git a/src/types.ts b/src/types.ts index e462f03..5a6986a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,10 +29,15 @@ export enum HVAC_MODES { FAN_ONLY = 'fan_only', } +export interface ControlModeOption { + value: string + name: string + icon: string +} export interface ControlMode { type: string - mode?: any + mode: any name?: string | boolean hide_when_off?: boolean - list: Array> + list: Array }