diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.ts b/src/panels/lovelace/cards/hui-alarm-panel-card.ts
index b35235bd2d..34585865c6 100644
--- a/src/panels/lovelace/cards/hui-alarm-panel-card.ts
+++ b/src/panels/lovelace/cards/hui-alarm-panel-card.ts
@@ -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 {
${(stateObj.state === "disarmed"
- ? this._config.states!
+ ? states
: (["disarm"] as const)
).map(
(stateAction) => html`
diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts
index 0a7c8ee412..7ddeef08a3 100644
--- a/src/panels/lovelace/cards/types.ts
+++ b/src/panels/lovelace/cards/types.ts
@@ -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;
}
diff --git a/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts
index b70e346018..74ff40fc22 100644
--- a/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-alarm-panel-card-editor.ts
@@ -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`
@@ -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 = (