Add default code to alarm_control_panel (#20062)

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
This commit is contained in:
G Johansson 2024-05-29 11:05:46 +02:00 committed by GitHub
parent febbf34de6
commit 91e5fcacd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 152 additions and 88 deletions

View File

@ -10,8 +10,10 @@ import {
HassEntityAttributeBase,
HassEntityBase,
} from "home-assistant-js-websocket";
import { HomeAssistant } from "../types";
import { supportsFeature } from "../common/entity/supports-feature";
import { showEnterCodeDialog } from "../dialogs/enter-code/show-enter-code-dialog";
import { HomeAssistant } from "../types";
import { getExtendedEntityRegistryEntry } from "./entity_registry";
export const FORMAT_TEXT = "text";
export const FORMAT_NUMBER = "number";
@ -103,3 +105,50 @@ export const supportedAlarmModes = (stateObj: AlarmControlPanelEntity) =>
const feature = ALARM_MODES[mode].feature;
return !feature || supportsFeature(stateObj, feature);
});
export const setProtectedAlarmControlPanelMode = async (
element: HTMLElement,
hass: HomeAssistant,
stateObj: AlarmControlPanelEntity,
mode: AlarmMode
) => {
const { service } = ALARM_MODES[mode];
let code: string | undefined;
if (
(mode !== "disarmed" &&
stateObj.attributes.code_arm_required &&
stateObj.attributes.code_format) ||
(mode === "disarmed" && stateObj.attributes.code_format)
) {
const entry = await getExtendedEntityRegistryEntry(
hass,
stateObj.entity_id
).catch(() => undefined);
const defaultCode = entry?.options?.alarm_control_panel?.default_code;
if (!defaultCode) {
const disarm = mode === "disarmed";
const response = await showEnterCodeDialog(element, {
codeFormat: stateObj.attributes.code_format,
title: hass.localize(
`ui.card.alarm_control_panel.${disarm ? "disarm" : "arm"}`
),
submitText: hass.localize(
`ui.card.alarm_control_panel.${disarm ? "disarm" : "arm"}`
),
});
if (response == null) {
throw new Error("Code dialog closed");
}
code = response;
}
}
await hass.callService("alarm_control_panel", service, {
entity_id: stateObj.entity_id,
code,
});
};

View File

