From 40954504763f226dfb5912c2a6e9c759d7d1f00d Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Sat, 4 Dec 2021 11:43:14 +0100 Subject: [PATCH 01/16] Add `switch` to input row domains to fix mobile focus issue (#10792) --- src/common/const.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/const.ts b/src/common/const.ts index 0aedba0806..6f4c6319c5 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -221,6 +221,7 @@ export const DOMAINS_INPUT_ROW = [ "scene", "script", "select", + "switch", ]; /** Domains that should have the history hidden in the more info dialog. */ From 8f5751d5bbb502ac7102c7724b2374556ec92190 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Sun, 5 Dec 2021 19:09:06 +0100 Subject: [PATCH 02/16] Use correct label in area card editor (#10799) --- .../lovelace/editor/config-elements/hui-area-card-editor.ts | 4 +++- src/translations/en.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts index e23c463746..b624018a5a 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts @@ -59,7 +59,9 @@ export class HuiAreaCardEditor .value=${this._area} .placeholder=${this._area} .configValue=${"area"} - .label=${this.hass.localize("ui.dialogs.entity_registry.editor.area")} + .label=${this.hass.localize( + "ui.panel.lovelace.editor.card.area.name" + )} @value-changed=${this._valueChanged} > Date: Mon, 6 Dec 2021 12:51:21 +0100 Subject: [PATCH 03/16] Use ha-logo-svg on info page (#10807) --- src/panels/config/info/ha-config-info.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/panels/config/info/ha-config-info.ts b/src/panels/config/info/ha-config-info.ts index 24ed7b84fe..943f6aa329 100644 --- a/src/panels/config/info/ha-config-info.ts +++ b/src/panels/config/info/ha-config-info.ts @@ -1,6 +1,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { property } from "lit/decorators"; import "../../../layouts/hass-tabs-subpage"; +import "../../../components/ha-logo-svg"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; @@ -40,13 +41,14 @@ class HaConfigInfo extends LitElement { href=${documentationUrl(this.hass, "")} target="_blank" rel="noreferrer" - >${this.hass.localize( + + > + +

Home Assistant ${hass.connection.haVersion}

@@ -193,6 +195,11 @@ class HaConfigInfo extends LitElement { margin: 0 auto; padding-bottom: 16px; } + ha-logo-svg { + padding: 12px; + height: 180px; + width: 180px; + } `, ]; } From 419879ee7aa9d69b1d85489b6c4863fab325a9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 6 Dec 2021 12:51:56 +0100 Subject: [PATCH 04/16] Fix core changelog URL (#10804) --- .../src/update-available/update-available-card.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hassio/src/update-available/update-available-card.ts b/hassio/src/update-available/update-available-card.ts index e45d10588e..7b32fe8551 100644 --- a/hassio/src/update-available/update-available-card.ts +++ b/hassio/src/update-available/update-available-card.ts @@ -48,7 +48,6 @@ import "../../../src/layouts/hass-subpage"; import "../../../src/layouts/hass-tabs-subpage"; import { SUPERVISOR_UPDATE_NAMES } from "../../../src/panels/config/dashboard/ha-config-updates"; import { HomeAssistant, Route } from "../../../src/types"; -import { documentationUrl } from "../../../src/util/documentation-url"; import { addonArchIsSupported, extractChangelog } from "../util/addon"; declare global { @@ -60,7 +59,6 @@ declare global { type updateType = "os" | "supervisor" | "core" | "addon"; const changelogUrl = ( - hass: HomeAssistant, entry: updateType, version: string ): string | undefined => { @@ -68,17 +66,19 @@ const changelogUrl = ( return undefined; } if (entry === "core") { - return version?.includes("dev") + return version.includes("dev") ? "https://github.com/home-assistant/core/commits/dev" - : documentationUrl(hass, "/latest-release-notes/"); + : version.includes("b") + ? "https://next.home-assistant.io/latest-release-notes/" + : "https://www.home-assistant.io/latest-release-notes/"; } if (entry === "os") { - return version?.includes("dev") + return version.includes("dev") ? "https://github.com/home-assistant/operating-system/commits/dev" : `https://github.com/home-assistant/operating-system/releases/tag/${version}`; } if (entry === "supervisor") { - return version?.includes("dev") + return version.includes("dev") ? "https://github.com/home-assistant/supervisor/commits/main" : `https://github.com/home-assistant/supervisor/releases/tag/${version}`; } @@ -120,7 +120,7 @@ class UpdateAvailableCard extends LitElement { return html``; } - const changelog = changelogUrl(this.hass, this._updateType, this._version); + const changelog = changelogUrl(this._updateType, this._version); return html` Date: Mon, 6 Dec 2021 12:52:38 +0100 Subject: [PATCH 05/16] Mark more trigger fields as optional (#10798) --- src/translations/en.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index db11bf37d8..372374f8db 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1514,7 +1514,7 @@ "extra_fields": { "above": "Above", "below": "Below", - "for": "Duration", + "for": "Duration (optional)", "zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]" } }, @@ -1537,9 +1537,9 @@ "state": { "label": "State", "attribute": "Attribute (optional)", - "from": "From", - "for": "For", - "to": "To" + "from": "From (optional)", + "for": "For (optional)", + "to": "To (optional)" }, "homeassistant": { "label": "Home Assistant", From f164d21c44748559759bfbdb8172cf6f65da6416 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 6 Dec 2021 12:53:45 +0100 Subject: [PATCH 06/16] Filter out invalid text input for `input_text` (#10797) --- .../lovelace/entity-rows/hui-input-text-entity-row.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts index 40e33d7fe9..a8af772a68 100644 --- a/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-text-entity-row.ts @@ -1,7 +1,7 @@ import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { UNAVAILABLE } from "../../../data/entity"; +import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; import { setValue } from "../../../data/input_text"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -67,6 +67,12 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow { const element = this._inputEl; const stateObj = this.hass!.states[this._config!.entity]; + // Filter out invalid text states + if (element.value && UNAVAILABLE_STATES.includes(element.value)) { + element.value = stateObj.state; + return; + } + if (element.value !== stateObj.state) { setValue(this.hass!, stateObj.entity_id, element.value!); } From 66df15007a84b359e27641d44de543ffab1fad45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 6 Dec 2021 12:54:16 +0100 Subject: [PATCH 07/16] Fetch cloud and updates on reconnect (#10808) --- src/panels/config/ha-panel-config.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 4c23f77086..333d87a27f 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -447,9 +447,19 @@ class HaPanelConfig extends HassRouterPage { this.hass.loadBackendTranslation("title"); if (isComponentLoaded(this.hass, "cloud")) { this._updateCloudStatus(); + this.addEventListener("connection-status", (ev) => { + if (ev.detail === "connected") { + this._updateCloudStatus(); + } + }); } if (isComponentLoaded(this.hass, "hassio")) { this._loadSupervisorUpdates(); + this.addEventListener("connection-status", (ev) => { + if (ev.detail === "connected") { + this._loadSupervisorUpdates(); + } + }); } else { this._supervisorUpdates = null; } From d6b9b16f02c1628156fb5775b1d3275e75835b69 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 6 Dec 2021 18:02:32 +0100 Subject: [PATCH 08/16] Add toggle for camera view in area card (#10810) Co-authored-by: Zack Barett --- src/panels/lovelace/cards/hui-area-card.ts | 10 +++++--- src/panels/lovelace/cards/types.ts | 1 + .../config-elements/hui-area-card-editor.ts | 24 +++++++++++++++++-- .../lovelace/editor/get-card-stub-config.ts | 2 +- .../get-headerfooter-stub-config.ts | 2 +- src/panels/lovelace/editor/lovelace-cards.ts | 1 + src/translations/en.json | 3 ++- 7 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 617f6336fc..478201625d 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -25,6 +25,7 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import { domainIcon } from "../../../common/entity/domain_icon"; import { navigate } from "../../../common/navigate"; import { formatNumber } from "../../../common/number/format_number"; +import { subscribeOne } from "../../../common/util/subscribe-one"; import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; @@ -83,8 +84,11 @@ export class HuiAreaCard return document.createElement("hui-area-card-editor"); } - public static getStubConfig(): AreaCardConfig { - return { type: "area", area: "" }; + public static async getStubConfig( + hass: HomeAssistant + ): Promise { + const areas = await subscribeOne(hass.connection, subscribeAreaRegistry); + return { type: "area", area: areas[0]?.area_id || "" }; } @property({ attribute: false }) public hass!: HomeAssistant; @@ -358,7 +362,7 @@ export class HuiAreaCard }); let cameraEntityId: string | undefined; - if ("camera" in entitiesByDomain) { + if (this._config.show_camera && "camera" in entitiesByDomain) { cameraEntityId = entitiesByDomain.camera[0].entity_id; } diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index cf5ecb5198..9762d85845 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -79,6 +79,7 @@ export interface EntitiesCardConfig extends LovelaceCardConfig { export interface AreaCardConfig extends LovelaceCardConfig { area: string; navigation_path?: string; + show_camera?: boolean; } export interface ButtonCardConfig extends LovelaceCardConfig { diff --git a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts index b624018a5a..c2e9bb7564 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts @@ -1,7 +1,7 @@ import "@polymer/paper-input/paper-input"; import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { assert, assign, object, optional, string } from "superstruct"; +import { assert, assign, boolean, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-area-picker"; import { HomeAssistant } from "../../../../types"; @@ -11,6 +11,8 @@ import { LovelaceCardEditor } from "../../types"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; +import "../../../../components/ha-formfield"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; const cardConfigStruct = assign( baseLovelaceCardConfig, @@ -18,6 +20,7 @@ const cardConfigStruct = assign( area: optional(string()), navigation_path: optional(string()), theme: optional(string()), + show_camera: optional(boolean()), }) ); @@ -47,6 +50,10 @@ export class HuiAreaCardEditor return this._config!.theme || ""; } + get _show_camera(): boolean { + return this._config!.show_camera || false; + } + protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; @@ -64,6 +71,18 @@ export class HuiAreaCardEditor )} @value-changed=${this._valueChanged} > + + + Date: Mon, 6 Dec 2021 18:25:23 +0100 Subject: [PATCH 09/16] Fix disabled date input (#10813) --- src/components/ha-date-input.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ha-date-input.ts b/src/components/ha-date-input.ts index d2289c464b..852e4aa4c7 100644 --- a/src/components/ha-date-input.ts +++ b/src/components/ha-date-input.ts @@ -96,7 +96,11 @@ export class HaDateInput extends LitElement { attr-for-value="value" .i18n=${i18n} > - + `; From ff7a2c8cb77c31e170202ebc7c72a08a08ab443e Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 6 Dec 2021 19:29:51 +0100 Subject: [PATCH 10/16] Fix clearing of picture (e.g. area and person config) --- src/components/ha-picture-upload.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/ha-picture-upload.ts b/src/components/ha-picture-upload.ts index 809d6a2b23..b18852cc3c 100644 --- a/src/components/ha-picture-upload.ts +++ b/src/components/ha-picture-upload.ts @@ -39,6 +39,7 @@ export class HaPictureUpload extends LitElement { .uploading=${this._uploading} .value=${this.value ? html`` : ""} @file-picked=${this._handleFilePicked} + @change=${this._handleFileCleared} accept="image/png, image/jpeg, image/gif" > `; @@ -53,6 +54,10 @@ export class HaPictureUpload extends LitElement { } } + private async _handleFileCleared() { + this.value = null; + } + private async _cropFile(file: File) { if (!["image/png", "image/jpeg", "image/gif"].includes(file.type)) { showAlertDialog(this, { From e8b9766eb60848fcfaba363d367bd44b5c854697 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 6 Dec 2021 19:39:09 +0100 Subject: [PATCH 11/16] Fix camera stream rendering in area card without picture (#10815) --- src/panels/lovelace/cards/hui-area-card.ts | 2 +- src/translations/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 478201625d..6706a094be 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -367,7 +367,7 @@ export class HuiAreaCard } return html` - + ${area.picture || cameraEntityId ? html` Date: Mon, 6 Dec 2021 19:47:35 +0100 Subject: [PATCH 12/16] Fix zwavejs provisioned view (#10809) --- src/data/zwave_js.ts | 12 +++--- .../zwave_js/dialog-zwave_js-add-node.ts | 9 ++++ .../zwave_js/show-dialog-zwave_js-add-node.ts | 1 + .../zwave_js/zwave_js-config-dashboard.ts | 1 + .../zwave_js/zwave_js-provisioned.ts | 43 +++++++++++++++---- src/translations/en.json | 2 + 6 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/data/zwave_js.ts b/src/data/zwave_js.ts index 9296ca8990..4940cd2d92 100644 --- a/src/data/zwave_js.ts +++ b/src/data/zwave_js.ts @@ -208,11 +208,11 @@ export const enum NodeStatus { export interface ZwaveJSProvisioningEntry { /** The device specific key (DSK) in the form aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222 */ dsk: string; - securityClasses: SecurityClass[]; - /** - * Additional properties to be stored in this provisioning entry, e.g. the device ID from a scanned QR code - */ - [prop: string]: any; + security_classes: SecurityClass[]; + additional_properties: { + nodeId?: number; + [prop: string]: any; + }; } export interface RequestedGrant { @@ -278,7 +278,7 @@ export const setZwaveDataCollectionPreference = ( export const fetchZwaveProvisioningEntries = ( hass: HomeAssistant, entry_id: string -): Promise => +): Promise => hass.callWS({ type: "zwave_js/get_provisioning_entries", entry_id, diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-add-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-add-node.ts index b9f846fa09..151350351d 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-add-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-add-node.ts @@ -45,6 +45,8 @@ export interface ZWaveJSAddNodeDevice { class DialogZWaveJSAddNode extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @state() private _params?: ZWaveJSAddNodeDialogParams; + @state() private _entryId?: string; @state() private _status?: @@ -91,6 +93,7 @@ class DialogZWaveJSAddNode extends LitElement { } public async showDialog(params: ZWaveJSAddNodeDialogParams): Promise { + this._params = params; this._entryId = params.entry_id; this._status = "loading"; this._checkSmartStartSupport(); @@ -562,6 +565,9 @@ class DialogZWaveJSAddNode extends LitElement { provisioningInfo ); this._status = "provisioned"; + if (this._params?.addedCallback) { + this._params.addedCallback(); + } } catch (err: any) { this._error = err.message; this._status = "failed"; @@ -693,6 +699,9 @@ class DialogZWaveJSAddNode extends LitElement { if (message.event === "interview completed") { this._unsubscribe(); this._status = "finished"; + if (this._params?.addedCallback) { + this._params.addedCallback(); + } } if (message.event === "interview stage completed") { diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node.ts index 702a618fa3..d956d79d78 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node.ts @@ -2,6 +2,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; export interface ZWaveJSAddNodeDialogParams { entry_id: string; + addedCallback?: () => void; } export const loadAddNodeDialog = () => import("./dialog-zwave_js-add-node"); diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts index 413e6bd099..afd91be9a4 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts @@ -411,6 +411,7 @@ class ZWaveJSConfigDashboard extends LitElement { private async _addNodeClicked() { showZWaveJSAddNodeDialog(this, { entry_id: this.configEntryId!, + addedCallback: () => this._fetchData(), }); } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts index cad49415cf..a9a41cdadb 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts @@ -1,4 +1,4 @@ -import { mdiDelete } from "@mdi/js"; +import { mdiCheckCircle, mdiCloseCircleOutline, mdiDelete } from "@mdi/js"; import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -42,17 +42,42 @@ class ZWaveJSProvisioned extends LitElement { private _columns = memoizeOne( (narrow: boolean): DataTableColumnContainer => ({ + included: { + title: this.hass.localize( + "ui.panel.config.zwave_js.provisioned.included" + ), + type: "icon", + width: "100px", + template: (_info, provisioningEntry: any) => + provisioningEntry.additional_properties.nodeId + ? html` + + ` + : html` + + `, + }, dsk: { title: this.hass.localize("ui.panel.config.zwave_js.provisioned.dsk"), sortable: true, filterable: true, grows: true, }, - securityClasses: { + security_classes: { title: this.hass.localize( "ui.panel.config.zwave_js.provisioned.security_classes" ), - width: "15%", + width: "30%", hidden: narrow, filterable: true, sortable: true, @@ -60,7 +85,7 @@ class ZWaveJSProvisioned extends LitElement { securityClasses .map((secClass) => this.hass.localize( - `ui.panel.config.zwave_js.security_classes.${SecurityClass[secClass]}` + `ui.panel.config.zwave_js.security_classes.${SecurityClass[secClass]}.title` ) ) .join(", "), @@ -70,6 +95,7 @@ class ZWaveJSProvisioned extends LitElement { "ui.panel.config.zwave_js.provisioned.unprovison" ), type: "icon-button", + width: "100px", template: (_info, provisioningEntry: any) => html` { + const dsk = ev.currentTarget.provisioningEntry.dsk; + const confirm = await showConfirmationDialog(this, { title: this.hass.localize( "ui.panel.config.zwave_js.provisioned.confirm_unprovision_title" @@ -113,11 +141,8 @@ class ZWaveJSProvisioned extends LitElement { return; } - await unprovisionZwaveSmartStartNode( - this.hass, - this.configEntryId, - ev.currentTarget.provisioningEntry.dsk - ); + await unprovisionZwaveSmartStartNode(this.hass, this.configEntryId, dsk); + this._fetchData(); }; } diff --git a/src/translations/en.json b/src/translations/en.json index 0f7ad0b5ee..ab73f2fc4c 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2897,6 +2897,8 @@ "dsk": "DSK", "security_classes": "Security classes", "unprovison": "Unprovison", + "included": "Included", + "not_included": "Not Included", "confirm_unprovision_title": "Are you sure you want to unprovision the device?", "confirm_unprovision_text": "If you unprovision the device it will not be added to Home Assistant when it is powered on. If it is already added to Home Assistant, removing the provisioned device will not remove it from Home Assistant." }, From a065740c918b1f417a070d663a5c099cae61c258 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 6 Dec 2021 13:49:23 -0500 Subject: [PATCH 13/16] zwave_js config param should only be a toggle if there are 2 states (#10812) --- .../integration-panels/zwave_js/zwave_js-node-config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts index 19b7d24dd8..3dcd84f9aa 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-node-config.ts @@ -327,6 +327,9 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) { if (!("states" in item.metadata)) { return false; } + if (Object.keys(item.metadata.states).length !== 2) { + return false; + } if (!(0 in item.metadata.states) || !(1 in item.metadata.states)) { return false; } From 2459477ec4d3b7555c16a558a539cf8b1c452c47 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 6 Dec 2021 20:13:32 +0100 Subject: [PATCH 14/16] Add struct for state trigger and condition (#10811) * Add struct for state trigger and condition * remove `milliseconds` from struct --- .../types/ha-automation-condition-state.ts | 35 +++++++++++-------- src/panels/config/automation/structs.ts | 8 +++++ .../trigger/ha-automation-trigger-row.ts | 2 +- .../types/ha-automation-trigger-state.ts | 25 +++++++++++-- src/resources/styles.ts | 1 + src/translations/en.json | 3 +- 6 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 src/panels/config/automation/structs.ts diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts index 03c9ce25c0..5df15283b7 100644 --- a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts +++ b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts @@ -1,17 +1,27 @@ import "@polymer/paper-input/paper-input"; import { html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; +import { assert, literal, object, optional, string, union } from "superstruct"; import { createDurationData } from "../../../../../common/datetime/create_duration_data"; +import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/entity/ha-entity-attribute-picker"; import "../../../../../components/entity/ha-entity-picker"; +import "../../../../../components/ha-duration-input"; import { StateCondition } from "../../../../../data/automation"; import { HomeAssistant } from "../../../../../types"; +import { forDictStruct } from "../../structs"; import { ConditionElement, handleChangeEvent, } from "../ha-automation-condition-row"; -import "../../../../../components/ha-duration-input"; -import { fireEvent } from "../../../../../common/dom/fire_event"; + +const stateConditionStruct = object({ + condition: literal("state"), + entity_id: string(), + attribute: optional(string()), + state: string(), + for: optional(union([string(), forDictStruct])), +}); @customElement("ha-automation-condition-state") export class HaStateCondition extends LitElement implements ConditionElement { @@ -23,19 +33,14 @@ export class HaStateCondition extends LitElement implements ConditionElement { return { entity_id: "", state: "" }; } - public willUpdate(changedProperties: PropertyValues): boolean { - if ( - changedProperties.has("condition") && - Array.isArray(this.condition?.state) - ) { - fireEvent( - this, - "ui-mode-not-available", - Error(this.hass.localize("ui.errors.config.no_state_array_support")) - ); - // We have to stop the update if state is an array. - // Otherwise the state will be changed to a comma-separated string by the input element. - return false; + public shouldUpdate(changedProperties: PropertyValues) { + if (changedProperties.has("condition")) { + try { + assert(this.condition, stateConditionStruct); + } catch (e: any) { + fireEvent(this, "ui-mode-not-available", e); + return false; + } } return true; } diff --git a/src/panels/config/automation/structs.ts b/src/panels/config/automation/structs.ts new file mode 100644 index 0000000000..eb6baefa97 --- /dev/null +++ b/src/panels/config/automation/structs.ts @@ -0,0 +1,8 @@ +import { object, optional, number } from "superstruct"; + +export const forDictStruct = object({ + days: optional(number()), + hours: optional(number()), + minutes: optional(number()), + seconds: optional(number()), +}); diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index 3e8a6bee37..7db893f8fe 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -68,7 +68,7 @@ export const handleChangeEvent = (element: TriggerElement, ev: CustomEvent) => { } let newTrigger: Trigger; - if (!newVal) { + if (newVal === undefined || newVal === "") { newTrigger = { ...element.trigger }; delete newTrigger[name]; } else { diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts index 6be2d30fab..e75f2f8b33 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts @@ -1,19 +1,30 @@ import "@polymer/paper-input/paper-input"; import { html, LitElement, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; +import { assert, literal, object, optional, string, union } from "superstruct"; import { createDurationData } from "../../../../../common/datetime/create_duration_data"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { hasTemplate } from "../../../../../common/string/has-template"; import "../../../../../components/entity/ha-entity-attribute-picker"; import "../../../../../components/entity/ha-entity-picker"; +import "../../../../../components/ha-duration-input"; import { StateTrigger } from "../../../../../data/automation"; import { HomeAssistant } from "../../../../../types"; -import "../../../../../components/ha-duration-input"; +import { forDictStruct } from "../../structs"; import { handleChangeEvent, TriggerElement, } from "../ha-automation-trigger-row"; +const stateTriggerStruct = object({ + platform: literal("state"), + entity_id: string(), + attribute: optional(string()), + from: optional(string()), + to: optional(string()), + for: optional(union([string(), forDictStruct])), +}); + @customElement("ha-automation-trigger-state") export class HaStateTrigger extends LitElement implements TriggerElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -24,9 +35,9 @@ export class HaStateTrigger extends LitElement implements TriggerElement { return { entity_id: "" }; } - public willUpdate(changedProperties: PropertyValues) { + public shouldUpdate(changedProperties: PropertyValues) { if (!changedProperties.has("trigger")) { - return; + return true; } // Check for templates in trigger. If found, revert to YAML mode. if (this.trigger && hasTemplate(this.trigger)) { @@ -35,7 +46,15 @@ export class HaStateTrigger extends LitElement implements TriggerElement { "ui-mode-not-available", Error(this.hass.localize("ui.errors.config.no_template_editor_support")) ); + return false; } + try { + assert(this.trigger, stateTriggerStruct); + } catch (e: any) { + fireEvent(this, "ui-mode-not-available", e); + return false; + } + return true; } protected render() { diff --git a/src/resources/styles.ts b/src/resources/styles.ts index 5c4cc273c6..150e5efede 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -95,6 +95,7 @@ export const derivedStyles = { "mdc-theme-text-disabled-on-light": "var(--disabled-text-color)", "mdc-theme-text-primary-on-background": "var(--primary-text-color)", "mdc-theme-text-secondary-on-background": "var(--secondary-text-color)", + "mdc-theme-text-hint-on-background": "var(--secondary-text-color)", "mdc-theme-text-icon-on-background": "var(--secondary-text-color)", "mdc-theme-error": "var(--error-color)", "app-header-text-color": "var(--text-primary-color)", diff --git a/src/translations/en.json b/src/translations/en.json index ab73f2fc4c..5c25d8c494 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -878,8 +878,7 @@ "key_missing": "Required key ''{key}'' is missing.", "key_not_expected": "Key ''{key}'' is not expected or not supported by the visual editor.", "key_wrong_type": "The provided value for ''{key}'' is not supported by the visual editor. We support ({type_correct}) but received ({type_wrong}).", - "no_template_editor_support": "Templates not supported in visual editor", - "no_state_array_support": "Multiple state values not supported in visual editor" + "no_template_editor_support": "Templates not supported in visual editor" }, "supervisor": { "title": "Could not load the Supervisor panel!", From 5c78b740053ea450848a5364c2d289d296216704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 7 Dec 2021 00:10:50 +0100 Subject: [PATCH 15/16] Reorder configuration (#10817) --- .../config/blueprint/ha-blueprint-overview.ts | 2 +- .../config/dashboard/ha-config-dashboard.ts | 30 +----------- .../config/dashboard/ha-config-navigation.ts | 19 +++++++- src/panels/config/ha-panel-config.ts | 46 +++++++++++-------- .../config/helpers/ha-config-helpers.ts | 2 +- 5 files changed, 49 insertions(+), 50 deletions(-) diff --git a/src/panels/config/blueprint/ha-blueprint-overview.ts b/src/panels/config/blueprint/ha-blueprint-overview.ts index 0e65094222..35fd53a523 100644 --- a/src/panels/config/blueprint/ha-blueprint-overview.ts +++ b/src/panels/config/blueprint/ha-blueprint-overview.ts @@ -224,7 +224,7 @@ class HaBlueprintOverview extends LitElement { .narrow=${this.narrow} back-path="/config" .route=${this.route} - .tabs=${configSections.automations} + .tabs=${configSections.blueprints} .columns=${this._columns(this.narrow, this.hass.language)} .data=${this._processedBlueprints(this.blueprints)} id="entity_id" diff --git a/src/panels/config/dashboard/ha-config-dashboard.ts b/src/panels/config/dashboard/ha-config-dashboard.ts index 3d7dc017f8..5e033fcf4c 100644 --- a/src/panels/config/dashboard/ha-config-dashboard.ts +++ b/src/panels/config/dashboard/ha-config-dashboard.ts @@ -1,4 +1,4 @@ -import { mdiCellphoneCog, mdiCloudLock } from "@mdi/js"; +import { mdiCloudLock } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import { @@ -110,29 +110,10 @@ class HaConfigDashboard extends LitElement { > ` : ""} - ${this._externalConfig?.hasSettingsScreen - ? html` - - ` - : ""} @@ -142,13 +123,6 @@ class HaConfigDashboard extends LitElement { `; } - private _handleExternalAppConfiguration(ev: Event) { - ev.preventDefault(); - this.hass.auth.external!.fireMessage({ - type: "config_screen/show", - }); - } - static get styles(): CSSResultGroup { return [ haStyle, diff --git a/src/panels/config/dashboard/ha-config-navigation.ts b/src/panels/config/dashboard/ha-config-navigation.ts index b577fe1abe..a076385d10 100644 --- a/src/panels/config/dashboard/ha-config-navigation.ts +++ b/src/panels/config/dashboard/ha-config-navigation.ts @@ -6,6 +6,7 @@ import { canShowPage } from "../../../common/config/can_show_page"; import "../../../components/ha-card"; import "../../../components/ha-icon-next"; import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud"; +import { ExternalConfig } from "../../../external_app/external_config"; import { PageNavigation } from "../../../layouts/hass-tabs-subpage"; import { HomeAssistant } from "../../../types"; @@ -19,10 +20,16 @@ class HaConfigNavigation extends LitElement { @property() public pages!: PageNavigation[]; + @property() public externalConfig?: ExternalConfig; + protected render(): TemplateResult { return html` ${this.pages.map((page) => - canShowPage(this.hass, page) + ( + page.path === "#external-app-configuration" + ? this.externalConfig?.hasSettingsScreen + : canShowPage(this.hass, page) + ) ? html` @@ -77,6 +84,16 @@ class HaConfigNavigation extends LitElement { private _entryClicked(ev) { ev.currentTarget.blur(); + if ( + ev.currentTarget.parentElement.href.endsWith( + "#external-app-configuration" + ) + ) { + ev.preventDefault(); + this.hass.auth.external!.fireMessage({ + type: "config_screen/show", + }); + } } static get styles(): CSSResultGroup { diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 333d87a27f..9b1a3243db 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -1,6 +1,7 @@ import { mdiAccount, mdiBadgeAccountHorizontal, + mdiCellphoneCog, mdiCog, mdiDevices, mdiHomeAssistant, @@ -57,22 +58,22 @@ export const configSections: { [name: string]: PageNavigation[] } = { { path: "/config/automation", name: "Automations & Scenes", - description: "Automations, blueprints, scenes and scripts", + description: "Manage automations, scenes, scripts and helpers", iconPath: mdiRobot, iconColor: "#518C43", - components: ["automation", "blueprint", "scene", "script"], - }, - { - path: "/config/helpers", - name: "Automation Helpers", - description: "Elements that help build automations", - iconPath: mdiTools, - iconColor: "#4D2EA4", core: true, }, + { + path: "/config/blueprint", + name: "Blueprints", + description: "Manage blueprints", + iconPath: mdiPaletteSwatch, + iconColor: "#64B5F6", + component: "blueprint", + }, { path: "/hassio", - name: "Add-ons & Backups (Supervisor)", + name: "Add-ons, Backups & Supervisor", description: "Create backups, check logs or reboot your system", iconPath: mdiHomeAssistant, iconColor: "#4084CD", @@ -111,6 +112,13 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconColor: "#E48629", components: ["person", "zone", "users"], }, + { + path: "#external-app-configuration", + name: "Companion App", + description: "Location and notifications", + iconPath: mdiCellphoneCog, + iconColor: "#8E24AA", + }, { path: "/config/core", name: "Settings", @@ -155,13 +163,6 @@ export const configSections: { [name: string]: PageNavigation[] } = { }, ], automations: [ - { - component: "blueprint", - path: "/config/blueprint", - translationKey: "ui.panel.config.blueprint.caption", - iconPath: mdiPaletteSwatch, - iconColor: "#518C43", - }, { component: "automation", path: "/config/automation", @@ -183,8 +184,6 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconPath: mdiScriptText, iconColor: "#518C43", }, - ], - helpers: [ { component: "helpers", path: "/config/helpers", @@ -194,6 +193,15 @@ export const configSections: { [name: string]: PageNavigation[] } = { core: true, }, ], + blueprints: [ + { + component: "blueprint", + path: "/config/blueprint", + translationKey: "ui.panel.config.blueprint.caption", + iconPath: mdiPaletteSwatch, + iconColor: "#518C43", + }, + ], tags: [ { component: "tag", diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index d33d73e32b..3a54f1de0a 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -132,7 +132,7 @@ export class HaConfigHelpers extends LitElement { .narrow=${this.narrow} back-path="/config" .route=${this.route} - .tabs=${configSections.helpers} + .tabs=${configSections.automations} .columns=${this._columns(this.narrow, this.hass.language)} .data=${this._getItems(this._stateItems)} @row-click=${this._openEditDialog} From 8412cd71cbdff3264ded36892f7c9ca38f314fb3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 6 Dec 2021 15:11:11 -0800 Subject: [PATCH 16/16] Bumped version to 20211206.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3f9a1d8f35..86d9feb61d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20211203.0", + version="20211206.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/frontend", author="The Home Assistant Authors",