Skip to content

Commit 04db261

Browse files
authored
Alarm control panel code dialog (#1437)
* Use code dialog to arm and disarm instead of the keypad * Set the right minimal version for HA
1 parent 12e1c8b commit 04db261

33 files changed

+309
-413
lines changed

docs/cards/alarm-control-panel.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ All the options are available in the lovelace editor but you can use `yaml` if y
2222
| `secondary_info` | `name` `state` `last-changed` `last-updated` `none` | `state` | Info to show as secondary info |
2323
| `icon_type` | `icon` `entity-picture` `none` | `icon` | Type of icon to display |
2424
| `states` | list | `["armed_home", "armed_away"]` | List of arm states to display |
25-
| `show_keypad` | boolean | `false` | Show the keypad |
2625
| `tap_action` | action | `more-info` | Home assistant action to perform on tap |
2726
| `hold_action` | action | `more-info` | Home assistant action to perform on hold |
2827
| `double_tap_action` | action | `more-info` | Home assistant action to perform on double_tap |

hacs.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Mushroom",
33
"filename": "mushroom.js",
4-
"homeassistant": "2023.7.0",
4+
"homeassistant": "2024.3.0",
55
"render_readme": true
66
}
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { array, assign, boolean, object, optional } from "superstruct";
1+
import { array, assign, boolean, deprecated, object, optional } from "superstruct";
22
import { LovelaceCardConfig } from "../../ha";
33
import { ActionsSharedConfig, actionsSharedConfigStruct } from "../../shared/config/actions-config";
44
import {
@@ -7,20 +7,24 @@ import {
77
} from "../../shared/config/appearance-config";
88
import { EntitySharedConfig, entitySharedConfigStruct } from "../../shared/config/entity-config";
99
import { lovelaceCardConfigStruct } from "../../shared/config/lovelace-card-config";
10+
import { AlarmMode } from "../../ha/data/alarm_control_panel";
1011

1112
export type AlarmControlPanelCardConfig = LovelaceCardConfig &
1213
EntitySharedConfig &
1314
AppearanceSharedConfig &
1415
ActionsSharedConfig & {
15-
states?: string[];
16-
show_keypad?: boolean;
16+
states?: AlarmMode[];
1717
};
1818

1919
export const alarmControlPanelCardCardConfigStruct = assign(
2020
lovelaceCardConfigStruct,
2121
assign(entitySharedConfigStruct, appearanceSharedConfigStruct, actionsSharedConfigStruct),
2222
object({
2323
states: optional(array()),
24-
show_keypad: optional(boolean()),
24+
show_keypad: deprecated(optional(boolean()), (_value, ctx) => {
25+
console.warn(
26+
`🍄 "${ctx.path}" option is deprecated and no longer available. Remove it from your YAML configuration or use the built-in Home Assistant alarm panel card if you want keypad.`
27+
);
28+
}),
2529
})
2630
);

src/cards/alarm-control-panel-card/alarm-control-panel-card-editor.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ const actions: UiAction[] = ["more-info", "navigate", "url", "call-service", "as
2121

2222
const states = ["armed_home", "armed_away", "armed_night", "armed_vacation", "armed_custom_bypass"];
2323

24-
const ALARM_CONTROL_PANEL_LABELS = ["show_keypad"];
25-
2624
const computeSchema = memoizeOne((localize: LocalizeFunc): HaFormSchema[] => [
2725
{ name: "entity", selector: { entity: { domain: ALARM_CONTROl_PANEL_ENTITY_DOMAINS } } },
2826
{ name: "name", selector: { text: {} } },
@@ -36,7 +34,6 @@ const computeSchema = memoizeOne((localize: LocalizeFunc): HaFormSchema[] => [
3634
localize(`ui.card.alarm_control_panel.${state.replace("armed", "arm")}`),
3735
]) as [string, string][],
3836
},
39-
{ name: "show_keypad", selector: { boolean: {} } },
4037
...computeActionsFormSchema(actions),
4138
]);
4239

@@ -78,9 +75,6 @@ export class SwitchCardEditor extends MushroomBaseElement implements LovelaceCar
7875
if (GENERIC_LABELS.includes(schema.name)) {
7976
return customLocalize(`editor.card.generic.${schema.name}`);
8077
}
81-
if (ALARM_CONTROL_PANEL_LABELS.includes(schema.name)) {
82-
return customLocalize(`editor.card.alarm_control_panel.${schema.name}`);
83-
}
8478
if (schema.name === "states") {
8579
return this.hass!.localize(
8680
"ui.panel.lovelace.editor.card.alarm-panel.available_states"

src/cards/alarm-control-panel-card/alarm-control-panel-card.ts

Lines changed: 10 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { HassEntity } from "home-assistant-js-websocket";
22
import { css, CSSResultGroup, html, nothing, PropertyValues, TemplateResult } from "lit";
3-
import { customElement, query } from "lit/decorators.js";
3+
import { customElement } from "lit/decorators.js";
44
import { classMap } from "lit/directives/class-map.js";
55
import { styleMap } from "lit/directives/style-map.js";
66
import {
@@ -12,8 +12,8 @@ import {
1212
HomeAssistant,
1313
LovelaceCard,
1414
LovelaceCardEditor,
15-
LovelaceLayoutOptions,
1615
} from "../../ha";
16+
import { ALARM_MODES, AlarmMode, setProtectedAlarmControlPanelMode } from "../../ha/data/alarm_control_panel";
1717
import "../../shared/badge-icon";
1818
import "../../shared/button";
1919
import "../../shared/button-group";
@@ -25,22 +25,14 @@ import { computeAppearance } from "../../utils/appearance";
2525
import { MushroomBaseCard } from "../../utils/base-card";
2626
import { cardStyle } from "../../utils/card-styles";
2727
import { registerCustomCard } from "../../utils/custom-cards";
28-
import { alarmPanelIconAction } from "../../utils/icons/alarm-panel-icon";
2928
import { computeEntityPicture } from "../../utils/info";
3029
import { AlarmControlPanelCardConfig } from "./alarm-control-panel-card-config";
3130
import {
3231
ALARM_CONTROl_PANEL_CARD_EDITOR_NAME,
3332
ALARM_CONTROl_PANEL_CARD_NAME,
3433
ALARM_CONTROl_PANEL_ENTITY_DOMAINS,
3534
} from "./const";
36-
import {
37-
getStateColor,
38-
getStateService,
39-
hasCode,
40-
isActionsAvailable,
41-
isDisarmed,
42-
shouldPulse,
43-
} from "./utils";
35+
import { getStateColor, hasCode, isActionsAvailable, isDisarmed, shouldPulse } from "./utils";
4436

4537
registerCustomCard({
4638
type: ALARM_CONTROl_PANEL_CARD_NAME,
@@ -49,14 +41,10 @@ registerCustomCard({
4941
});
5042

5143
type ActionButtonType = {
52-
state: string;
44+
mode: AlarmMode;
5345
disabled?: boolean;
5446
};
5547

56-
type HaTextField = any;
57-
58-
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"];
59-
6048
/*
6149
* Ref: https://github.com/home-assistant/frontend/blob/dev/src/panels/lovelace/cards/hui-alarm-panel-card.ts
6250
* TODO: customize icon for modes (advanced YAML configuration)
@@ -88,68 +76,15 @@ export class AlarmControlPanelCard
8876
return Boolean(this._config?.states?.length);
8977
}
9078

91-
public getLayoutOptions(): LovelaceLayoutOptions {
92-
const options = super.getLayoutOptions();
93-
if (this._config?.show_keypad) {
94-
delete options.grid_columns;
95-
delete options.grid_rows;
96-
}
97-
return options;
98-
}
99-
100-
@query("#alarmCode") private _input?: HaTextField;
101-
102-
setConfig(config: AlarmControlPanelCardConfig): void {
103-
super.setConfig(config);
104-
this.loadComponents();
105-
}
106-
107-
protected updated(changedProperties: PropertyValues) {
108-
super.updated(changedProperties);
109-
if (this.hass && changedProperties.has("hass")) {
110-
this.loadComponents();
111-
}
112-
}
113-
114-
async loadComponents() {
115-
const stateObj = this._stateObj;
116-
117-
if (stateObj && hasCode(stateObj)) {
118-
void import("../../shared/form/mushroom-textfield");
119-
}
120-
}
121-
122-
private _onTap(e: MouseEvent, state: string): void {
123-
const service = getStateService(state);
124-
if (!service) return;
79+
private _onTap(e: MouseEvent, mode: AlarmMode): void {
12580
e.stopPropagation();
126-
const code = this._input?.value || undefined;
127-
this.hass.callService("alarm_control_panel", service, {
128-
entity_id: this._config?.entity,
129-
code,
130-
});
131-
if (this._input) {
132-
this._input.value = "";
133-
}
134-
}
135-
136-
private _handlePadClick(e: MouseEvent): void {
137-
const val = (e.currentTarget! as any).value;
138-
if (this._input) {
139-
this._input.value = val === "clear" ? "" : this._input!.value + val;
140-
}
81+
setProtectedAlarmControlPanelMode(this, this.hass!, this._stateObj!, mode);
14182
}
14283

14384
private _handleAction(ev: ActionHandlerEvent) {
14485
handleAction(this, this.hass!, this._config!, ev.detail.action!);
14586
}
14687

147-
private get _hasCode(): boolean {
148-
const stateObj = this._stateObj;
149-
if (!stateObj) return false;
150-
return hasCode(stateObj) && Boolean(this._config?.show_keypad);
151-
}
152-
15388
protected render() {
15489
if (!this.hass || !this._config || !this._config.entity) {
15590
return nothing;
@@ -169,8 +104,8 @@ export class AlarmControlPanelCard
169104
const actions: ActionButtonType[] =
170105
this._config.states && this._config.states.length > 0
171106
? isDisarmed(stateObj)
172-
? this._config.states.map((state) => ({ state }))
173-
: [{ state: "disarmed" }]
107+
? this._config.states.map((state) => ({ mode: state }))
108+
: [{ mode: "disarmed" }]
174109
: [];
175110

176111
const isActionEnabled = isActionsAvailable(stateObj);
@@ -202,10 +137,10 @@ export class AlarmControlPanelCard
202137
${actions.map(
203138
(action) => html`
204139
<mushroom-button
205-
@click=${(e) => this._onTap(e, action.state)}
140+
@click=${(e) => this._onTap(e, action.mode)}
206141
.disabled=${!isActionEnabled}
207142
>
208-
<ha-icon .icon=${alarmPanelIconAction(action.state)}>
143+
<ha-icon .icon=${ALARM_MODES[action.mode].icon}>
209144
</ha-icon>
210145
</mushroom-button>
211146
`
@@ -214,44 +149,6 @@ export class AlarmControlPanelCard
214149
`
215150
: nothing}
216151
</mushroom-card>
217-
${!this._hasCode
218-
? nothing
219-
: html`
220-
<mushroom-textfield
221-
id="alarmCode"
222-
.label=${this.hass.localize("ui.card.alarm_control_panel.code")}
223-
type="password"
224-
.inputmode=${stateObj.attributes.code_format === "number"
225-
? "numeric"
226-
: "text"}
227-
></mushroom-textfield>
228-
`}
229-
${!(this._hasCode && stateObj.attributes.code_format === "number")
230-
? nothing
231-
: html`
232-
<div id="keypad">
233-
${BUTTONS.map((value) =>
234-
value === ""
235-
? html`<mwc-button disabled></mwc-button>`
236-
: html`
237-
<mwc-button
238-
.value=${value}
239-
@click=${this._handlePadClick}
240-
outlined
241-
class=${classMap({
242-
numberkey: value !== "clear",
243-
})}
244-
>
245-
${value === "clear"
246-
? this.hass!.localize(
247-
`ui.card.alarm_control_panel.clear_code`
248-
)
249-
: value}
250-
</mwc-button>
251-
`
252-
)}
253-
</div>
254-
`}
255152
</ha-card>
256153
`;
257154
}
@@ -287,31 +184,9 @@ export class AlarmControlPanelCard
287184
mushroom-state-item {
288185
cursor: pointer;
289186
}
290-
.alert {
291-
--main-color: var(--warning-color);
292-
}
293187
mushroom-shape-icon.pulse {
294188
--shape-animation: 1s ease 0s infinite normal none running pulse;
295189
}
296-
mushroom-textfield {
297-
display: block;
298-
margin: 8px auto;
299-
max-width: 150px;
300-
text-align: center;
301-
}
302-
#keypad {
303-
display: flex;
304-
justify-content: center;
305-
flex-wrap: wrap;
306-
margin: auto;
307-
width: 100%;
308-
max-width: 300px;
309-
}
310-
#keypad mwc-button {
311-
padding: 8px;
312-
width: 30%;
313-
box-sizing: border-box;
314-
}
315190
`,
316191
];
317192
}

0 commit comments

Comments
 (0)