Updates to alarm panel card configuration (#17598)

* Updates to alarm panel card configuration

* changes from feedback
This commit is contained in:
karwosts 2023-08-30 02:42:51 -07:00 committed by GitHub
parent ae9fcebfd5
commit 034ce56da5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 27 deletions

View File

@ -9,6 +9,7 @@ import {
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { HassEntity } from "home-assistant-js-websocket";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { fireEvent } from "../../../common/dom/fire_event";
import { alarmPanelIcon } from "../../../common/entity/alarm_panel_icon";
@ -20,16 +21,48 @@ import type { HaTextField } from "../../../components/ha-textfield";
import {
callAlarmAction,
FORMAT_NUMBER,
ALARM_MODES,
AlarmMode,
} from "../../../data/alarm_control_panel";
import { UNAVAILABLE } from "../../../data/entity";
import type { HomeAssistant } from "../../../types";
import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { LovelaceCard } from "../types";
import { AlarmPanelCardConfig } from "./types";
import { AlarmPanelCardConfig, AlarmPanelCardConfigState } from "./types";
import { supportsFeature } from "../../../common/entity/supports-feature";
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"];
export const DEFAULT_STATES = [
"arm_home",
"arm_away",
] as AlarmPanelCardConfigState[];
export const ALARM_MODE_STATE_MAP: Record<
AlarmPanelCardConfigState,
AlarmMode
> = {
arm_home: "armed_home",
arm_away: "armed_away",
arm_night: "armed_night",
arm_vacation: "armed_vacation",
arm_custom_bypass: "armed_custom_bypass",
};
export const filterSupportedAlarmStates = (
stateObj: HassEntity | undefined,
states: AlarmPanelCardConfigState[]
): AlarmPanelCardConfigState[] =>
states.filter(
(s) =>
stateObj &&
supportsFeature(
stateObj,
ALARM_MODES[ALARM_MODE_STATE_MAP[s]].feature || 0
)
);
@customElement("hui-alarm-panel-card")
class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
public static async getConfigElement() {
@ -52,10 +85,13 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
includeDomains
);
const entity = foundEntities[0] || "";
const stateObj = hass.states[entity];
return {
type: "alarm-panel",
states: ["arm_home", "arm_away"],
entity: foundEntities[0] || "",
states: filterSupportedAlarmStates(stateObj, DEFAULT_STATES),
entity,
};
}
@ -86,11 +122,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
throw new Error("Invalid configuration");
}
const defaults = {
states: ["arm_away", "arm_home"] as const,
};
this._config = { ...defaults, ...config };
this._config = { ...config };
}
protected updated(changedProps: PropertyValues): void {
@ -138,6 +170,9 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
return nothing;
}
const stateObj = this.hass.states[this._config.entity];
const states =
this._config.states ||
filterSupportedAlarmStates(stateObj, DEFAULT_STATES);
if (!stateObj) {
return html`
@ -170,7 +205,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
</h1>
<div id="armActions" class="actions">
${(stateObj.state === "disarmed"
? this._config.states!
? states
: (["disarm"] as const)
).map(
(stateAction) => html`

View File

@ -14,10 +14,17 @@ import { HaDurationData } from "../../../components/ha-duration-input";
import { LovelaceTileFeatureConfig } from "../tile-features/types";
import { ForecastType } from "../../../data/weather";
export type AlarmPanelCardConfigState =
| "arm_away"
| "arm_home"
| "arm_night"
| "arm_vacation"
| "arm_custom_bypass";
export interface AlarmPanelCardConfig extends LovelaceCardConfig {
entity: string;
name?: string;
states?: readonly (keyof TranslationDict["ui"]["card"]["alarm_control_panel"])[];
states?: AlarmPanelCardConfigState[];
theme?: string;
}

View File

@ -2,14 +2,25 @@ import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { array, assert, assign, object, optional, string } from "superstruct";
import { HassEntity } from "home-assistant-js-websocket";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import type { AlarmPanelCardConfig } from "../../cards/types";
import type {
AlarmPanelCardConfig,
AlarmPanelCardConfigState,
} from "../../cards/types";
import type { LovelaceCardEditor } from "../../types";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import {
DEFAULT_STATES,
ALARM_MODE_STATE_MAP,
filterSupportedAlarmStates,
} from "../../cards/hui-alarm-panel-card";
import { supportsFeature } from "../../../../common/entity/supports-feature";
import { ALARM_MODES } from "../../../../data/alarm_control_panel";
const cardConfigStruct = assign(
baseLovelaceCardConfig,
@ -21,13 +32,7 @@ const cardConfigStruct = assign(
})
);
const states = [
"arm_home",
"arm_away",
"arm_night",
"arm_vacation",
"arm_custom_bypass",
] as const;
const states = Object.keys(ALARM_MODE_STATE_MAP) as AlarmPanelCardConfigState[];
@customElement("hui-alarm-panel-card-editor")
export class HuiAlarmPanelCardEditor
@ -44,7 +49,11 @@ export class HuiAlarmPanelCardEditor
}
private _schema = memoizeOne(
(localize: LocalizeFunc) =>
(
localize: LocalizeFunc,
stateObj: HassEntity | undefined,
config_states: AlarmPanelCardConfigState[]
) =>
[
{
name: "entity",
@ -60,12 +69,24 @@ export class HuiAlarmPanelCardEditor
],
},
{
type: "multi_select",
name: "states",
options: states.map((s) => [
s,
localize(`ui.card.alarm_control_panel.${s}`),
]) as [string, string][],
selector: {
select: {
multiple: true,
mode: "list",
options: states.map((s) => ({
value: s,
label: localize(`ui.card.alarm_control_panel.${s}`),
disabled:
!config_states.includes(s) &&
(!stateObj ||
!supportsFeature(
stateObj,
ALARM_MODES[ALARM_MODE_STATE_MAP[s]].feature || 0
)),
})),
},
},
},
] as const
);
@ -75,11 +96,18 @@ export class HuiAlarmPanelCardEditor
return nothing;
}
const stateObj = this.hass.states[this._config.entity];
const defaultFilteredStates = filterSupportedAlarmStates(
stateObj,
DEFAULT_STATES
);
const config = { states: defaultFilteredStates, ...this._config };
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${this._schema(this.hass.localize)}
.data=${config}
.schema=${this._schema(this.hass.localize, stateObj, config.states)}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
@ -87,7 +115,26 @@ export class HuiAlarmPanelCardEditor
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
const newConfig = ev.detail.value;
// Sort states in a consistent order
if (newConfig.states) {
const sortStates = states.filter((s) => newConfig.states.includes(s));
newConfig.states = sortStates;
}
// When changing entities, clear any states that the new entity does not support
if (newConfig.states && newConfig.entity !== this._config?.entity) {
const newStateObj = this.hass?.states[newConfig.entity];
if (newStateObj) {
newConfig.states = filterSupportedAlarmStates(
newStateObj,
newConfig.states
);
}
}
fireEvent(this, "config-changed", { config: newConfig });
}
private _computeLabelCallback = (