@ -96,6 +96,10 @@ export interface LockEntityOptions {
default_code?: string | null;
}
export interface AlarmControlPanelEntityOptions {
default_code?: string | null;
}
export interface WeatherEntityOptions {
precipitation_unit?: string | null;
pressure_unit?: string | null;
@ -112,6 +116,7 @@ export interface SwitchAsXEntityOptions {
export interface EntityRegistryOptions {
number?: NumberEntityOptions;
sensor?: SensorEntityOptions;
alarm_control_panel?: AlarmControlPanelEntityOptions;
lock?: LockEntityOptions;
weather?: WeatherEntityOptions;
light?: LightEntityOptions;
@ -134,6 +139,7 @@ export interface EntityRegistryEntryUpdateParams {
| SensorEntityOptions
| NumberEntityOptions
| LockEntityOptions
| AlarmControlPanelEntityOptions
| WeatherEntityOptions
| LightEntityOptions;
aliases?: string[];

View File

@ -4,10 +4,12 @@ import { styleMap } from "lit/directives/style-map";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-control-button";
import "../../../components/ha-state-icon";
import { AlarmControlPanelEntity } from "../../../data/alarm_control_panel";
import {
AlarmControlPanelEntity,
setProtectedAlarmControlPanelMode,
} from "../../../data/alarm_control_panel";
import "../../../state-control/alarm_control_panel/ha-state-control-alarm_control_panel-modes";
import type { HomeAssistant } from "../../../types";
import { showEnterCodeDialog } from "../../enter-code/show-enter-code-dialog";
import "../components/ha-more-info-state-header";
import { moreInfoControlStyle } from "../components/more-info-control-style";
@ -18,24 +20,12 @@ class MoreInfoAlarmControlPanel extends LitElement {
@property({ attribute: false }) public stateObj?: AlarmControlPanelEntity;
private async _disarm() {
let code: string | undefined;
if (this.stateObj!.attributes.code_format) {
const response = await showEnterCodeDialog(this, {
codeFormat: this.stateObj!.attributes.code_format,
title: this.hass.localize("ui.card.alarm_control_panel.disarm"),
submitText: this.hass.localize("ui.card.alarm_control_panel.disarm"),
});
if (response == null) {
return;
}
code = response;
}
this.hass.callService("alarm_control_panel", "alarm_disarm", {
entity_id: this.stateObj!.entity_id,
code,
});
setProtectedAlarmControlPanelMode(
this,
this.hass,
this.stateObj!,
"disarmed"
);
}
protected render() {

View File

@ -59,6 +59,7 @@ import {
updateDeviceRegistryEntry,
} from "../../../data/device_registry";
import {
AlarmControlPanelEntityOptions,
EntityRegistryEntry,
EntityRegistryEntryUpdateParams,
ExtEntityRegistryEntry,
@ -257,6 +258,10 @@ export class EntityRegistrySettingsEditor extends LitElement {
this._defaultCode = this.entry.options?.lock?.default_code;
}
if (domain === "alarm_control_panel") {
this._defaultCode = this.entry.options?.alarm_control_panel?.default_code;
}
if (domain === "weather") {
const stateObj: HassEntity | undefined =
this.hass.states[this.entry.entity_id];
@ -583,6 +588,19 @@ export class EntityRegistrySettingsEditor extends LitElement {
></ha-textfield>
`
: ""}
${domain === "alarm_control_panel"
? html`
<ha-textfield
.value=${this._defaultCode == null ? "" : this._defaultCode}
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.default_code"
)}
type="password"
.disabled=${this.disabled}
@input=${this._defaultcodeChanged}
></ha-textfield>
`
: ""}
${domain === "sensor" &&
this._deviceClass &&
stateObj?.attributes.unit_of_measurement &&
@ -1071,6 +1089,15 @@ export class EntityRegistrySettingsEditor extends LitElement {
params.options = this.entry.options?.[domain] || {};
(params.options as LockEntityOptions).default_code = this._defaultCode;
}
if (
domain === "alarm_control_panel" &&
this.entry.options?.[domain]?.default_code !== this._defaultCode
) {
params.options_domain = domain;
params.options = this.entry.options?.[domain] || {};
(params.options as AlarmControlPanelEntityOptions).default_code =
this._defaultCode;
}
if (
domain === "weather" &&
(stateObj?.attributes?.precipitation_unit !== this._precipitation_unit ||

View File

@ -16,10 +16,10 @@ import {
ALARM_MODES,
AlarmControlPanelEntity,
AlarmMode,
setProtectedAlarmControlPanelMode,
supportedAlarmModes,
} from "../../../data/alarm_control_panel";
import { UNAVAILABLE } from "../../../data/entity";
import { showEnterCodeDialog } from "../../../dialogs/enter-code/show-enter-code-dialog";
import { HomeAssistant } from "../../../types";
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { filterModes } from "./common/filter-modes";
@ -115,37 +115,7 @@ class HuiAlarmModeCardFeature
}
private async _setMode(mode: AlarmMode) {
const { service } = ALARM_MODES[mode];
let code: string | undefined;
if (
(mode !== "disarmed" &&
this.stateObj!.attributes.code_arm_required &&
this.stateObj!.attributes.code_format) ||
(mode === "disarmed" && this.stateObj!.attributes.code_format)
) {
const disarm = mode === "disarmed";
const response = await showEnterCodeDialog(this, {
codeFormat: this.stateObj!.attributes.code_format,
title: this.hass!.localize(
`ui.card.alarm_control_panel.${disarm ? "disarm" : "arm"}`
),
submitText: this.hass!.localize(
`ui.card.alarm_control_panel.${disarm ? "disarm" : "arm"}`
),
});
if (response == null) {
throw new Error("cancel");
}
code = response;
}
await this.hass!.callService("alarm_control_panel", service, {
entity_id: this.stateObj!.entity_id,
code,
});
setProtectedAlarmControlPanelMode(this, this.hass!, this.stateObj!, mode);
}
protected render(): TemplateResult | null {

View File

@ -1,4 +1,4 @@
import { HassEntity } from "home-assistant-js-websocket";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
CSSResultGroup,
LitElement,
@ -30,6 +30,11 @@ import type { HomeAssistant } from "../../../types";
import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { LovelaceCard } from "../types";
import {
ExtEntityRegistryEntry,
getExtendedEntityRegistryEntry,
subscribeEntityRegistry,
} from "../../../data/entity_registry";
import { AlarmPanelCardConfig, AlarmPanelCardConfigState } from "./types";
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"];
@ -99,8 +104,22 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
@state() private _config?: AlarmPanelCardConfig;
@state() private _entry?: ExtEntityRegistryEntry | null;
@query("#alarmCode") private _input?: HaTextField;
private _unsubEntityRegistry?: UnsubscribeFunc;
public connectedCallback() {
super.connectedCallback();
this._subscribeEntityEntry();
}
public disconnectedCallback() {
super.disconnectedCallback();
this._unsubscribeEntityRegistry();
}
public async getCardSize(): Promise<number> {
if (!this._config || !this.hass) {
return 9;
@ -123,6 +142,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
}
this._config = { ...config };
this._subscribeEntityEntry();
}
protected updated(changedProps: PropertyValues): void {
@ -165,6 +185,36 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
);
}
private async _unsubscribeEntityRegistry() {
if (this._unsubEntityRegistry) {
this._unsubEntityRegistry();
this._unsubEntityRegistry = undefined;
}
}
private async _subscribeEntityEntry() {
if (!this._config?.entity) {
return;
}
try {
this._unsubEntityRegistry = subscribeEntityRegistry(
this.hass!.connection,
async (entries) => {
if (
entries.some((entry) => entry.entity_id === this._config!.entity)
) {
this._entry = await getExtendedEntityRegistryEntry(
this.hass!,
this._config!.entity
);
}
}
);
} catch (e) {
this._entry = null;
}
}
protected render() {
if (!this._config || !this.hass) {
return nothing;
@ -184,6 +234,8 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
const stateLabel = this._stateDisplay(stateObj.state);
const defaultCode = this._entry?.options?.alarm_control_panel?.default_code;
return html`
<ha-card>
<h1 class="card-header">
@ -222,7 +274,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
`
)}
</div>
${!stateObj.attributes.code_format
${!stateObj.attributes.code_format || defaultCode
? nothing
: html`
<ha-textfield
@ -234,7 +286,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
: "text"}
></ha-textfield>
`}
${stateObj.attributes.code_format !== FORMAT_NUMBER
${stateObj.attributes.code_format !== FORMAT_NUMBER || defaultCode
? nothing
: html`
<div id="keypad">

View File

@ -11,9 +11,9 @@ import {
ALARM_MODES,
AlarmControlPanelEntity,
AlarmMode,
setProtectedAlarmControlPanelMode,
} from "../../data/alarm_control_panel";
import { UNAVAILABLE } from "../../data/entity";
import { showEnterCodeDialog } from "../../dialogs/enter-code/show-enter-code-dialog";
import { HomeAssistant } from "../../types";
@customElement("ha-state-control-alarm_control_panel-modes")
@ -44,37 +44,7 @@ export class HaStateControlAlarmControlPanelModes extends LitElement {
}
private async _setMode(mode: AlarmMode) {
const { service } = ALARM_MODES[mode];
let code: string | undefined;
if (
(mode !== "disarmed" &&
this.stateObj!.attributes.code_arm_required &&
this.stateObj!.attributes.code_format) ||
(mode === "disarmed" && this.stateObj!.attributes.code_format)
) {
const disarm = mode === "disarmed";
const response = await showEnterCodeDialog(this, {
codeFormat: this.stateObj!.attributes.code_format,
title: this.hass!.localize(
`ui.card.alarm_control_panel.${disarm ? "disarm" : "arm"}`
),
submitText: this.hass!.localize(
`ui.card.alarm_control_panel.${disarm ? "disarmn" : "arm"}`
),
});
if (response == null) {
throw new Error("cancel");
}
code = response;
}
await this.hass!.callService("alarm_control_panel", service, {
entity_id: this.stateObj!.entity_id,
code,
});
setProtectedAlarmControlPanelMode(this, this.hass!, this.stateObj!, mode);
}
private async _valueChanged(ev: CustomEvent) {