From add92a559d20daf193c5268d335762979ddd4692 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 2 Mar 2022 17:50:01 +0100 Subject: [PATCH 01/35] Fix quickbar overlaying, fix click handling (#11900) --- src/dialogs/quick-bar/ha-quick-bar.ts | 57 ++++++++++++++------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 337855d71b48..3a7b3aaa9631 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -86,11 +86,11 @@ export class QuickBar extends LitElement { @state() private _search = ""; - @state() private _opened = false; + @state() private _open = false; @state() private _commandMode = false; - @state() private _done = false; + @state() private _opened = false; @state() private _narrow = false; @@ -109,12 +109,12 @@ export class QuickBar extends LitElement { "all and (max-width: 450px), all and (max-height: 500px)" ).matches; this._initializeItemsIfNeeded(); - this._opened = true; + this._open = true; } public closeDialog() { + this._open = false; this._opened = false; - this._done = false; this._focusSet = false; this._filter = ""; this._search = ""; @@ -133,7 +133,7 @@ export class QuickBar extends LitElement { ); protected render() { - if (!this._opened) { + if (!this._open) { return html``; } @@ -218,24 +218,26 @@ export class QuickBar extends LitElement { ` : html` - - + ${this._opened + ? html` + ` + : ""} `} ${this._hint ? html`
${this._hint}
` : ""} @@ -252,9 +254,7 @@ export class QuickBar extends LitElement { } private _handleOpened() { - this.updateComplete.then(() => { - this._done = true; - }); + this._opened = true; } private async _handleRangeChanged(e) { @@ -454,9 +454,10 @@ export class QuickBar extends LitElement { } private _handleItemClick(ev) { + const listItem = ev.target.closest("mwc-list-item"); this.processItemAndCloseDialog( - (ev.target as any).item, - Number((ev.target as HTMLElement).getAttribute("index")) + listItem.item, + Number(listItem.getAttribute("index")) ); } From 24829bd9038ca270d689fef2ad01cab47be64806 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Thu, 3 Mar 2022 04:15:22 -0500 Subject: [PATCH 02/35] Supervisor mobile click accessibility (#11915) --- hassio/src/entrypoint.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hassio/src/entrypoint.ts b/hassio/src/entrypoint.ts index 48c4a3318050..09e73ecf4ddd 100644 --- a/hassio/src/entrypoint.ts +++ b/hassio/src/entrypoint.ts @@ -1,9 +1,12 @@ // Compat needs to be first import import "../../src/resources/compatibility"; +import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings"; import "../../src/resources/roboto"; import "../../src/resources/safari-14-attachshadow-patch"; import "./hassio-main"; +setCancelSyntheticClickEvents(false); + const styleEl = document.createElement("style"); styleEl.innerHTML = ` body { From 797c871137d6123996105f4680e753464ba82d4e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 3 Mar 2022 13:55:40 +0100 Subject: [PATCH 03/35] Convert objects to string in config flow error (#11908) --- src/dialogs/config-flow/dialog-data-entry-flow.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index 29bea7491e2a..6a128d081a7c 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -117,13 +117,17 @@ class DataEntryFlowDialog extends LitElement { ); } catch (err: any) { this.closeDialog(); + let message = err.message || err.body || "Unknown error"; + if (typeof message !== "string") { + message = JSON.stringify(message); + } showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.error" ), text: `${this.hass.localize( "ui.panel.config.integrations.config_flow.could_not_load" - )}: ${err.message || err.body}`, + )}: ${message}`, }); return; } From 8c445f6409f71d3bc29890ea663fb11dafefa330 Mon Sep 17 00:00:00 2001 From: Robin Wittebol Date: Thu, 3 Mar 2022 19:45:03 +0100 Subject: [PATCH 04/35] Fix datepicker triangle (#11920) --- src/components/date-range-picker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/date-range-picker.ts b/src/components/date-range-picker.ts index f0c0951a77e0..e6a5c6479f41 100644 --- a/src/components/date-range-picker.ts +++ b/src/components/date-range-picker.ts @@ -115,7 +115,7 @@ class DateRangePickerElement extends WrappedElement { color: var(--primary-text-color); min-width: initial !important; } - .daterangepicker:after { + .daterangepicker:before { display: none; } .daterangepicker:after { From 604b79696e001a0b36c8daef45e99a37f9fab93f Mon Sep 17 00:00:00 2001 From: Robin Wittebol Date: Thu, 3 Mar 2022 19:46:14 +0100 Subject: [PATCH 05/35] Always show tab labels (#11919) --- src/components/ha-tab.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/ha-tab.ts b/src/components/ha-tab.ts index da172e1fab2b..106ebd6211f5 100644 --- a/src/components/ha-tab.ts +++ b/src/components/ha-tab.ts @@ -42,9 +42,7 @@ export class HaTab extends LitElement { @keydown=${this._handleKeyDown} > ${this.narrow ? html`` : ""} - ${!this.narrow || this.active - ? html`${this.name}` - : ""} + ${this.name} ${this._shouldRenderRipple ? html`` : ""} `; From 8f8017ecff0b06b1bf60f203cfed9b5ca36544ad Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Mar 2022 23:10:44 +0100 Subject: [PATCH 06/35] Remove zwave and ozw panels (#11911) Remove zwave and ozw panels --- cast/src/receiver/second-load.ts | 1 - package.json | 2 - src/data/ozw.ts | 213 ----- src/data/zwave.ts | 81 -- .../ozw/ha-device-actions-ozw.ts | 84 -- .../ozw/ha-device-info-ozw.ts | 99 --- .../zwave_js/ha-device-info-zwave_js.ts | 8 +- .../config/devices/ha-config-device-page.ts | 16 - src/panels/config/ha-panel-config.ts | 10 - .../integrations/ha-integration-card.ts | 2 - .../ozw/dialog-ozw-refresh-node.ts | 269 ------ .../ozw/ozw-config-dashboard.ts | 260 ------ .../ozw/ozw-config-router.ts | 67 -- .../ozw/ozw-network-dashboard.ts | 245 ------ .../ozw/ozw-network-nodes.ts | 131 --- .../ozw/ozw-network-router.ts | 74 -- .../integration-panels/ozw/ozw-node-config.ts | 265 ------ .../ozw/ozw-node-dashboard.ts | 254 ------ .../integration-panels/ozw/ozw-node-router.ts | 84 -- .../ozw/show-dialog-ozw-refresh-node.ts | 19 - .../zwave/ha-config-zwave.js | 765 ------------------ .../zwave/zwave-config-router.ts | 62 -- .../integration-panels/zwave/zwave-groups.js | 380 --------- .../zwave/zwave-log-dialog.js | 83 -- .../integration-panels/zwave/zwave-log.js | 160 ---- .../zwave/zwave-migration.ts | 573 ------------- .../integration-panels/zwave/zwave-network.ts | 302 ------- .../zwave/zwave-node-config.ts | 388 --------- .../zwave/zwave-node-protection.js | 179 ---- .../zwave/zwave-usercodes.js | 226 ------ .../integration-panels/zwave/zwave-values.ts | 109 --- src/translations/en.json | 204 ----- yarn.lock | 70 +- 33 files changed, 9 insertions(+), 5676 deletions(-) delete mode 100644 src/data/ozw.ts delete mode 100644 src/data/zwave.ts delete mode 100644 src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts delete mode 100644 src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/dialog-ozw-refresh-node.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-node-config.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts delete mode 100644 src/panels/config/integrations/integration-panels/ozw/show-dialog-ozw-refresh-node.ts delete mode 100644 src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-config-router.ts delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-groups.js delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-log-dialog.js delete mode 100755 src/panels/config/integrations/integration-panels/zwave/zwave-log.js delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-migration.ts delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-network.ts delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-node-config.ts delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-node-protection.js delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-usercodes.js delete mode 100644 src/panels/config/integrations/integration-panels/zwave/zwave-values.ts diff --git a/cast/src/receiver/second-load.ts b/cast/src/receiver/second-load.ts index e3c0885561d5..97de839a76ff 100644 --- a/cast/src/receiver/second-load.ts +++ b/cast/src/receiver/second-load.ts @@ -1,4 +1,3 @@ -import "web-animations-js/web-animations-next-lite.min"; import "../../../src/resources/ha-style"; import "../../../src/resources/roboto"; import "./layout/hc-lovelace"; diff --git a/package.json b/package.json index c9e04cedd0c2..24c7cccf92f0 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,6 @@ "@polymer/iron-icon": "^3.0.1", "@polymer/iron-input": "^3.0.1", "@polymer/iron-resizable-behavior": "^3.0.1", - "@polymer/paper-dropdown-menu": "^3.2.0", "@polymer/paper-input": "^3.2.1", "@polymer/paper-item": "^3.0.1", "@polymer/paper-listbox": "^3.0.1", @@ -136,7 +135,6 @@ "vis-network": "^8.5.4", "vue": "^2.6.12", "vue2-daterange-picker": "^0.5.1", - "web-animations-js": "^2.3.2", "workbox-cacheable-response": "^6.4.2", "workbox-core": "^6.4.2", "workbox-expiration": "^6.4.2", diff --git a/src/data/ozw.ts b/src/data/ozw.ts deleted file mode 100644 index 011a685fd86e..000000000000 --- a/src/data/ozw.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { HomeAssistant } from "../types"; -import { DeviceRegistryEntry } from "./device_registry"; - -export interface OZWNodeIdentifiers { - ozw_instance: number; - node_id: number; -} - -export interface OZWDevice { - node_id: number; - node_query_stage: string; - is_awake: boolean; - is_failed: boolean; - is_zwave_plus: boolean; - ozw_instance: number; - event: string; - node_manufacturer_name: string; - node_product_name: string; -} - -export interface OZWDeviceMetaDataResponse { - node_id: number; - ozw_instance: number; - metadata: OZWDeviceMetaData; -} - -export interface OZWDeviceMetaData { - OZWInfoURL: string; - ZWAProductURL: string; - ProductPic: string; - Description: string; - ProductManualURL: string; - ProductPageURL: string; - InclusionHelp: string; - ExclusionHelp: string; - ResetHelp: string; - WakeupHelp: string; - ProductSupportURL: string; - Frequency: string; - Name: string; - ProductPicBase64: string; -} - -export interface OZWInstance { - ozw_instance: number; - OZWDaemon_Version: string; - OpenZWave_Version: string; - QTOpenZWave_Version: string; - Status: string; - getControllerPath: string; - homeID: string; -} - -export interface OZWNetworkStatistics { - ozw_instance: number; - node_count: number; - readCnt: number; - writeCnt: number; - ACKCnt: number; - CANCnt: number; - NAKCnt: number; - dropped: number; - retries: number; -} - -export interface OZWDeviceConfig { - label: string; - type: string; - value: string | number; - parameter: number; - min: number; - max: number; - help: string; -} - -export const nodeQueryStages = [ - "ProtocolInfo", - "Probe", - "WakeUp", - "ManufacturerSpecific1", - "NodeInfo", - "NodePlusInfo", - "ManufacturerSpecific2", - "Versions", - "Instances", - "Static", - "CacheLoad", - "Associations", - "Neighbors", - "Session", - "Dynamic", - "Configuration", - "Complete", -]; - -export const networkOnlineStatuses = [ - "driverAllNodesQueried", - "driverAllNodesQueriedSomeDead", - "driverAwakeNodesQueried", -]; -export const networkStartingStatuses = [ - "starting", - "started", - "Ready", - "driverReady", -]; -export const networkOfflineStatuses = [ - "Offline", - "stopped", - "driverFailed", - "driverReset", - "driverRemoved", - "driverAllNodesOnFire", -]; - -export const getIdentifiersFromDevice = function ( - device: DeviceRegistryEntry -): OZWNodeIdentifiers | undefined { - if (!device) { - return undefined; - } - - const ozwIdentifier = device.identifiers.find( - (identifier) => identifier[0] === "ozw" - ); - if (!ozwIdentifier) { - return undefined; - } - - const identifiers = ozwIdentifier[1].split("."); - return { - node_id: parseInt(identifiers[1]), - ozw_instance: parseInt(identifiers[0]), - }; -}; - -export const fetchOZWInstances = ( - hass: HomeAssistant -): Promise => - hass.callWS({ - type: "ozw/get_instances", - }); - -export const fetchOZWNetworkStatus = ( - hass: HomeAssistant, - ozw_instance: number -): Promise => - hass.callWS({ - type: "ozw/network_status", - ozw_instance, - }); - -export const fetchOZWNetworkStatistics = ( - hass: HomeAssistant, - ozw_instance: number -): Promise => - hass.callWS({ - type: "ozw/network_statistics", - ozw_instance, - }); - -export const fetchOZWNodes = ( - hass: HomeAssistant, - ozw_instance: number -): Promise => - hass.callWS({ - type: "ozw/get_nodes", - ozw_instance, - }); - -export const fetchOZWNodeStatus = ( - hass: HomeAssistant, - ozw_instance: number, - node_id: number -): Promise => - hass.callWS({ - type: "ozw/node_status", - ozw_instance, - node_id, - }); - -export const fetchOZWNodeMetadata = ( - hass: HomeAssistant, - ozw_instance: number, - node_id: number -): Promise => - hass.callWS({ - type: "ozw/node_metadata", - ozw_instance, - node_id, - }); - -export const fetchOZWNodeConfig = ( - hass: HomeAssistant, - ozw_instance: number, - node_id: number -): Promise => - hass.callWS({ - type: "ozw/get_config_parameters", - ozw_instance, - node_id, - }); - -export const refreshNodeInfo = ( - hass: HomeAssistant, - ozw_instance: number, - node_id: number -): Promise => - hass.callWS({ - type: "ozw/refresh_node_info", - ozw_instance, - node_id, - }); diff --git a/src/data/zwave.ts b/src/data/zwave.ts deleted file mode 100644 index fb6fbe935e80..000000000000 --- a/src/data/zwave.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { HomeAssistant } from "../types"; - -export interface ZWaveNetworkStatus { - state: number; -} - -export interface ZWaveValue { - key: number; - value: { - index: number; - instance: number; - label: string; - poll_intensity: number; - }; -} - -export interface ZWaveConfigItem { - key: number; - value: { - data: any; - data_items: any[]; - help: string; - label: string; - max: number; - min: number; - type: string; - }; -} - -export interface ZWaveConfigServiceData { - node_id: number; - parameter: number; - value: number | string; -} - -export interface ZWaveNode { - attributes: ZWaveAttributes; -} - -export interface ZWaveAttributes { - node_id: number; - wake_up_interval?: number; -} - -export interface ZWaveMigrationConfig { - usb_path: string; - network_key: string; -} - -export const ZWAVE_NETWORK_STATE_STOPPED = 0; -export const ZWAVE_NETWORK_STATE_FAILED = 1; -export const ZWAVE_NETWORK_STATE_STARTED = 5; -export const ZWAVE_NETWORK_STATE_AWAKED = 7; -export const ZWAVE_NETWORK_STATE_READY = 10; - -export const fetchNetworkStatus = ( - hass: HomeAssistant -): Promise => - hass.callWS({ - type: "zwave/network_status", - }); - -export const startZwaveJsConfigFlow = ( - hass: HomeAssistant -): Promise<{ flow_id: string }> => - hass.callWS({ - type: "zwave/start_zwave_js_config_flow", - }); - -export const fetchMigrationConfig = ( - hass: HomeAssistant -): Promise => - hass.callWS({ - type: "zwave/get_migration_config", - }); - -export const fetchValues = (hass: HomeAssistant, nodeId: number) => - hass.callApi("GET", `zwave/values/${nodeId}`); - -export const fetchNodeConfig = (hass: HomeAssistant, nodeId: number) => - hass.callApi("GET", `zwave/config/${nodeId}`); diff --git a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts b/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts deleted file mode 100644 index 8b9f4309a9db..000000000000 --- a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts +++ /dev/null @@ -1,84 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { - css, - CSSResultGroup, - html, - LitElement, - PropertyValues, - TemplateResult, -} from "lit"; -import { customElement, property } from "lit/decorators"; -import { navigate } from "../../../../../../common/navigate"; -import { DeviceRegistryEntry } from "../../../../../../data/device_registry"; -import { - getIdentifiersFromDevice, - OZWNodeIdentifiers, -} from "../../../../../../data/ozw"; -import { haStyle } from "../../../../../../resources/styles"; -import { HomeAssistant } from "../../../../../../types"; -import { showOZWRefreshNodeDialog } from "../../../../integrations/integration-panels/ozw/show-dialog-ozw-refresh-node"; - -@customElement("ha-device-actions-ozw") -export class HaDeviceActionsOzw extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public device!: DeviceRegistryEntry; - - @property() - private node_id = 0; - - @property() - private ozw_instance = 1; - - protected updated(changedProperties: PropertyValues) { - if (changedProperties.has("device")) { - const identifiers: OZWNodeIdentifiers | undefined = - getIdentifiersFromDevice(this.device); - if (!identifiers) { - return; - } - this.ozw_instance = identifiers.ozw_instance; - this.node_id = identifiers.node_id; - } - } - - protected render(): TemplateResult { - if (!this.ozw_instance || !this.node_id) { - return html``; - } - return html` - - ${this.hass.localize("ui.panel.config.ozw.node.button")} - - - ${this.hass.localize("ui.panel.config.ozw.refresh_node.button")} - - `; - } - - private async _refreshNodeClicked() { - showOZWRefreshNodeDialog(this, { - node_id: this.node_id, - ozw_instance: this.ozw_instance, - }); - } - - private async _nodeDetailsClicked() { - navigate( - `/config/ozw/network/${this.ozw_instance}/node/${this.node_id}/dashboard` - ); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - :host { - display: flex; - flex-direction: column; - align-items: flex-start; - } - `, - ]; - } -} diff --git a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts b/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts deleted file mode 100644 index 4717d235b8f0..000000000000 --- a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - css, - CSSResultGroup, - html, - LitElement, - PropertyValues, - TemplateResult, -} from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { DeviceRegistryEntry } from "../../../../../../data/device_registry"; -import { - fetchOZWNodeStatus, - getIdentifiersFromDevice, - OZWDevice, - OZWNodeIdentifiers, -} from "../../../../../../data/ozw"; -import { haStyle } from "../../../../../../resources/styles"; -import { HomeAssistant } from "../../../../../../types"; - -@customElement("ha-device-info-ozw") -export class HaDeviceInfoOzw extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public device!: DeviceRegistryEntry; - - @property() - private node_id = 0; - - @property() - private ozw_instance = 1; - - @state() private _ozwDevice?: OZWDevice; - - protected updated(changedProperties: PropertyValues) { - if (changedProperties.has("device")) { - const identifiers: OZWNodeIdentifiers | undefined = - getIdentifiersFromDevice(this.device); - if (!identifiers) { - return; - } - this.ozw_instance = identifiers.ozw_instance; - this.node_id = identifiers.node_id; - - this._fetchNodeDetails(); - } - } - - protected async _fetchNodeDetails() { - this._ozwDevice = await fetchOZWNodeStatus( - this.hass, - this.ozw_instance, - this.node_id - ); - } - - protected render(): TemplateResult { - if (!this._ozwDevice) { - return html``; - } - return html` -

- ${this.hass.localize("ui.panel.config.ozw.device_info.zwave_info")} -

-
- ${this.hass.localize("ui.panel.config.ozw.common.node_id")}: - ${this._ozwDevice.node_id} -
-
- ${this.hass.localize("ui.panel.config.ozw.device_info.stage")}: - ${this._ozwDevice.node_query_stage} -
-
- ${this.hass.localize("ui.panel.config.ozw.common.ozw_instance")}: - ${this._ozwDevice.ozw_instance} -
-
- ${this.hass.localize("ui.panel.config.ozw.device_info.node_failed")}: - ${this._ozwDevice.is_failed - ? this.hass.localize("ui.common.yes") - : this.hass.localize("ui.common.no")} -
- `; - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - h4 { - margin-bottom: 4px; - } - div { - word-break: break-all; - margin-top: 2px; - } - `, - ]; - } -} diff --git a/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-info-zwave_js.ts b/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-info-zwave_js.ts index 32a732662fd5..99f48c8989b3 100644 --- a/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-info-zwave_js.ts +++ b/src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-info-zwave_js.ts @@ -27,7 +27,7 @@ import { HomeAssistant } from "../../../../../../types"; export class HaDeviceInfoZWaveJS extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public device!: DeviceRegistryEntry; + @property({ attribute: false }) public device!: DeviceRegistryEntry; @state() private _entryId?: string; @@ -173,3 +173,9 @@ export class HaDeviceInfoZWaveJS extends LitElement { ]; } } + +declare global { + interface HTMLElementTagNameMap { + "ha-device-info-zwave_js": HaDeviceInfoZWaveJS; + } +} diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 55728341063a..7512b3067f94 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -902,22 +902,6 @@ export class HaConfigDevicePage extends LitElement { > `); } - if (domains.includes("ozw")) { - import("./device-detail/integration-elements/ozw/ha-device-actions-ozw"); - import("./device-detail/integration-elements/ozw/ha-device-info-ozw"); - deviceInfo.push(html` - - `); - deviceActions.push(html` - - `); - } if (domains.includes("zha")) { import("./device-detail/integration-elements/zha/ha-device-actions-zha"); import("./device-detail/integration-elements/zha/ha-device-info-zha"); diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index c938ca097534..850408f09507 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -376,21 +376,11 @@ class HaPanelConfig extends HassRouterPage { "./integrations/integration-panels/zha/zha-config-dashboard-router" ), }, - zwave: { - tag: "zwave-config-router", - load: () => - import("./integrations/integration-panels/zwave/zwave-config-router"), - }, mqtt: { tag: "mqtt-config-panel", load: () => import("./integrations/integration-panels/mqtt/mqtt-config-panel"), }, - ozw: { - tag: "ozw-config-router", - load: () => - import("./integrations/integration-panels/ozw/ozw-config-router"), - }, zwave_js: { tag: "zwave_js-config-router", load: () => diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 4af887d59a19..28154e279c6a 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -55,8 +55,6 @@ const integrationsWithPanel = { hassio: "/hassio/dashboard", mqtt: "/config/mqtt", zha: "/config/zha/dashboard", - ozw: "/config/ozw/dashboard", - zwave: "/config/zwave", zwave_js: "/config/zwave_js/dashboard", }; diff --git a/src/panels/config/integrations/integration-panels/ozw/dialog-ozw-refresh-node.ts b/src/panels/config/integrations/integration-panels/ozw/dialog-ozw-refresh-node.ts deleted file mode 100644 index 54ee3ce9d67c..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/dialog-ozw-refresh-node.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { - css, - CSSResultGroup, - html, - LitElement, - PropertyValues, - TemplateResult, -} from "lit"; -import { customElement, property, state } from "lit/decorators"; -import "../../../../../components/ha-circular-progress"; -import "../../../../../components/ha-code-editor"; -import { createCloseHeading } from "../../../../../components/ha-dialog"; -import { - fetchOZWNodeMetadata, - nodeQueryStages, - OZWDevice, - OZWDeviceMetaData, -} from "../../../../../data/ozw"; -import { haStyleDialog } from "../../../../../resources/styles"; -import { HomeAssistant } from "../../../../../types"; -import { OZWRefreshNodeDialogParams } from "./show-dialog-ozw-refresh-node"; - -@customElement("dialog-ozw-refresh-node") -class DialogOZWRefreshNode extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @state() private _node_id?: number; - - @state() private _ozw_instance = 1; - - @state() private _nodeMetaData?: OZWDeviceMetaData; - - @state() private _node?: OZWDevice; - - @state() private _active = false; - - @state() private _complete = false; - - private _refreshDevicesTimeoutHandle?: number; - - private _subscribed?: Promise<() => Promise>; - - public disconnectedCallback(): void { - super.disconnectedCallback(); - this._unsubscribe(); - } - - protected updated(changedProperties: PropertyValues): void { - super.update(changedProperties); - if (changedProperties.has("node_id")) { - this._fetchData(); - } - } - - private async _fetchData() { - if (!this._node_id) { - return; - } - const metaDataResponse = await fetchOZWNodeMetadata( - this.hass, - this._ozw_instance, - this._node_id - ); - - this._nodeMetaData = metaDataResponse.metadata; - } - - public async showDialog(params: OZWRefreshNodeDialogParams): Promise { - this._node_id = params.node_id; - this._ozw_instance = params.ozw_instance; - this._fetchData(); - } - - protected render(): TemplateResult { - if (!this._node_id) { - return html``; - } - - return html` - - ${this._complete - ? html` -

- ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.complete" - )} -

- - ${this.hass.localize("ui.common.close")} - - ` - : html` - ${this._active - ? html` -
- -
-

- - ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.refreshing_description" - )} - -

- ${this._node - ? html` -

- ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.node_status" - )}: - ${this._node.node_query_stage} - (${this.hass.localize( - "ui.panel.config.ozw.refresh_node.step" - )} - ${nodeQueryStages.indexOf( - this._node.node_query_stage - ) + 1}/17) -

-

- - ${this.hass.localize( - "ui.panel.config.ozw.node_query_stages." + - this._node.node_query_stage.toLowerCase() - )} -

- ` - : ``} -
-
- ` - : html` - ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.description" - )} -

- ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.battery_note" - )} -

- `} - ${this._nodeMetaData?.WakeupHelp !== "" - ? html` - - ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.wakeup_header" - )} - ${this._nodeMetaData!.Name} - -
- ${this._nodeMetaData!.WakeupHelp} -
- - ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.wakeup_instructions_source" - )} - -
- ` - : ""} - ${!this._active - ? html` - - ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.start_refresh_button" - )} - - ` - : html``} - `} -
- `; - } - - private _startRefresh(): void { - this._subscribe(); - } - - private _handleMessage(message: any): void { - if (message.type === "node_updated") { - this._node = message; - if (message.node_query_stage === "Complete") { - this._unsubscribe(); - this._complete = true; - } - } - } - - private _unsubscribe(): void { - this._active = false; - if (this._refreshDevicesTimeoutHandle) { - clearTimeout(this._refreshDevicesTimeoutHandle); - } - if (this._subscribed) { - this._subscribed.then((unsub) => unsub()); - this._subscribed = undefined; - } - } - - private _subscribe(): void { - if (!this.hass) { - return; - } - this._active = true; - this._subscribed = this.hass.connection.subscribeMessage( - (message) => this._handleMessage(message), - { - type: "ozw/refresh_node_info", - node_id: this._node_id, - ozw_instance: this._ozw_instance, - } - ); - this._refreshDevicesTimeoutHandle = window.setTimeout( - () => this._unsubscribe(), - 120000 - ); - } - - private _close(): void { - this._complete = false; - this._node_id = undefined; - this._node = undefined; - } - - static get styles(): CSSResultGroup { - return [ - haStyleDialog, - css` - blockquote { - display: block; - background-color: #ddd; - padding: 8px; - margin: 8px 0; - font-size: 0.9em; - } - - blockquote em { - font-size: 0.9em; - margin-top: 6px; - } - - .flex-container { - display: flex; - align-items: center; - } - - .flex-container ha-circular-progress { - margin-right: 20px; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "dialog-ozw-refresh-node": DialogOZWRefreshNode; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts deleted file mode 100644 index dcdb81518dca..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts +++ /dev/null @@ -1,260 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { mdiCheckCircle, mdiCircle, mdiCloseCircle, mdiZWave } from "@mdi/js"; -import "@polymer/paper-item/paper-icon-item"; -import "@polymer/paper-item/paper-item-body"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { navigate } from "../../../../../common/navigate"; -import "../../../../../components/ha-card"; -import "../../../../../components/ha-icon-next"; -import { - fetchOZWInstances, - networkOfflineStatuses, - networkOnlineStatuses, - networkStartingStatuses, - OZWInstance, -} from "../../../../../data/ozw"; -import "../../../../../layouts/hass-error-screen"; -import "../../../../../layouts/hass-loading-screen"; -import "../../../../../layouts/hass-tabs-subpage"; -import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; -import { haStyle } from "../../../../../resources/styles"; -import type { HomeAssistant, Route } from "../../../../../types"; -import "../../../ha-config-section"; -import "../../../../../components/ha-alert"; - -export const ozwTabs: PageNavigation[] = []; - -@customElement("ozw-config-dashboard") -class OZWConfigDashboard extends LitElement { - @property({ type: Object }) public hass!: HomeAssistant; - - @property({ type: Object }) public route!: Route; - - @property({ type: Boolean }) public narrow!: boolean; - - @property({ type: Boolean }) public isWide!: boolean; - - @property() public configEntryId?: string; - - @state() private _instances?: OZWInstance[]; - - protected firstUpdated() { - this._fetchData(); - } - - protected render(): TemplateResult { - if (!this._instances) { - return html``; - } - - if (this._instances.length === 0) { - return html``; - } - - return html` - - - The OpenZWave integration is deprecated and will no longer receive any - updates. The technical dependencies will render this integration - unusable in the near future. We strongly advise you to migrate to the - new - Z-Wave JS integration. - - learn more - - - - -
- ${this.hass.localize("ui.panel.config.ozw.select_instance.header")} -
- -
- ${this.hass.localize( - "ui.panel.config.ozw.select_instance.introduction" - )} -
- ${this._instances.length > 0 - ? html` - ${this._instances.map((instance) => { - let status = "unknown"; - let icon = mdiCircle; - if (networkOnlineStatuses.includes(instance.Status)) { - status = "online"; - icon = mdiCheckCircle; - } - if (networkStartingStatuses.includes(instance.Status)) { - status = "starting"; - } - if (networkOfflineStatuses.includes(instance.Status)) { - status = "offline"; - icon = mdiCloseCircle; - } - - return html` - - - - - - - ${this.hass.localize( - "ui.panel.config.ozw.common.instance" - )} - ${instance.ozw_instance} -
- - ${this.hass.localize( - "ui.panel.config.ozw.network_status." + status - )} - - - ${this.hass.localize( - "ui.panel.config.ozw.network_status.details." + - instance.Status.toLowerCase() - )}
- ${this.hass.localize( - "ui.panel.config.ozw.common.controller" - )} - : ${instance.getControllerPath}
- OZWDaemon ${instance.OZWDaemon_Version} (OpenZWave - ${instance.OpenZWave_Version}) -
-
- -
-
-
- `; - })} - ` - : ""} -
-
- `; - } - - private async _fetchData() { - this._instances = await fetchOZWInstances(this.hass!); - if (this._instances.length === 1) { - navigate(`/config/ozw/network/${this._instances[0].ozw_instance}`, { - replace: true, - }); - } - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - ha-card:last-child { - margin-bottom: 24px; - } - ha-config-section { - margin-top: -12px; - } - :host([narrow]) ha-config-section { - margin-top: -20px; - } - ha-alert { - display: block; - margin: 16px; - } - ha-alert a { - text-decoration: none; - } - ha-card { - overflow: hidden; - } - ha-card a { - text-decoration: none; - color: var(--primary-text-color); - } - paper-item-body { - margin: 16px 0; - } - a { - text-decoration: none; - color: var(--primary-text-color); - position: relative; - display: block; - outline: 0; - } - ha-svg-icon.network-status-icon { - height: 14px; - width: 14px; - } - .online { - color: green; - } - .starting { - color: orange; - } - .offline { - color: red; - } - ha-svg-icon, - ha-icon-next { - color: var(--secondary-text-color); - } - .iron-selected paper-item::before, - a:not(.iron-selected):focus::before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - content: ""; - transition: opacity 15ms linear; - will-change: opacity; - } - a:not(.iron-selected):focus::before { - background-color: currentColor; - opacity: var(--dark-divider-opacity); - } - .iron-selected paper-item:focus::before, - .iron-selected:focus paper-item::before { - opacity: 0.2; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-config-dashboard": OZWConfigDashboard; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts deleted file mode 100644 index ad2d2e38febe..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { customElement, property } from "lit/decorators"; -import memoizeOne from "memoize-one"; -import { - HassRouterPage, - RouterOptions, -} from "../../../../../layouts/hass-router-page"; -import { HomeAssistant, Route } from "../../../../../types"; - -export const computeTail = memoizeOne((route: Route) => { - const dividerPos = route.path.indexOf("/", 1); - return dividerPos === -1 - ? { - prefix: route.prefix + route.path, - path: "", - } - : { - prefix: route.prefix + route.path.substr(0, dividerPos), - path: route.path.substr(dividerPos), - }; -}); - -@customElement("ozw-config-router") -class OZWConfigRouter extends HassRouterPage { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public isWide!: boolean; - - @property() public narrow!: boolean; - - private _configEntry = new URLSearchParams(window.location.search).get( - "config_entry" - ); - - protected routerOptions: RouterOptions = { - defaultPage: "dashboard", - showLoading: true, - routes: { - dashboard: { - tag: "ozw-config-dashboard", - load: () => import("./ozw-config-dashboard"), - }, - network: { - tag: "ozw-network-router", - load: () => import("./ozw-network-router"), - }, - }, - }; - - protected updatePageEl(el): void { - el.route = this.routeTail; - el.hass = this.hass; - el.isWide = this.isWide; - el.narrow = this.narrow; - el.configEntryId = this._configEntry; - if (this._currentPage === "network") { - const path = this.routeTail.path.split("/"); - el.ozwInstance = path[1]; - el.route = computeTail(this.routeTail); - } - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-config-router": OZWConfigRouter; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts deleted file mode 100644 index 7dc547893911..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts +++ /dev/null @@ -1,245 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { mdiCheckCircle, mdiCircle, mdiCloseCircle } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; -import { navigate } from "../../../../../common/navigate"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import { - fetchOZWNetworkStatistics, - fetchOZWNetworkStatus, - networkOfflineStatuses, - networkOnlineStatuses, - networkStartingStatuses, - OZWInstance, - OZWNetworkStatistics, -} from "../../../../../data/ozw"; -import "../../../../../layouts/hass-tabs-subpage"; -import { haStyle } from "../../../../../resources/styles"; -import type { HomeAssistant, Route } from "../../../../../types"; -import "../../../ha-config-section"; -import { ozwNetworkTabs } from "./ozw-network-router"; - -@customElement("ozw-network-dashboard") -class OZWNetworkDashboard extends LitElement { - @property({ type: Object }) public hass!: HomeAssistant; - - @property({ type: Object }) public route!: Route; - - @property({ type: Boolean }) public narrow!: boolean; - - @property({ type: Boolean }) public isWide!: boolean; - - @property() public configEntryId?: string; - - @property() public ozwInstance?: number; - - @state() private _network?: OZWInstance; - - @state() private _statistics?: OZWNetworkStatistics; - - @state() private _status = "unknown"; - - @state() private _icon = mdiCircle; - - protected firstUpdated() { - if (!this.ozwInstance) { - navigate("/config/ozw/dashboard", { replace: true }); - } else if (this.hass) { - this._fetchData(); - } - } - - protected render(): TemplateResult { - return html` - - -
- ${this.hass.localize("ui.panel.config.ozw.network.header")} -
- -
- ${this.hass.localize("ui.panel.config.ozw.network.introduction")} -
- ${this._network - ? html` - -
-
- - ${this.hass.localize( - "ui.panel.config.ozw.common.network" - )} - ${this.hass.localize( - `ui.panel.config.ozw.network_status.${this._status}` - )} -
- - ${this.hass.localize( - `ui.panel.config.ozw.network_status.details.${this._network.Status.toLowerCase()}` - )} - -
-
- ${this.hass.localize( - "ui.panel.config.ozw.common.ozw_instance" - )} - ${this._network.ozw_instance} - ${this._statistics - ? html` - • - ${this.hass.localize( - "ui.panel.config.ozw.network.node_count", - "count", - this._statistics.node_count - )} - ` - : ``} -
- ${this.hass.localize( - "ui.panel.config.ozw.common.controller" - )}: - ${this._network.getControllerPath}
- OZWDaemon ${this._network.OZWDaemon_Version} (OpenZWave - ${this._network.OpenZWave_Version}) -
-
-
- ${this._generateServiceButton("add_node")} - ${this._generateServiceButton("remove_node")} - ${this._generateServiceButton("cancel_command")} -
-
- ` - : ``} -
-
- `; - } - - private async _fetchData() { - if (!this.ozwInstance) return; - this._network = await fetchOZWNetworkStatus(this.hass!, this.ozwInstance); - this._statistics = await fetchOZWNetworkStatistics( - this.hass!, - this.ozwInstance - ); - if (networkOnlineStatuses.includes(this._network!.Status)) { - this._status = "online"; - this._icon = mdiCheckCircle; - } - if (networkStartingStatuses.includes(this._network!.Status)) { - this._status = "starting"; - } - if (networkOfflineStatuses.includes(this._network!.Status)) { - this._status = "offline"; - this._icon = mdiCloseCircle; - } - } - - private _generateServiceButton(service: string) { - const serviceData = { instance_id: this.ozwInstance }; - return html` - - ${this.hass!.localize(`ui.panel.config.ozw.services.${service}`)} - - `; - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .secondary { - color: var(--secondary-text-color); - } - .online { - color: green; - } - .starting { - color: orange; - } - .offline { - color: red; - } - .content { - margin-top: 24px; - } - - .sectionHeader { - position: relative; - padding-right: 40px; - } - - .network-status { - text-align: center; - } - - .network-status div.details { - font-size: 1.5rem; - margin-bottom: 16px; - } - - .network-status ha-svg-icon { - display: block; - margin: 0px auto 16px; - width: 48px; - height: 48px; - } - - .network-status small { - font-size: 1rem; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - - .card-actions.warning ha-call-service-button { - color: var(--error-color); - } - - .toggle-help-icon { - position: absolute; - top: -6px; - right: 0; - color: var(--primary-color); - } - - ha-service-description { - display: block; - color: grey; - padding: 0 8px 12px; - } - - [hidden] { - display: none; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-network-dashboard": OZWNetworkDashboard; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts deleted file mode 100644 index 85470588840b..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts +++ /dev/null @@ -1,131 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { mdiAlert, mdiCheck } from "@mdi/js"; -import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; -import { HASSDomEvent } from "../../../../../common/dom/fire_event"; -import { navigate } from "../../../../../common/navigate"; -import "../../../../../components/buttons/ha-call-service-button"; -import { - DataTableColumnContainer, - RowClickedEvent, -} from "../../../../../components/data-table/ha-data-table"; -import "../../../../../components/ha-card"; -import { fetchOZWNodes, OZWDevice } from "../../../../../data/ozw"; -import "../../../../../layouts/hass-tabs-subpage"; -import "../../../../../layouts/hass-tabs-subpage-data-table"; -import { haStyle } from "../../../../../resources/styles"; -import type { HomeAssistant, Route } from "../../../../../types"; -import "../../../ha-config-section"; -import { ozwNetworkTabs } from "./ozw-network-router"; - -export interface NodeRowData extends OZWDevice { - node?: NodeRowData; - id?: number; -} - -@customElement("ozw-network-nodes") -class OZWNetworkNodes extends LitElement { - @property({ type: Object }) public hass!: HomeAssistant; - - @property({ type: Object }) public route!: Route; - - @property({ type: Boolean }) public narrow!: boolean; - - @property({ type: Boolean }) public isWide!: boolean; - - @property() public configEntryId?: string; - - @property() public ozwInstance = 0; - - @state() private _nodes: OZWDevice[] = []; - - private _columns = memoizeOne( - (narrow: boolean): DataTableColumnContainer => ({ - node_id: { - title: this.hass.localize("ui.panel.config.ozw.nodes_table.id"), - sortable: true, - type: "numeric", - width: "72px", - filterable: true, - direction: "asc", - }, - node_product_name: { - title: this.hass.localize("ui.panel.config.ozw.nodes_table.model"), - sortable: true, - width: narrow ? "75%" : "25%", - }, - node_manufacturer_name: { - title: this.hass.localize( - "ui.panel.config.ozw.nodes_table.manufacturer" - ), - sortable: true, - hidden: narrow, - width: "25%", - }, - node_query_stage: { - title: this.hass.localize( - "ui.panel.config.ozw.nodes_table.query_stage" - ), - sortable: true, - width: narrow ? "25%" : "15%", - }, - is_zwave_plus: { - title: this.hass.localize("ui.panel.config.ozw.nodes_table.zwave_plus"), - hidden: narrow, - template: (value: boolean) => - value ? html` ` : "", - }, - is_failed: { - title: this.hass.localize("ui.panel.config.ozw.nodes_table.failed"), - hidden: narrow, - template: (value: boolean) => - value ? html` ` : "", - }, - }) - ); - - protected firstUpdated() { - if (!this.ozwInstance) { - navigate("/config/ozw/dashboard", { replace: true }); - } else if (this.hass) { - this._fetchData(); - } - } - - protected render(): TemplateResult { - return html` - - - `; - } - - private async _fetchData() { - this._nodes = await fetchOZWNodes(this.hass!, this.ozwInstance!); - } - - private _handleRowClicked(ev: HASSDomEvent) { - const nodeId = ev.detail.id; - navigate(`/config/ozw/network/${this.ozwInstance}/node/${nodeId}`); - } - - static get styles(): CSSResultGroup { - return haStyle; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-network-nodes": OZWNetworkNodes; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts deleted file mode 100644 index 0294ff9b0f22..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { mdiNetwork, mdiServerNetwork } from "@mdi/js"; -import { customElement, property } from "lit/decorators"; -import { - HassRouterPage, - RouterOptions, -} from "../../../../../layouts/hass-router-page"; -import { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; -import { HomeAssistant } from "../../../../../types"; -import { computeTail } from "./ozw-config-router"; - -export const ozwNetworkTabs = (instance: number): PageNavigation[] => [ - { - translationKey: "ui.panel.config.ozw.navigation.network", - path: `/config/ozw/network/${instance}/dashboard`, - iconPath: mdiServerNetwork, - }, - { - translationKey: "ui.panel.config.ozw.navigation.nodes", - path: `/config/ozw/network/${instance}/nodes`, - iconPath: mdiNetwork, - }, -]; - -@customElement("ozw-network-router") -class OZWNetworkRouter extends HassRouterPage { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public isWide!: boolean; - - @property() public narrow!: boolean; - - @property() public ozwInstance!: number; - - private _configEntry = new URLSearchParams(window.location.search).get( - "config_entry" - ); - - protected routerOptions: RouterOptions = { - defaultPage: "dashboard", - showLoading: true, - routes: { - dashboard: { - tag: "ozw-network-dashboard", - load: () => import("./ozw-network-dashboard"), - }, - nodes: { - tag: "ozw-network-nodes", - load: () => import("./ozw-network-nodes"), - }, - node: { - tag: "ozw-node-router", - load: () => import("./ozw-node-router"), - }, - }, - }; - - protected updatePageEl(el): void { - el.route = computeTail(this.routeTail); - el.hass = this.hass; - el.isWide = this.isWide; - el.narrow = this.narrow; - el.configEntryId = this._configEntry; - el.ozwInstance = this.ozwInstance; - if (this._currentPage === "node") { - el.nodeId = this.routeTail.path.split("/")[1]; - } - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-network-router": OZWNetworkRouter; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-node-config.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-node-config.ts deleted file mode 100644 index 7c29bf2cbc31..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-node-config.ts +++ /dev/null @@ -1,265 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { navigate } from "../../../../../common/navigate"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import { - fetchOZWNodeConfig, - fetchOZWNodeMetadata, - fetchOZWNodeStatus, - OZWDevice, - OZWDeviceConfig, - OZWDeviceMetaDataResponse, -} from "../../../../../data/ozw"; -import { ERR_NOT_FOUND } from "../../../../../data/websocket_api"; -import "../../../../../layouts/hass-tabs-subpage"; -import { haStyle } from "../../../../../resources/styles"; -import type { HomeAssistant, Route } from "../../../../../types"; -import "../../../ha-config-section"; -import { ozwNodeTabs } from "./ozw-node-router"; -import { showOZWRefreshNodeDialog } from "./show-dialog-ozw-refresh-node"; - -@customElement("ozw-node-config") -class OZWNodeConfig extends LitElement { - @property({ type: Object }) public hass!: HomeAssistant; - - @property({ type: Object }) public route!: Route; - - @property({ type: Boolean }) public narrow!: boolean; - - @property({ type: Boolean }) public isWide!: boolean; - - @property() public configEntryId?: string; - - @property() public ozwInstance?; - - @property() public nodeId?; - - @state() private _node?: OZWDevice; - - @state() private _metadata?: OZWDeviceMetaDataResponse; - - @state() private _config?: OZWDeviceConfig[]; - - @state() private _error?: string; - - protected firstUpdated() { - if (!this.ozwInstance) { - navigate("/config/ozw/dashboard", { replace: true }); - } else if (!this.nodeId) { - navigate(`/config/ozw/network/${this.ozwInstance}/nodes`, { - replace: true, - }); - } else { - this._fetchData(); - } - } - - protected render(): TemplateResult { - if (this._error) { - return html` - - `; - } - - return html` - - -
- ${this.hass.localize("ui.panel.config.ozw.node_config.header")} -
- -
- ${this.hass.localize( - "ui.panel.config.ozw.node_config.introduction" - )} -

- - ${this.hass.localize( - "ui.panel.config.ozw.node_config.help_source" - )} - -

-

- Note: This panel is currently read-only. The ability to change - values will come in a later update. -

-
- ${this._node - ? html` - -
- - ${this._node.node_manufacturer_name} - ${this._node.node_product_name}
- ${this.hass.localize("ui.panel.config.ozw.common.node_id")}: - ${this._node.node_id}
- ${this.hass.localize( - "ui.panel.config.ozw.common.query_stage" - )}: - ${this._node.node_query_stage} - ${this._metadata?.metadata.ProductManualURL - ? html` -

- ${this.hass.localize( - "ui.panel.config.ozw.node_metadata.product_manual" - )} -

-
` - : ``} -
-
- - ${this.hass.localize( - "ui.panel.config.ozw.refresh_node.button" - )} - -
-
- - ${this._metadata?.metadata.WakeupHelp - ? html` - -
- - ${this.hass.localize( - "ui.panel.config.ozw.node_config.wakeup_help" - )} - -

${this._metadata.metadata.WakeupHelp}

-
-
- ` - : ``} - ${this._config - ? html` - ${this._config.map( - (item) => html` - -
- ${item.label}
- ${item.help} -

${item.value}

-
-
- ` - )} - ` - : ``} - ` - : ``} -
-
- `; - } - - private async _fetchData() { - if (!this.ozwInstance || !this.nodeId) { - return; - } - - try { - const nodeProm = fetchOZWNodeStatus( - this.hass!, - this.ozwInstance, - this.nodeId - ); - const metadataProm = fetchOZWNodeMetadata( - this.hass!, - this.ozwInstance, - this.nodeId - ); - const configProm = fetchOZWNodeConfig( - this.hass!, - this.ozwInstance, - this.nodeId - ); - [this._node, this._metadata, this._config] = await Promise.all([ - nodeProm, - metadataProm, - configProm, - ]); - } catch (err: any) { - if (err.code === ERR_NOT_FOUND) { - this._error = ERR_NOT_FOUND; - return; - } - throw err; - } - } - - private async _refreshNodeClicked() { - showOZWRefreshNodeDialog(this, { - node_id: this.nodeId, - ozw_instance: this.ozwInstance, - }); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .secondary { - color: var(--secondary-text-color); - font-size: 0.9em; - } - - .content { - margin-top: 24px; - } - - .sectionHeader { - position: relative; - padding-right: 40px; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - - [hidden] { - display: none; - } - - blockquote { - display: block; - background-color: #ddd; - padding: 8px; - margin: 8px 0; - font-size: 0.9em; - } - - blockquote em { - font-size: 0.9em; - margin-top: 6px; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-node-config": OZWNodeConfig; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts deleted file mode 100644 index ec45071c16cb..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts +++ /dev/null @@ -1,254 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { navigate } from "../../../../../common/navigate"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import { - fetchOZWNodeMetadata, - fetchOZWNodeStatus, - OZWDevice, - OZWDeviceMetaDataResponse, -} from "../../../../../data/ozw"; -import { ERR_NOT_FOUND } from "../../../../../data/websocket_api"; -import "../../../../../layouts/hass-tabs-subpage"; -import { haStyle } from "../../../../../resources/styles"; -import type { HomeAssistant, Route } from "../../../../../types"; -import "../../../ha-config-section"; -import { ozwNodeTabs } from "./ozw-node-router"; -import { showOZWRefreshNodeDialog } from "./show-dialog-ozw-refresh-node"; - -@customElement("ozw-node-dashboard") -class OZWNodeDashboard extends LitElement { - @property({ type: Object }) public hass!: HomeAssistant; - - @property({ type: Object }) public route!: Route; - - @property({ type: Boolean }) public narrow!: boolean; - - @property({ type: Boolean }) public isWide!: boolean; - - @property() public configEntryId?: string; - - @property() public ozwInstance?; - - @property() public nodeId?; - - @state() private _node?: OZWDevice; - - @state() private _metadata?: OZWDeviceMetaDataResponse; - - @state() private _not_found = false; - - protected firstUpdated() { - if (!this.ozwInstance) { - navigate("/config/ozw/dashboard", { replace: true }); - } else if (!this.nodeId) { - navigate(`/config/ozw/network/${this.ozwInstance}/nodes`, { - replace: true, - }); - } else if (this.hass) { - this._fetchData(); - } - } - - protected render(): TemplateResult { - if (this._not_found) { - return html` - - `; - } - - return html` - - -
Node Management
- -
- View the status of a node and manage its configuration. -
- ${this._node - ? html` - -
-
- - ${this._node.node_manufacturer_name} - ${this._node.node_product_name} - -
- Node ID: ${this._node.node_id}
- Query Stage: ${this._node.node_query_stage} - ${this._metadata?.metadata.ProductManualURL - ? html` -

Product Manual

-
` - : ``} -
- ${this._metadata?.metadata.ProductPicBase64 - ? html`` - : ``} -
-
- - Refresh Node - -
-
- - ${this._metadata - ? html` - -
- ${this._metadata.metadata.Description} -
-
- -
- ${this._metadata.metadata.InclusionHelp} -
-
- -
- ${this._metadata.metadata.ExclusionHelp} -
-
- -
- ${this._metadata.metadata.ResetHelp} -
-
- ${this._metadata.metadata.WakeupHelp - ? html` - -
- ${this._metadata.metadata.WakeupHelp} -
-
- ` - : ``} - ` - : ``} - ` - : ``} -
-
- `; - } - - private async _fetchData() { - if (!this.ozwInstance || !this.nodeId) { - return; - } - - try { - this._node = await fetchOZWNodeStatus( - this.hass!, - this.ozwInstance, - this.nodeId - ); - this._metadata = await fetchOZWNodeMetadata( - this.hass!, - this.ozwInstance, - this.nodeId - ); - } catch (err: any) { - if (err.code === ERR_NOT_FOUND) { - this._not_found = true; - return; - } - throw err; - } - } - - private async _refreshNodeClicked() { - showOZWRefreshNodeDialog(this, { - node_id: this.nodeId, - ozw_instance: this.ozwInstance, - }); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .secondary { - color: var(--secondary-text-color); - } - - .content { - margin-top: 24px; - } - - .content:last-child { - margin-bottom: 24px; - } - - .sectionHeader { - position: relative; - padding-right: 40px; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - - .flex { - display: flex; - justify-content: space-between; - } - - .card-actions.warning ha-call-service-button { - color: var(--error-color); - } - - .toggle-help-icon { - position: absolute; - top: -6px; - right: 0; - color: var(--primary-color); - } - - ha-service-description { - display: block; - color: grey; - padding: 0 8px 12px; - } - - [hidden] { - display: none; - } - - .product-image { - padding: 12px; - max-height: 140px; - max-width: 140px; - } - .card-actions { - clear: right; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-node-dashboard": OZWNodeDashboard; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts deleted file mode 100644 index f5676bdcaa87..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { mdiNetwork, mdiWrench } from "@mdi/js"; -import { customElement, property } from "lit/decorators"; -import { navigate } from "../../../../../common/navigate"; -import { - HassRouterPage, - RouterOptions, -} from "../../../../../layouts/hass-router-page"; -import { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; -import { HomeAssistant } from "../../../../../types"; - -export const ozwNodeTabs = ( - instance: number, - node: number -): PageNavigation[] => [ - { - translationKey: "ui.panel.config.ozw.navigation.node.dashboard", - path: `/config/ozw/network/${instance}/node/${node}/dashboard`, - iconPath: mdiNetwork, - }, - { - translationKey: "ui.panel.config.ozw.navigation.node.config", - path: `/config/ozw/network/${instance}/node/${node}/config`, - iconPath: mdiWrench, - }, -]; - -@customElement("ozw-node-router") -class OZWNodeRouter extends HassRouterPage { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public isWide!: boolean; - - @property() public narrow!: boolean; - - @property() public ozwInstance!: number; - - @property() public nodeId!: number; - - private _configEntry = new URLSearchParams(window.location.search).get( - "config_entry" - ); - - protected routerOptions: RouterOptions = { - defaultPage: "dashboard", - showLoading: true, - routes: { - dashboard: { - tag: "ozw-node-dashboard", - load: () => import("./ozw-node-dashboard"), - }, - config: { - tag: "ozw-node-config", - load: () => import("./ozw-node-config"), - }, - }, - }; - - protected updatePageEl(el): void { - el.route = this.routeTail; - el.hass = this.hass; - el.isWide = this.isWide; - el.narrow = this.narrow; - el.configEntryId = this._configEntry; - el.ozwInstance = this.ozwInstance; - el.nodeId = this.nodeId; - - const searchParams = new URLSearchParams(window.location.search); - if (this._configEntry && !searchParams.has("config_entry")) { - searchParams.append("config_entry", this._configEntry); - navigate( - `${this.routeTail.prefix}${ - this.routeTail.path - }?${searchParams.toString()}`, - { replace: true } - ); - } - } -} - -declare global { - interface HTMLElementTagNameMap { - "ozw-node-router": OZWNodeRouter; - } -} diff --git a/src/panels/config/integrations/integration-panels/ozw/show-dialog-ozw-refresh-node.ts b/src/panels/config/integrations/integration-panels/ozw/show-dialog-ozw-refresh-node.ts deleted file mode 100644 index e3ecd75e47c1..000000000000 --- a/src/panels/config/integrations/integration-panels/ozw/show-dialog-ozw-refresh-node.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { fireEvent } from "../../../../../common/dom/fire_event"; - -export interface OZWRefreshNodeDialogParams { - ozw_instance: number; - node_id: number; -} - -export const loadRefreshNodeDialog = () => import("./dialog-ozw-refresh-node"); - -export const showOZWRefreshNodeDialog = ( - element: HTMLElement, - refreshNodeDialogParams: OZWRefreshNodeDialogParams -): void => { - fireEvent(element, "show-dialog", { - dialogTag: "dialog-ozw-refresh-node", - dialogImport: loadRefreshNodeDialog, - dialogParams: refreshNodeDialogParams, - }); -}; diff --git a/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js b/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js deleted file mode 100644 index 58caa941af6e..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/ha-config-zwave.js +++ /dev/null @@ -1,765 +0,0 @@ -import "@polymer/app-layout/app-header/app-header"; -import "@polymer/app-layout/app-toolbar/app-toolbar"; -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-input/paper-input"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; -import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { computeStateDomain } from "../../../../../common/entity/compute_state_domain"; -import { computeStateName } from "../../../../../common/entity/compute_state_name"; -import { sortStatesByName } from "../../../../../common/entity/states_sort_by_name"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import "../../../../../components/ha-alert"; -import "../../../../../components/ha-icon"; -import "../../../../../components/ha-icon-button"; -import "../../../../../components/ha-icon-button-arrow-prev"; -import "../../../../../components/ha-menu-button"; -import "../../../../../components/ha-service-description"; -import "../../../../../layouts/ha-app-layout"; -import { EventsMixin } from "../../../../../mixins/events-mixin"; -import LocalizeMixin from "../../../../../mixins/localize-mixin"; -import "../../../../../styles/polymer-ha-style"; -import "../../../ha-config-section"; -import "../../../ha-form-style"; -import "./zwave-groups"; -import "./zwave-log"; -import "./zwave-network"; -import "./zwave-node-config"; -import "./zwave-node-protection"; -import "./zwave-usercodes"; -import "./zwave-values"; - -/* - * @appliesMixin LocalizeMixin - * @appliesMixin EventsMixin - */ -class HaConfigZwave extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - - - - -
[[localize('component.zwave.title')]]
-
-
- - - This Z-Wave integration is deprecated and will no longer receive any - updates. The technical dependencies will render this integration - unusable in the near future. We strongly advise you to migrate to the - new - Z-Wave JS integration. - - learn more - - - - - -
- [[localize('ui.panel.config.zwave.migration.zwave_js.introduction')]] -
- -
-
- - - - - -
- [[localize('ui.panel.config.zwave.node_management.header')]] - - - -
- - [[localize('ui.panel.config.zwave.node_management.introduction')]] - - - -
- - - - - -
- - - -
- - - - - - - - -
- - - -
- `; - } - - static get properties() { - return { - hass: Object, - - isWide: Boolean, - - nodes: { - type: Array, - computed: "computeNodes(hass)", - }, - - selectedNode: { - type: Number, - value: -1, - observer: "selectedNodeChanged", - }, - - nodeFailed: { - type: Boolean, - value: false, - }, - - config: { - type: Array, - value: () => [], - }, - - entities: { - type: Array, - computed: "computeEntities(selectedNode)", - }, - - selectedEntity: { - type: Number, - value: -1, - observer: "selectedEntityChanged", - }, - - values: { - type: Array, - }, - - groups: { - type: Array, - }, - - userCodes: { - type: Array, - value: () => [], - }, - - hasNodeUserCodes: { - type: Boolean, - value: false, - }, - - showHelp: { - type: Boolean, - value: false, - }, - - entityIgnored: Boolean, - - entityPollingIntensity: { - type: Number, - value: 0, - }, - - _protection: { - type: Array, - value: () => [], - }, - - _protectionNode: { - type: Boolean, - value: false, - }, - }; - } - - ready() { - super.ready(); - import("web-animations-js/web-animations-next-lite.min"); - this.addEventListener("hass-service-called", (ev) => - this.serviceCalled(ev) - ); - } - - attached() { - setCancelSyntheticClickEvents(true); - } - - detached() { - setCancelSyntheticClickEvents(false); - } - - serviceCalled(ev) { - if (ev.detail.success && ev.detail.service === "set_poll_intensity") { - this._saveEntity(); - } - } - - computeNodes(hass) { - return Object.keys(hass.states) - .map((key) => hass.states[key]) - .filter((ent) => ent.entity_id.match("zwave[.]")) - .sort(sortStatesByName); - } - - computeEntities(selectedNode) { - if (!this.nodes || selectedNode === -1) { - return -1; - } - const nodeid = this.nodes[this.selectedNode].attributes.node_id; - const hass = this.hass; - return Object.keys(this.hass.states) - .map((key) => hass.states[key]) - .filter((ent) => { - if (ent.attributes.node_id === undefined) { - return false; - } - return ( - "node_id" in ent.attributes && - ent.attributes.node_id === nodeid && - !ent.entity_id.match("zwave[.]") - ); - }) - .sort(sortStatesByName); - } - - selectedNodeChanged(selectedNode) { - if (selectedNode === -1) { - return; - } - this.selectedEntity = -1; - - this.hass - .callApi( - "GET", - `zwave/config/${this.nodes[selectedNode].attributes.node_id}` - ) - .then((configs) => { - this.config = this._objToArray(configs); - }); - - this.hass - .callApi( - "GET", - `zwave/values/${this.nodes[selectedNode].attributes.node_id}` - ) - .then((values) => { - this.values = this._objToArray(values); - }); - - this.hass - .callApi( - "GET", - `zwave/groups/${this.nodes[selectedNode].attributes.node_id}` - ) - .then((groups) => { - this.groups = this._objToArray(groups); - }); - - this.hasNodeUserCodes = false; - this.notifyPath("hasNodeUserCodes"); - this.hass - .callApi( - "GET", - `zwave/usercodes/${this.nodes[selectedNode].attributes.node_id}` - ) - .then((usercodes) => { - this.userCodes = this._objToArray(usercodes); - this.hasNodeUserCodes = this.userCodes.length > 0; - this.notifyPath("hasNodeUserCodes"); - }); - this.hass - .callApi( - "GET", - `zwave/protection/${this.nodes[selectedNode].attributes.node_id}` - ) - .then((protections) => { - this._protection = this._objToArray(protections); - if (this._protection) { - if (this._protection.length === 0) { - return; - } - this._protectionNode = true; - } - }); - - this.nodeFailed = this.nodes[selectedNode].attributes.is_failed; - } - - selectedEntityChanged(selectedEntity) { - if (selectedEntity === -1) { - return; - } - this.hass - .callApi( - "GET", - `zwave/values/${this.nodes[this.selectedNode].attributes.node_id}` - ) - .then((values) => { - this.values = this._objToArray(values); - }); - - const valueId = this.entities[selectedEntity].attributes.value_id; - const valueData = this.values.find((obj) => obj.key === valueId); - const valueIndex = this.values.indexOf(valueData); - this.hass - .callApi( - "GET", - `config/zwave/device_config/${this.entities[selectedEntity].entity_id}` - ) - .then((data) => { - this.setProperties({ - entityIgnored: data.ignored || false, - entityPollingIntensity: this.values[valueIndex].value.poll_intensity, - }); - }) - .catch(() => { - this.setProperties({ - entityIgnored: false, - entityPollingIntensity: this.values[valueIndex].value.poll_intensity, - }); - }); - } - - computeSelectCaption(stateObj) { - return ( - computeStateName(stateObj) + - " (Node:" + - stateObj.attributes.node_id + - " " + - stateObj.attributes.query_stage + - ")" - ); - } - - computeSelectCaptionEnt(stateObj) { - return computeStateDomain(stateObj) + "." + computeStateName(stateObj); - } - - computeIsNodeSelected() { - return this.nodes && this.selectedNode !== -1; - } - - computeIsEntitySelected(selectedEntity) { - return selectedEntity === -1; - } - - computeNodeServiceData(selectedNode) { - return { node_id: this.nodes[selectedNode].attributes.node_id }; - } - - computeHealNodeServiceData(selectedNode) { - return { - node_id: this.nodes[selectedNode].attributes.node_id, - return_routes: true, - }; - } - - computeRefreshEntityServiceData(selectedEntity) { - if (selectedEntity === -1) { - return -1; - } - return { entity_id: this.entities[selectedEntity].entity_id }; - } - - computePollIntensityServiceData(entityPollingIntensity) { - if (this.selectedNode === -1 || this.selectedEntity === -1) { - return -1; - } - return { - node_id: this.nodes[this.selectedNode].attributes.node_id, - value_id: this.entities[this.selectedEntity].attributes.value_id, - poll_intensity: parseInt(entityPollingIntensity), - }; - } - - _nodeMoreInfo() { - this.fire("hass-more-info", { - entityId: this.nodes[this.selectedNode].entity_id, - }); - } - - _entityMoreInfo() { - this.fire("hass-more-info", { - entityId: this.entities[this.selectedEntity].entity_id, - }); - } - - _saveEntity() { - const data = { - ignored: this.entityIgnored, - polling_intensity: parseInt(this.entityPollingIntensity), - }; - return this.hass.callApi( - "POST", - `config/zwave/device_config/${ - this.entities[this.selectedEntity].entity_id - }`, - data - ); - } - - toggleHelp() { - this.showHelp = !this.showHelp; - } - - _objToArray(obj) { - const array = []; - Object.keys(obj).forEach((key) => { - array.push({ - key, - value: obj[key], - }); - }); - return array; - } - - _backTapped() { - history.back(); - } - - entityIgnoredChanged(ev) { - this.entityIgnored = ev.target.checked; - } -} - -customElements.define("ha-config-zwave", HaConfigZwave); diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-config-router.ts b/src/panels/config/integrations/integration-panels/zwave/zwave-config-router.ts deleted file mode 100644 index 4ebb4b99701f..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-config-router.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { customElement, property } from "lit/decorators"; -import { navigate } from "../../../../../common/navigate"; -import { - HassRouterPage, - RouterOptions, -} from "../../../../../layouts/hass-router-page"; -import { HomeAssistant } from "../../../../../types"; - -@customElement("zwave-config-router") -class ZWaveConfigRouter extends HassRouterPage { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public isWide!: boolean; - - @property() public narrow!: boolean; - - private _configEntry = new URLSearchParams(window.location.search).get( - "config_entry" - ); - - protected routerOptions: RouterOptions = { - defaultPage: "dashboard", - showLoading: true, - routes: { - dashboard: { - tag: "ha-config-zwave", - load: () => - import(/* webpackChunkName: "ha-config-zwave" */ "./ha-config-zwave"), - }, - migration: { - tag: "zwave-migration", - load: () => - import(/* webpackChunkName: "zwave-migration" */ "./zwave-migration"), - }, - }, - }; - - protected updatePageEl(el): void { - el.route = this.routeTail; - el.hass = this.hass; - el.isWide = this.isWide; - el.narrow = this.narrow; - el.configEntryId = this._configEntry; - - const searchParams = new URLSearchParams(window.location.search); - if (this._configEntry && !searchParams.has("config_entry")) { - searchParams.append("config_entry", this._configEntry); - navigate( - `${this.routeTail.prefix}${ - this.routeTail.path - }?${searchParams.toString()}`, - { replace: true } - ); - } - } -} - -declare global { - interface HTMLElementTagNameMap { - "zwave-config-router": ZWaveConfigRouter; - } -} diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-groups.js b/src/panels/config/integrations/integration-panels/zwave/zwave-groups.js deleted file mode 100644 index 129587fcfb1c..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-groups.js +++ /dev/null @@ -1,380 +0,0 @@ -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { computeStateName } from "../../../../../common/entity/compute_state_name"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import LocalizeMixin from "../../../../../mixins/localize-mixin"; -import "../../../../../styles/polymer-ha-style"; - -class ZwaveGroups extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - -
- - - - - -
- - - -
- `; - } - - static get properties() { - return { - hass: Object, - - nodes: Array, - - groups: Array, - - selectedNode: { - type: Number, - observer: "_selectedNodeChanged", - }, - - _selectedTargetNode: { - type: Number, - value: -1, - observer: "_selectedTargetNodeChanged", - }, - - _selectedGroup: { - type: Number, - value: -1, - }, - - _otherGroupNodes: { - type: Array, - value: -1, - computed: "_computeOtherGroupNodes(_selectedGroup)", - }, - - _maxAssociations: { - type: String, - value: "", - computed: "_computeMaxAssociations(_selectedGroup)", - }, - - _noAssociationsLeft: { - type: Boolean, - value: true, - computed: "_computeAssociationsLeft(_selectedGroup)", - }, - - _addAssocServiceData: { - type: String, - value: "", - }, - - _removeAssocServiceData: { - type: String, - value: "", - }, - - _removeBroadcastNodeServiceData: { - type: String, - value: "", - }, - - _isBroadcastNodeInGroup: { - type: Boolean, - value: false, - }, - }; - } - - static get observers() { - return ["_selectedGroupChanged(groups, _selectedGroup)"]; - } - - ready() { - super.ready(); - this.addEventListener("hass-service-called", (ev) => - this.serviceCalled(ev) - ); - } - - serviceCalled(ev) { - if (ev.detail.success) { - setTimeout(() => { - this._refreshGroups(this.selectedNode); - }, 5000); - } - } - - _computeAssociationsLeft(selectedGroup) { - if (selectedGroup === -1) return true; - return this._maxAssociations === this._otherGroupNodes.length; - } - - _computeMaxAssociations(selectedGroup) { - if (selectedGroup === -1) return -1; - const maxAssociations = this.groups[selectedGroup].value.max_associations; - if (!maxAssociations) return "None"; - return maxAssociations; - } - - _computeOtherGroupNodes(selectedGroup) { - if (selectedGroup === -1) return -1; - this.setProperties({ _isBroadcastNodeInGroup: false }); - const associations = Object.values( - this.groups[selectedGroup].value.association_instances - ); - if (!associations.length) return ["None"]; - return associations.map((assoc) => { - if (!assoc.length || assoc.length !== 2) { - return `Unknown Node: ${assoc}`; - } - const id = assoc[0]; - const instance = assoc[1]; - const node = this.nodes.find((n) => n.attributes.node_id === id); - if (id === 255) { - this.setProperties({ - _isBroadcastNodeInGroup: true, - _removeBroadcastNodeServiceData: { - node_id: this.nodes[this.selectedNode].attributes.node_id, - association: "remove", - target_node_id: 255, - group: this.groups[selectedGroup].key, - }, - }); - } - if (!node) { - return `Unknown Node (${id}: (${instance} ? ${id}.${instance} : ${id}))`; - } - let caption = this._computeSelectCaption(node); - if (instance) { - caption += `/ Instance: ${instance}`; - } - return caption; - }); - } - - _computeTargetInGroup(selectedGroup, selectedTargetNode) { - if (selectedGroup === -1 || selectedTargetNode === -1) return false; - const associations = Object.values( - this.groups[selectedGroup].value.associations - ); - if (!associations.length) return false; - return ( - associations.indexOf( - this.nodes[selectedTargetNode].attributes.node_id - ) !== -1 - ); - } - - _computeSelectCaption(stateObj) { - return `${computeStateName(stateObj)} - (Node: ${stateObj.attributes.node_id} - ${stateObj.attributes.query_stage})`; - } - - _computeSelectCaptionGroup(stateObj) { - return `${stateObj.key}: ${stateObj.value.label}`; - } - - _computeIsTargetNodeSelected(selectedTargetNode) { - return this.nodes && selectedTargetNode !== -1; - } - - _computeIsGroupSelected(selectedGroup) { - return this.nodes && this.selectedNode !== -1 && selectedGroup !== -1; - } - - _computeAssocServiceData(selectedGroup, type) { - if ( - !this.groups || - selectedGroup === -1 || - this.selectedNode === -1 || - this._selectedTargetNode === -1 - ) { - return -1; - } - return { - node_id: this.nodes[this.selectedNode].attributes.node_id, - association: type, - target_node_id: this.nodes[this._selectedTargetNode].attributes.node_id, - group: this.groups[selectedGroup].key, - }; - } - - async _refreshGroups(selectedNode) { - const groupData = []; - const groups = await this.hass.callApi( - "GET", - `zwave/groups/${this.nodes[selectedNode].attributes.node_id}` - ); - Object.keys(groups).forEach((key) => { - groupData.push({ - key, - value: groups[key], - }); - }); - this.setProperties({ - groups: groupData, - _maxAssociations: groupData[this._selectedGroup].value.max_associations, - _otherGroupNodes: Object.values( - groupData[this._selectedGroup].value.associations - ), - _isBroadcastNodeInGroup: false, - }); - const oldGroup = this._selectedGroup; - this.setProperties({ _selectedGroup: -1 }); - this.setProperties({ _selectedGroup: oldGroup }); - } - - _selectedGroupChanged() { - if (this._selectedGroup === -1) return; - this.setProperties({ - _maxAssociations: this.groups[this._selectedGroup].value.max_associations, - _otherGroupNodes: Object.values( - this.groups[this._selectedGroup].value.associations - ), - }); - } - - _selectedTargetNodeChanged() { - if (this._selectedGroup === -1) return; - if ( - this._computeTargetInGroup(this._selectedGroup, this._selectedTargetNode) - ) { - this.setProperties({ - _removeAssocServiceData: this._computeAssocServiceData( - this._selectedGroup, - "remove" - ), - }); - } else { - this.setProperties({ - _addAssocServiceData: this._computeAssocServiceData( - this._selectedGroup, - "add" - ), - }); - } - } - - _selectedNodeChanged() { - if (this.selectedNode === -1) return; - this.setProperties({ _selectedTargetNode: -1, _selectedGroup: -1 }); - } -} - -customElements.define("zwave-groups", ZwaveGroups); diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-log-dialog.js b/src/panels/config/integrations/integration-panels/zwave/zwave-log-dialog.js deleted file mode 100644 index df5acc95d12d..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-log-dialog.js +++ /dev/null @@ -1,83 +0,0 @@ -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { EventsMixin } from "../../../../../mixins/events-mixin"; -import "../../../../../styles/polymer-ha-style-dialog"; -import "../../../../../components/ha-dialog"; - -class ZwaveLogDialog extends EventsMixin(PolymerElement) { - static get template() { - return html` - - -
-
[[_ozwLog]]
-
- - `; - } - - static get properties() { - return { - hass: Object, - _ozwLog: String, - - _dialogClosedCallback: Function, - - _opened: { - type: Boolean, - value: false, - }, - - _intervalId: String, - - _numLogLines: { - type: Number, - }, - }; - } - - ready() { - super.ready(); - this.addEventListener("iron-overlay-closed", (ev) => - this._dialogClosed(ev) - ); - } - - showDialog({ _ozwLog, hass, _tail, _numLogLines, dialogClosedCallback }) { - this.hass = hass; - this._ozwLog = _ozwLog; - this._opened = true; - this._dialogClosedCallback = dialogClosedCallback; - this._numLogLines = _numLogLines; - if (_tail) { - this.setProperties({ - _intervalId: setInterval(() => { - this._refreshLog(); - }, 1500), - }); - } - } - - closeDialog() { - clearInterval(this._intervalId); - this._opened = false; - const closedEvent = true; - this._dialogClosedCallback({ closedEvent }); - this._dialogClosedCallback = null; - } - - async _refreshLog() { - const info = await this.hass.callApi( - "GET", - "zwave/ozwlog?lines=" + this._numLogLines - ); - this.setProperties({ _ozwLog: info }); - } -} - -customElements.define("zwave-log-dialog", ZwaveLogDialog); diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-log.js b/src/panels/config/integrations/integration-panels/zwave/zwave-log.js deleted file mode 100755 index c55e153f00a4..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-log.js +++ /dev/null @@ -1,160 +0,0 @@ -import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import isPwa from "../../../../../common/config/is_pwa"; -import "../../../../../components/ha-card"; -import { EventsMixin } from "../../../../../mixins/events-mixin"; -import LocalizeMixin from "../../../../../mixins/localize-mixin"; -import "../../../../../styles/polymer-ha-style"; -import "../../../ha-config-section"; - -let registeredDialog = false; - -class OzwLog extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - - - [[localize('ui.panel.config.zwave.ozw_log.header')]] - - - [[localize('ui.panel.config.zwave.ozw_log.introduction')]] - - -
- - -
-
- [[localize('ui.panel.config.zwave.ozw_log.load')]] - [[localize('ui.panel.config.zwave.ozw_log.tail')]] - - -`; - } - - static get properties() { - return { - hass: Object, - - isWide: { - type: Boolean, - value: false, - }, - - _ozwLogs: String, - - _completeLog: { - type: Boolean, - value: true, - }, - - numLogLines: { - type: Number, - value: 0, - observer: "_isCompleteLog", - }, - - _intervalId: String, - - tail: Boolean, - }; - } - - async _tailLog() { - this.setProperties({ tail: true }); - const ozwWindow = await this._openLogWindow(); - if (!isPwa()) { - this.setProperties({ - _intervalId: setInterval(() => { - this._refreshLog(ozwWindow); - }, 1500), - }); - } - } - - async _openLogWindow() { - const info = await this.hass.callApi( - "GET", - "zwave/ozwlog?lines=" + this.numLogLines - ); - this.setProperties({ _ozwLogs: info }); - if (isPwa()) { - this._showOzwlogDialog(); - return -1; - } - const ozwWindow = open("", "ozwLog", "toolbar"); - ozwWindow.document.body.innerHTML = `
${this._ozwLogs}
`; - return ozwWindow; - } - - async _refreshLog(ozwWindow) { - if (ozwWindow.closed === true) { - clearInterval(this._intervalId); - this.setProperties({ _intervalId: null }); - } else { - const info = await this.hass.callApi( - "GET", - "zwave/ozwlog?lines=" + this.numLogLines - ); - this.setProperties({ _ozwLogs: info }); - ozwWindow.document.body.innerHTML = `
${this._ozwLogs}
`; - } - } - - _isCompleteLog() { - if (this.numLogLines !== "0") { - this.setProperties({ _completeLog: false }); - } else { - this.setProperties({ _completeLog: true }); - } - } - - connectedCallback() { - super.connectedCallback(); - if (!registeredDialog) { - registeredDialog = true; - this.fire("register-dialog", { - dialogShowEvent: "show-ozwlog-dialog", - dialogTag: "zwave-log-dialog", - dialogImport: () => import("./zwave-log-dialog"), - }); - } - } - - _showOzwlogDialog() { - this.fire("show-ozwlog-dialog", { - hass: this.hass, - _numLogLines: this.numLogLines, - _ozwLog: this._ozwLogs, - _tail: this.tail, - dialogClosedCallback: () => this._dialogClosed(), - }); - } - - _dialogClosed() { - this.setProperties({ - tail: false, - }); - } -} -customElements.define("ozw-log", OzwLog); diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-migration.ts b/src/panels/config/integrations/integration-panels/zwave/zwave-migration.ts deleted file mode 100644 index 2272bd884e94..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-migration.ts +++ /dev/null @@ -1,573 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import "@polymer/app-layout/app-header/app-header"; -import "@polymer/app-layout/app-toolbar/app-toolbar"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { isComponentLoaded } from "../../../../../common/config/is_component_loaded"; -import { computeStateDomain } from "../../../../../common/entity/compute_state_domain"; -import { computeStateName } from "../../../../../common/entity/compute_state_name"; -import "../../../../../components/buttons/ha-call-api-button"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-alert"; -import "../../../../../components/ha-card"; -import "../../../../../components/ha-circular-progress"; -import "../../../../../components/ha-icon"; -import "../../../../../components/ha-icon-button"; -import { - computeDeviceName, - DeviceRegistryEntry, - fetchDeviceRegistry, - subscribeDeviceRegistry, -} from "../../../../../data/device_registry"; -import { - fetchMigrationConfig, - fetchNetworkStatus, - startZwaveJsConfigFlow, - ZWaveMigrationConfig, - ZWaveNetworkStatus, - ZWAVE_NETWORK_STATE_STOPPED, -} from "../../../../../data/zwave"; -import { - fetchZwaveNetworkStatus as fetchZwaveJsNetworkStatus, - fetchZwaveNodeStatus, - getZwaveJsIdentifiersFromDevice, - migrateZwave, - subscribeZwaveNodeReady, - ZWaveJsMigrationData, -} from "../../../../../data/zwave_js"; -import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow"; -import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box"; -import "../../../../../layouts/hass-subpage"; -import { haStyle } from "../../../../../resources/styles"; -import type { HomeAssistant, Route } from "../../../../../types"; -import "../../../ha-config-section"; - -@customElement("zwave-migration") -export class ZwaveMigration extends LitElement { - @property({ type: Object }) public hass!: HomeAssistant; - - @property({ type: Object }) public route!: Route; - - @property({ type: Boolean }) public narrow!: boolean; - - @property({ type: Boolean }) public isWide!: boolean; - - @state() private _networkStatus?: ZWaveNetworkStatus; - - @state() private _step = 0; - - @state() private _stoppingNetwork = false; - - @state() private _migrationConfig?: ZWaveMigrationConfig; - - @state() private _migrationData?: ZWaveJsMigrationData; - - @state() private _migratedZwaveEntities?: string[]; - - @state() private _deviceNameLookup: { [id: string]: string } = {}; - - @state() private _waitingOnDevices?: DeviceRegistryEntry[]; - - private _zwaveJsEntryId?: string; - - private _nodeReadySubscriptions?: Promise[]; - - private _unsub?: Promise; - - private _unsubDevices?: UnsubscribeFunc; - - public disconnectedCallback(): void { - this._unsubscribe(); - if (this._unsubDevices) { - this._unsubDevices(); - this._unsubDevices = undefined; - } - } - - protected render(): TemplateResult { - return html` - - -
- ${this.hass.localize( - "ui.panel.config.zwave.migration.zwave_js.header" - )} -
- -
- ${this.hass.localize( - "ui.panel.config.zwave.migration.zwave_js.introduction" - )} -
- ${html` - ${this._step === 0 - ? html` - -
-

- This wizard will walk through the following steps to - migrate from the legacy Z-Wave integration to Z-Wave JS. -

-
    -
  1. Stop the Z-Wave network
  2. - ${!isComponentLoaded(this.hass, "hassio") - ? html`
  3. Configure and start Z-Wave JS
  4. ` - : ""} -
  5. Set up the Z-Wave JS integration
  6. -
  7. - Migrate entities and devices to the new integration -
  8. -
  9. Remove legacy Z-Wave integration
  10. -
-

- - ${isComponentLoaded(this.hass, "hassio") - ? html`Please - make a backup - before proceeding.` - : "Please make a backup of your installation before proceeding."} - -

-
-
- - Continue - -
-
- ` - : this._step === 1 - ? html` - -
-

- We need to stop the Z-Wave network to perform the - migration. Home Assistant will not be able to control - Z-Wave devices while the network is stopped. -

- ${Object.values(this.hass.states) - .filter( - (entityState) => - computeStateDomain(entityState) === "zwave" && - !["ready", "sleeping"].includes(entityState.state) - ) - .map( - (entityState) => - html` - Device ${computeStateName(entityState)} - (${entityState.entity_id}) is not ready yet! For - the best result, wake the device up if it is - battery powered and wait for this device to become - ready. - ` - )} - ${this._stoppingNetwork - ? html` -
- -

Stopping Z-Wave Network...

-
- ` - : ``} -
-
- - Stop Network - -
-
- ` - : this._step === 2 - ? html` - -
-

Now it's time to set up the Z-Wave JS integration.

- ${isComponentLoaded(this.hass, "hassio") - ? html` -

- Z-Wave JS runs as a Home Assistant add-on that - will be setup next. Make sure to check the - checkbox to use the add-on. -

- ` - : html` -

- You are not running Home Assistant OS (the default - installation type) or Home Assistant Supervised, - so we can not setup Z-Wave JS automaticaly. Follow - the - advanced installation instructions - to install Z-Wave JS. -

-

- Here's the current Z-Wave configuration. You'll - need these values when setting up Z-Wave JS. -

- ${this._migrationConfig - ? html`
- USB Path: ${this._migrationConfig.usb_path}
- Network Key: - ${this._migrationConfig.network_key} -
` - : ``} -

- Once Z-Wave JS is installed and running, click - 'Continue' to set up the Z-Wave JS integration and - migrate your devices and entities. -

- `} -
-
- - Continue - -
-
- ` - : this._step === 3 - ? html` - -
-

- Now it's time to migrate your devices and entities from - the legacy Z-Wave integration to the Z-Wave JS - integration, to make sure all your UI's and automations - keep working. -

- ${this._waitingOnDevices?.map( - (device) => - html` - Device ${computeDeviceName(device, this.hass)} is - not ready yet! For the best result, wake the device - up if it is battery powered and wait for this device - to become ready. - ` - )} - ${this._migrationData - ? html` -

Below is a list of what will be migrated.

- ${this._migratedZwaveEntities!.length !== - this._migrationData.zwave_entity_ids.length - ? html` - The following entities will not be migrated - and might need manual adjustments to your - config: - -
    - ${this._migrationData.zwave_entity_ids.map( - (entity_id) => - !this._migratedZwaveEntities!.includes( - entity_id - ) - ? html`
  • - ${entity_id in this.hass.states - ? computeStateName( - this.hass.states[entity_id] - ) - : ""} - (${entity_id}) -
  • ` - : "" - )} -
` - : ""} - ${Object.keys( - this._migrationData.migration_device_map - ).length - ? html`

Devices that will be migrated:

-
    - ${Object.keys( - this._migrationData.migration_device_map - ).map( - (device_id) => - html`
  • - ${this._deviceNameLookup[device_id] || - device_id} -
  • ` - )} -
` - : ""} - ${Object.keys( - this._migrationData.migration_entity_map - ).length - ? html`

Entities that will be migrated:

-
    - ${Object.keys( - this._migrationData.migration_entity_map - ).map( - (entity_id) => html`
  • - ${entity_id in this.hass.states - ? computeStateName( - this.hass.states[entity_id] - ) - : ""} - (${entity_id}) -
  • ` - )} -
` - : ""} - ` - : html`
-

Loading migration data...

- - -
`} -
-
- - Migrate - -
-
- ` - : this._step === 4 - ? html` -
- That was all! You are now migrated to the new Z-Wave JS - integration, check if all your devices and entities are back - the way they where, if not all entities could be migrated - you might have to change those manually. -

- If you have 'zwave' in your configurtion.yaml file, you - should remove it now. -

-
- -
` - : ""} - `} -
-
- `; - } - - private async _getMigrationConfig(): Promise { - this._migrationConfig = await fetchMigrationConfig(this.hass!); - } - - private async _unsubscribe(): Promise { - if (this._unsub) { - (await this._unsub)(); - this._unsub = undefined; - } - } - - private _continue(): void { - this._step++; - } - - private async _stopNetwork(): Promise { - this._stoppingNetwork = true; - await this._getNetworkStatus(); - if (this._networkStatus?.state === ZWAVE_NETWORK_STATE_STOPPED) { - this._networkStopped(); - return; - } - - this._unsub = this.hass!.connection.subscribeEvents( - () => this._networkStopped(), - "zwave.network_stop" - ); - this.hass!.callService("zwave", "stop_network"); - } - - private async _setupZwaveJs() { - const zwaveJsConfigFlow = await startZwaveJsConfigFlow(this.hass); - showConfigFlowDialog(this, { - continueFlowId: zwaveJsConfigFlow.flow_id, - dialogClosedCallback: (params) => { - if (params.entryId) { - this._zwaveJsEntryId = params.entryId; - this._getZwaveJSNodesStatus(); - this._step = 3; - } - }, - showAdvanced: this.hass.userData?.showAdvanced, - }); - this.hass.loadBackendTranslation("title", "zwave_js", true); - } - - private async _getZwaveJSNodesStatus() { - if (this._nodeReadySubscriptions?.length) { - const unsubs = await Promise.all(this._nodeReadySubscriptions); - unsubs.forEach((unsub) => { - unsub(); - }); - } - this._nodeReadySubscriptions = []; - const networkStatus = await fetchZwaveJsNetworkStatus( - this.hass, - this._zwaveJsEntryId! - ); - const nodeStatePromisses = networkStatus.controller.nodes.map((nodeId) => - fetchZwaveNodeStatus(this.hass, this._zwaveJsEntryId!, nodeId) - ); - const nodesNotReady = (await Promise.all(nodeStatePromisses)).filter( - (node) => !node.ready - ); - - // eslint-disable-next-line no-console - console.log("waiting for nodes to be ready", nodesNotReady); - - this._getMigrationData(); - if (nodesNotReady.length === 0) { - this._waitingOnDevices = []; - return; - } - this._nodeReadySubscriptions = nodesNotReady.map((node) => - subscribeZwaveNodeReady( - this.hass, - this._zwaveJsEntryId!, - node.node_id, - () => { - this._getZwaveJSNodesStatus(); - } - ) - ); - const deviceReg: DeviceRegistryEntry[] = await fetchDeviceRegistry( - this.hass.connection - ); - this._waitingOnDevices = deviceReg.filter((device) => { - const identifiers = getZwaveJsIdentifiersFromDevice(device); - if ( - !identifiers || - Number(identifiers.home_id) !== networkStatus.controller.home_id - ) { - return false; - } - return nodesNotReady.some((node) => identifiers.node_id === node.node_id); - }); - } - - private async _getMigrationData() { - try { - this._migrationData = await migrateZwave( - this.hass, - this._zwaveJsEntryId!, - true - ); - } catch (err: any) { - showAlertDialog(this, { - title: "Failed to get migration data!", - text: - err.code === "unknown_command" - ? "Restart Home Assistant and try again." - : err.message, - }); - return; - } - this._migratedZwaveEntities = Object.keys( - this._migrationData.migration_entity_map - ); - if (Object.keys(this._migrationData.migration_device_map).length) { - this._fetchDevices(); - } - } - - private _fetchDevices() { - this._unsubDevices = subscribeDeviceRegistry( - this.hass.connection, - (devices) => { - if (!this._migrationData) { - return; - } - const migrationDevices = Object.keys( - this._migrationData.migration_device_map - ); - const deviceNameLookup = {}; - devices.forEach((device) => { - if (migrationDevices.includes(device.id)) { - deviceNameLookup[device.id] = computeDeviceName(device, this.hass); - } - }); - this._deviceNameLookup = deviceNameLookup; - } - ); - } - - private async _doMigrate() { - const data = await migrateZwave(this.hass, this._zwaveJsEntryId!, false); - if (!data.migrated) { - showAlertDialog(this, { title: "Migration failed!" }); - return; - } - this._step = 4; - } - - private _networkStopped(): void { - this._unsubscribe(); - this._getMigrationConfig(); - this._stoppingNetwork = false; - this._step = 2; - } - - private async _getNetworkStatus(): Promise { - this._networkStatus = await fetchNetworkStatus(this.hass!); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .content { - margin-top: 24px; - } - - .flex-container { - display: flex; - align-items: center; - } - - .flex-container ha-circular-progress { - margin-right: 20px; - } - - blockquote { - display: block; - background-color: var(--secondary-background-color); - color: var(--primary-text-color); - padding: 8px; - margin: 8px 0; - font-size: 0.9em; - font-family: monospace; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "zwave-migration": ZwaveMigration; - } -} diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts b/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts deleted file mode 100644 index f49090c2adbe..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { mdiCheckboxMarkedCircle, mdiClose, mdiHelpCircle } from "@mdi/js"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import "../../../../../components/buttons/ha-call-api-button"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import "../../../../../components/ha-circular-progress"; -import "../../../../../components/ha-svg-icon"; -import "../../../../../components/ha-icon-button"; -import "../../../../../components/ha-service-description"; -import { - fetchNetworkStatus, - ZWaveNetworkStatus, - ZWAVE_NETWORK_STATE_AWAKED, - ZWAVE_NETWORK_STATE_READY, - ZWAVE_NETWORK_STATE_STARTED, - ZWAVE_NETWORK_STATE_STOPPED, -} from "../../../../../data/zwave"; -import { haStyle } from "../../../../../resources/styles"; -import { HomeAssistant } from "../../../../../types"; -import { documentationUrl } from "../../../../../util/documentation-url"; -import "../../../ha-config-section"; - -@customElement("zwave-network") -export class ZwaveNetwork extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public isWide!: boolean; - - @state() private _showHelp = false; - - @state() private _networkStatus?: ZWaveNetworkStatus; - - @state() private _unsubs: Array> = []; - - public disconnectedCallback(): void { - this._unsubscribe(); - } - - protected firstUpdated(changedProps): void { - super.firstUpdated(changedProps); - this._getNetworkStatus(); - this._subscribe(); - } - - protected render(): TemplateResult { - return html` - -
- - ${this.hass!.localize( - "ui.panel.config.zwave.network_management.header" - )} - - -
-
- ${this.hass!.localize( - "ui.panel.config.zwave.network_management.introduction" - )} -

- - ${this.hass!.localize("ui.panel.config.zwave.learn_more")} - -

-
- - ${this._networkStatus - ? html` - -
- ${this._networkStatus.state === ZWAVE_NETWORK_STATE_STOPPED - ? html` - - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_stopped" - )} - ` - : this._networkStatus.state === ZWAVE_NETWORK_STATE_STARTED - ? html` - - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_starting" - )}
- - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_starting_note" - )} - - ` - : this._networkStatus.state === ZWAVE_NETWORK_STATE_AWAKED - ? html` - - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_started" - )}
- - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_started_note_some_queried" - )} - - ` - : this._networkStatus.state === ZWAVE_NETWORK_STATE_READY - ? html` - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_started" - )}
- - ${this.hass!.localize( - "ui.panel.config.zwave.network_status.network_started_note_all_queried" - )} - - ` - : ""} -
-
- ${this._networkStatus.state >= ZWAVE_NETWORK_STATE_AWAKED - ? html` - ${this._generateServiceButton("stop_network")} - ${this._generateServiceButton("heal_network")} - ${this._generateServiceButton("test_network")} - ` - : html` ${this._generateServiceButton("start_network")} `} -
- ${this._networkStatus.state >= ZWAVE_NETWORK_STATE_AWAKED - ? html` -
- ${this._generateServiceButton("soft_reset")} - - ${this.hass!.localize( - "ui.panel.config.zwave.services.save_config" - )} - -
- ` - : ""} -
- ${this._networkStatus.state >= ZWAVE_NETWORK_STATE_AWAKED - ? html` - -
- ${this._generateServiceButton("add_node_secure")} - ${this._generateServiceButton("add_node")} - ${this._generateServiceButton("remove_node")} -
-
- ${this._generateServiceButton("cancel_command")} -
-
- ` - : ""} - ` - : ""} -
- `; - } - - private async _getNetworkStatus(): Promise { - this._networkStatus = await fetchNetworkStatus(this.hass!); - } - - private _subscribe(): void { - this._unsubs = [ - "zwave.network_start", - "zwave.network_stop", - "zwave.network_ready", - "zwave.network_complete", - "zwave.network_complete_some_dead", - ].map((e) => - this.hass!.connection.subscribeEvents( - (event) => this._handleEvent(event), - e - ) - ); - } - - private _unsubscribe(): void { - while (this._unsubs.length) { - this._unsubs.pop()!.then((unsub) => unsub()); - } - } - - private _handleEvent(event) { - if (event.event_type === "zwave.network_start") { - // Optimistically set the state, wait 1s and poll the backend - // The backend will still report a state of 0 when the 'network_start' - // event is first fired. - if (this._networkStatus) { - this._networkStatus = { ...this._networkStatus, state: 5 }; - } - setTimeout(() => this._getNetworkStatus, 1000); - } else { - this._getNetworkStatus(); - } - } - - private _onHelpTap(): void { - this._showHelp = !this._showHelp; - } - - private _generateServiceButton(service: string) { - return html` - - ${this.hass!.localize("ui.panel.config.zwave.services." + service)} - - - - `; - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .content { - margin-top: 24px; - } - - .sectionHeader { - position: relative; - padding-right: 40px; - } - - .network-status { - text-align: center; - } - - .network-status div.details { - font-size: 1.5rem; - padding: 24px; - } - - .network-status ha-svg-icon { - display: block; - margin: 0px auto 16px; - width: 48px; - height: 48px; - } - - .network-status small { - font-size: 1rem; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - - .card-actions.warning ha-call-service-button { - color: var(--error-color); - } - - .toggle-help-icon { - position: absolute; - top: -6px; - right: 0; - color: var(--primary-color); - } - - ha-service-description { - display: block; - color: grey; - padding: 0 8px 12px; - } - - [hidden] { - display: none; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "zwave-network": ZwaveNetwork; - } -} diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-node-config.ts b/src/panels/config/integrations/integration-panels/zwave/zwave-node-config.ts deleted file mode 100644 index 975862c67939..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-node-config.ts +++ /dev/null @@ -1,388 +0,0 @@ -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-input/paper-input"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; -import { - css, - CSSResultGroup, - html, - LitElement, - PropertyValues, - TemplateResult, -} from "lit"; -import { customElement, property, state } from "lit/decorators"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import { - fetchNodeConfig, - ZWaveConfigItem, - ZWaveConfigServiceData, - ZWaveNode, -} from "../../../../../data/zwave"; -import { haStyle } from "../../../../../resources/styles"; -import { HomeAssistant } from "../../../../../types"; - -@customElement("zwave-node-config") -export class ZwaveNodeConfig extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public nodes: ZWaveNode[] = []; - - @property() public config: ZWaveConfigItem[] = []; - - @property() public selectedNode = -1; - - @state() private _configItem?: ZWaveConfigItem; - - @state() private _wakeupInput = -1; - - @state() private _selectedConfigParameter = -1; - - @state() private _selectedConfigValue: number | string = -1; - - protected render(): TemplateResult { - return html` -
- - ${"wake_up_interval" in this.nodes[this.selectedNode].attributes - ? html` -
- -
- ${this.hass!.localize( - "ui.panel.config.zwave.node_config.seconds" - )} -
-
- - ${this.hass!.localize( - "ui.panel.config.zwave.node_config.set_wakeup" - )} - -
- ` - : ""} -
- - - ${this.config.map( - (entityState) => html` - - ${entityState.key}: ${entityState.value.label} - - ` - )} - - -
- ${this._configItem - ? html` - ${this._configItem.value.type === "List" - ? html` -
- - - ${this._configItem.value.data_items.map( - (entityState) => html` - ${entityState} - ` - )} - - -
- ` - : ""} - ${["Byte", "Short", "Int"].includes(this._configItem.value.type) - ? html` -
- - -
- ` - : ""} - ${["Bool", "Button"].includes(this._configItem.value.type) - ? html` -
- - - - ${this.hass!.localize( - "ui.panel.config.zwave.node_config.true" - )} - - - ${this.hass!.localize( - "ui.panel.config.zwave.node_config.false" - )} - - - -
- ` - : ""} -
- ${this._configItem.value.help} -
- ${["Bool", "Button", "Byte", "Short", "Int", "List"].includes( - this._configItem.value.type - ) - ? html` -
- - ${this.hass!.localize( - "ui.panel.config.zwave.node_config.set_config_parameter" - )} - -
- ` - : ""} - ` - : ""} -
-
- `; - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .content { - margin-top: 24px; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - - .device-picker { - @apply --layout-horizontal; - @apply --layout-center-center; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -ms-flex-direction: row; - -webkit-flex-direction: row; - flex-direction: row; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; - padding-left: 24px; - padding-right: 24px; - padding-bottom: 24px; - } - - .help-text { - padding-left: 24px; - padding-right: 24px; - } - - .flex { - -ms-flex: 1 1 0.000000001px; - -webkit-flex: 1; - flex: 1; - -webkit-flex-basis: 0.000000001px; - flex-basis: 0.000000001px; - } - `, - ]; - } - - protected firstUpdated(changedProps: PropertyValues): void { - super.firstUpdated(changedProps); - this.addEventListener("hass-service-called", (ev) => - this.serviceCalled(ev) - ); - } - - protected updated(changedProps: PropertyValues): void { - super.updated(changedProps); - if (changedProps.has("selectedNode")) { - this._nodesChanged(); - } - } - - private serviceCalled(ev): void { - if (ev.detail.success) { - setTimeout(() => { - this._refreshConfig(this.selectedNode); - }, 5000); - } - } - - private _nodesChanged(): void { - if (!this.nodes) { - return; - } - this._configItem = undefined; - this._wakeupInput = - this.nodes[this.selectedNode].attributes.wake_up_interval || -1; - } - - private _onWakeupIntervalChanged(value: ChangeEvent): void { - this._wakeupInput = value.detail!.value; - } - - private _computeWakeupServiceData(wakeupInput: number) { - return { - node_id: this.nodes[this.selectedNode].attributes.node_id, - value: wakeupInput, - }; - } - - private _computeSetConfigParameterServiceData(): - | ZWaveConfigServiceData - | boolean { - if (this.selectedNode === -1 || typeof this._configItem === "undefined") { - return false; - } - let valueData: number | string = ""; - if (["Short", "Byte", "Int"].includes(this._configItem!.value.type)) { - valueData = - typeof this._selectedConfigValue === "string" - ? parseInt(this._selectedConfigValue, 10) - : this._selectedConfigValue; - } - if (["Bool", "Button", "List"].includes(this._configItem!.value.type)) { - valueData = this._selectedConfigValue; - } - return { - node_id: this.nodes[this.selectedNode].attributes.node_id, - parameter: this._configItem.key, - value: valueData, - }; - } - - private _selectedConfigParameterChanged(event: ItemSelectedEvent): void { - if (event.target!.selected === -1) { - return; - } - this._selectedConfigParameter = event.target!.selected; - this._configItem = this.config[event.target!.selected]; - } - - private _configValueSelectChanged(event: ItemSelectedEvent): void { - if (event.target!.selected === -1) { - return; - } - this._selectedConfigValue = event.target!.selectedItem.textContent; - } - - private _configValueInputChanged(value: ChangeEvent): void { - this._selectedConfigValue = value.detail!.value; - } - - private async _refreshConfig(selectedNode): Promise { - const configData: ZWaveConfigItem[] = []; - const config = await fetchNodeConfig( - this.hass, - this.nodes[selectedNode].attributes.node_id - ); - - Object.keys(config).forEach((key) => { - configData.push({ - key: parseInt(key, 10), - value: config[key], - }); - }); - - this.config = configData; - this._configItem = this.config[this._selectedConfigParameter]; - } -} - -export interface ChangeEvent { - detail?: { - value?: any; - }; - target?: EventTarget; -} - -export interface PickerTarget extends EventTarget { - selected: number; - selectedItem?: any; -} - -export interface ItemSelectedEvent { - target?: PickerTarget; -} - -declare global { - interface HTMLElementTagNameMap { - "zwave-node-config": ZwaveNodeConfig; - } -} diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-node-protection.js b/src/panels/config/integrations/integration-panels/zwave/zwave-node-protection.js deleted file mode 100644 index 693ed425fb56..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-node-protection.js +++ /dev/null @@ -1,179 +0,0 @@ -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-input/paper-input"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../../../../../components/buttons/ha-call-api-button"; -import "../../../../../components/ha-card"; -import LocalizeMixin from "../../../../../mixins/localize-mixin"; -import "../../../../../styles/polymer-ha-style"; - -class ZwaveNodeProtection extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - -
- -
- - - - - -
-
- - [[localize('ui.panel.config.zwave.node_management.set_protection')]] - -
-
-
-`; - } - - static get properties() { - return { - hass: Object, - - nodes: Array, - - selectedNode: { - type: Number, - value: -1, - }, - - protectionNode: { - type: Boolean, - value: false, - }, - - _protectionValueID: { - type: Number, - value: -1, - }, - - _selectedProtectionParameter: { - type: Number, - value: -1, - observer: "_computeProtectionData", - }, - - _protectionOptions: Array, - - _protection: { - type: Array, - value: () => [], - }, - - _loadedProtectionValue: { - type: String, - value: "", - }, - - _protectionData: { - type: Object, - value: {}, - }, - - _nodePath: String, - }; - } - - static get observers() { - return ["_nodesChanged(nodes, selectedNode)"]; - } - - ready() { - super.ready(); - this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev)); - } - - apiCalled(ev) { - if (ev.detail.success) { - setTimeout(() => { - this._refreshProtection(this.selectedNode); - }, 5000); - } - } - - _nodesChanged() { - if (!this.nodes) return; - if (this.protection) { - if (this.protection.length === 0) { - return; - } - let options = []; - let value_id = -1; - let selected = -1; - this.protection.forEach((item) => { - if (item.key === "options") options = item.value; - else if (item.key === "value_id") value_id = item.value; - else if (item.key === "selected") selected = item.value; - }); - this.setProperties({ - protectionNode: true, - _protectionOptions: options, - _loadedProtectionValue: selected, - _protectionValueID: value_id, - }); - } - } - - async _refreshProtection(selectedNode) { - const protectionValues = []; - const protections = await this.hass.callApi( - "GET", - `zwave/protection/${this.nodes[selectedNode].attributes.node_id}` - ); - Object.keys(protections).forEach((key) => { - protectionValues.push({ - key, - value: protections[key], - }); - }); - this.setProperties({ - _protection: protectionValues, - _selectedProtectionParameter: -1, - _loadedProtectionValue: this.protection[1].value, - }); - } - - _computeProtectionData(selectedProtectionParameter) { - if (this.selectedNode === -1 || selectedProtectionParameter === -1) return; - this._protectionData = { - selection: this._protectionOptions[selectedProtectionParameter], - value_id: this._protectionValueID, - }; - this._nodePath = `zwave/protection/${ - this.nodes[this.selectedNode].attributes.node_id - }`; - } -} - -customElements.define("zwave-node-protection", ZwaveNodeProtection); diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-usercodes.js b/src/panels/config/integrations/integration-panels/zwave/zwave-usercodes.js deleted file mode 100644 index 7180f8d88007..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-usercodes.js +++ /dev/null @@ -1,226 +0,0 @@ -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-input/paper-input"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import "../../../../../styles/polymer-ha-style"; - -class ZwaveUsercodes extends PolymerElement { - static get template() { - return html` - -
- -
- - - - - -
- - -
-
- `; - } - - static get properties() { - return { - hass: Object, - - nodes: Array, - - selectedNode: { - type: Number, - observer: "_selectedNodeChanged", - }, - - userCodes: Object, - - _selectedUserCode: { - type: Number, - value: -1, - observer: "_selectedUserCodeChanged", - }, - - _selectedUserCodeValue: String, - - _computedCodeOutput: { - type: String, - value: "", - }, - }; - } - - ready() { - super.ready(); - this.addEventListener("hass-service-called", (ev) => - this.serviceCalled(ev) - ); - } - - serviceCalled(ev) { - if (ev.detail.success) { - setTimeout(() => { - this._refreshUserCodes(this.selectedNode); - }, 5000); - } - } - - _isUserCodeSelected(selectedUserCode) { - if (selectedUserCode === -1) return false; - return true; - } - - _computeSelectCaptionUserCodes(stateObj) { - return `${stateObj.key}: ${stateObj.value.label}`; - } - - _selectedUserCodeChanged(selectedUserCode) { - if (this._selectedUserCode === -1 || selectedUserCode === -1) return; - const value = this.userCodes[selectedUserCode].value.code; - this.setProperties({ - _selectedUserCodeValue: this._a2hex(value), - _computedCodeOutput: `[${this._hex2a(this._a2hex(value))}]`, - }); - } - - _computeUserCodeServiceData(selectedUserCodeValue, type) { - if (this.selectedNode === -1 || !selectedUserCodeValue) return -1; - let serviceData = null; - let valueData = null; - if (type === "Add") { - valueData = this._hex2a(selectedUserCodeValue); - this._computedCodeOutput = `[${valueData}]`; - serviceData = { - node_id: this.nodes[this.selectedNode].attributes.node_id, - code_slot: this._selectedUserCode, - usercode: valueData, - }; - } - if (type === "Delete") { - serviceData = { - node_id: this.nodes[this.selectedNode].attributes.node_id, - code_slot: this._selectedUserCode, - }; - } - return serviceData; - } - - async _refreshUserCodes(selectedNode) { - this.setProperties({ _selectedUserCodeValue: "" }); - const userCodes = []; - const userCodeData = await this.hass.callApi( - "GET", - `zwave/usercodes/${this.nodes[selectedNode].attributes.node_id}` - ); - Object.keys(userCodeData).forEach((key) => { - userCodes.push({ - key, - value: userCodeData[key], - }); - }); - this.setProperties({ userCodes: userCodes }); - this._selectedUserCodeChanged(this._selectedUserCode); - } - - _a2hex(str) { - const arr = []; - let output = ""; - for (let i = 0, l = str.length; i < l; i++) { - const hex = Number(str.charCodeAt(i)).toString(16); - if (hex === "0") { - output = "00"; - } else { - output = hex; - } - arr.push("\\x" + output); - } - return arr.join(""); - } - - _hex2a(hexx) { - const hex = hexx.toString(); - const hexMod = hex.replace(/\\x/g, ""); - let str = ""; - for (let i = 0; i < hexMod.length; i += 2) { - str += String.fromCharCode(parseInt(hexMod.substr(i, 2), 16)); - } - return str; - } - - _selectedNodeChanged() { - if (this.selectedNode === -1) return; - this.setProperties({ _selecteduserCode: -1 }); - } -} - -customElements.define("zwave-usercodes", ZwaveUsercodes); diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-values.ts b/src/panels/config/integrations/integration-panels/zwave/zwave-values.ts deleted file mode 100644 index 1e6750c6af17..000000000000 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-values.ts +++ /dev/null @@ -1,109 +0,0 @@ -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-listbox/paper-listbox"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import "../../../../../components/buttons/ha-call-service-button"; -import "../../../../../components/ha-card"; -import { ZWaveValue } from "../../../../../data/zwave"; -import { haStyle } from "../../../../../resources/styles"; -import { HomeAssistant } from "../../../../../types"; - -@customElement("zwave-values") -export class ZwaveValues extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property() public values: ZWaveValue[] = []; - - @state() private _selectedValue = -1; - - protected render(): TemplateResult { - return html` -
- -
- - - ${this.values.map( - (item) => html` - ${this._computeCaption(item)} - ` - )} - - -
-
-
- `; - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .content { - margin-top: 24px; - } - - ha-card { - margin: 0 auto; - max-width: 600px; - } - - .device-picker { - @apply --layout-horizontal; - @apply --layout-center-center; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -ms-flex-direction: row; - -webkit-flex-direction: row; - flex-direction: row; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; - padding-left: 24px; - padding-right: 24px; - padding-bottom: 24px; - } - - .flex { - -ms-flex: 1 1 0.000000001px; - -webkit-flex: 1; - flex: 1; - -webkit-flex-basis: 0.000000001px; - flex-basis: 0.000000001px; - } - - .help-text { - padding-left: 24px; - padding-right: 24px; - } - `, - ]; - } - - private _computeCaption(item) { - let out = `${item.value.label}`; - out += ` (${this.hass.localize("ui.panel.config.zwave.common.instance")}:`; - out += ` ${item.value.instance},`; - out += ` ${this.hass.localize("ui.panel.config.zwave.common.index")}:`; - out += ` ${item.value.index})`; - return out; - } -} - -declare global { - interface HTMLElementTagNameMap { - "zwave-values": ZwaveValues; - } -} diff --git a/src/translations/en.json b/src/translations/en.json index d1ec5a56b3a3..f657f3e6dd65 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2692,120 +2692,6 @@ "stop_listening": "Stop listening", "message_received": "Message {id} received on {topic} at {time}:" }, - "ozw": { - "common": { - "zwave": "Z-Wave", - "node_id": "Node ID", - "ozw_instance": "OpenZWave Instance", - "instance": "Instance", - "controller": "Controller", - "network": "Network", - "wakeup_instructions": "Wake-up Instructions", - "query_stage": "Query Stage" - }, - "device_info": { - "zwave_info": "Z-Wave Info", - "stage": "Stage", - "node_failed": "Node Failed" - }, - "node_query_stages": { - "protocolinfo": "Obtaining basic Z-Wave capabilities of this node from the controller", - "probe": "Checking if the node is awake/alive", - "wakeup": "Setting up support for wake-up queues and messages", - "manufacturerspecific1": "Obtaining manufacturer and product ID codes from the node", - "nodeinfo": "Obtaining supported command classes from the node", - "nodeplusinfo": "Obtaining Z-Wave+ information from the node", - "manufacturerspecific2": "Obtaining additional manufacturer and product ID codes from the node", - "versions": "Obtaining information about firmware and command class versions", - "instances": "Obtaining details about what instances or channels a device supports", - "static": "Obtaining static values from the device", - "cacheload": "Loading information from the OpenZWave cache file. Battery nodes will stay at this stage until the node wakes up.", - "associations": "Refreshing association groups and memberships", - "neighbors": "Obtaining a list of the node's neighbors", - "session": "Obtaining infrequently changing values from the node", - "dynamic": "Obtaining frequently changing values from the node", - "configuration": "Obtaining configuration values from the node", - "complete": "Interview process is complete" - }, - "refresh_node": { - "button": "Refresh Node", - "title": "Refresh Node Information", - "complete": "Node Refresh Complete", - "description": "This will tell OpenZWave to re-interview a node and update the node's command classes, capabilities, and values.", - "battery_note": "If the node is battery powered, be sure to wake it before proceeding", - "wakeup_header": "Wake-up Instructions for", - "wakeup_instructions_source": "Wake-up instructions are sourced from the OpenZWave community device database.", - "start_refresh_button": "Start Refresh", - "refreshing_description": "Refreshing node information…", - "node_status": "Node Status", - "step": "Step" - }, - "network_status": { - "online": "Online", - "offline": "Offline", - "starting": "Starting", - "unknown": "Unknown", - "details": { - "driverallnodesqueried": "All nodes have been queried", - "driverallnodesqueriedsomedead": "All nodes have been queried. Some nodes were found dead", - "driverawakenodesqueries": "All awake nodes have been queried", - "driverremoved": "The driver has been removed", - "driverreset": "The driver has been reset", - "driverfailed": "Failed to connect to Z-Wave controller", - "driverready": "Initializing the Z-Wave controller", - "ready": "Ready to connect", - "stopped": "OpenZWave stopped", - "started": "Connected to MQTT", - "starting": "Connecting to MQTT", - "offline": "OZWDaemon offline" - } - }, - "navigation": { - "select_instance": "Select Instance", - "network": "Network", - "nodes": "Nodes", - "node": { - "dashboard": "Dashboard", - "config": "Config" - } - }, - "select_instance": { - "header": "Select an OpenZWave Instance", - "introduction": "You have more than one OpenZWave instance running. Which instance would you like to manage?", - "none_found": "We couldn't find an OpenZWave instance. If you believe this is incorrect, check your OpenZWave and MQTT setups and ensure that Home Assistant can communicate with your MQTT broker." - }, - "network": { - "header": "Network Management", - "introduction": "Manage network-wide functions.", - "node_count": "{count} nodes" - }, - "nodes_table": { - "id": "ID", - "manufacturer": "Manufacturer", - "model": "Model", - "query_stage": "Query Stage", - "zwave_plus": "Z-Wave Plus", - "failed": "Failed" - }, - "node": { - "button": "Node Details", - "not_found": "Node not found" - }, - "node_config": { - "header": "Node Configuration", - "introduction": "Manage the different configuration parameters for a Z-Wave node.", - "help_source": "Config parameter descriptions and help text are provided by the OpenZWave project.", - "wakeup_help": "Battery powered nodes must be awake to change their configuration. If the node is not awake, OpenZWave will attempt to update the node's configuration the next time it wakes up, which could be multiple hours (or days) later. Follow these steps to wake up your device:" - }, - "node_metadata": { - "product_manual": "Product Manual" - }, - "services": { - "add_node": "Add Node", - "remove_node": "Remove Node", - "cancel_command": "Cancel Command" - } - }, "zha": { "common": { "clusters": "Clusters", @@ -2899,96 +2785,6 @@ "unbind_button_help": "Unbind the selected group from the selected device clusters." } }, - "zwave": { - "description": "Manage your Z-Wave network", - "learn_more": "Learn more about Z-Wave", - "common": { - "value": "Value", - "instance": "Instance", - "index": "Index", - "unknown": "unknown", - "wakeup_interval": "Wake-up Interval" - }, - "migration": { - "zwave_js": { - "header": "Migrate to Z-Wave JS", - "introduction": "This integration is no longer maintained, and we advise you to move to the new Z-Wave JS integration. This wizard will help you migrate from the legacy Z-Wave integration to the new Z-Wave JS integration." - } - }, - "network_management": { - "header": "Z-Wave Network Management", - "introduction": "Run commands that affect the Z-Wave network. You won't get feedback on whether most commands succeeded, but you can check the OZW Log to try to find out." - }, - "node_management": { - "header": "Z-Wave Node Management", - "introduction": "Run Z-Wave commands that affect a single node. Pick a node to see a list of available commands.", - "nodes": "Nodes", - "nodes_hint": "Select node to view per-node options", - "entities": "Entities of this node", - "entity_info": "Entity Information", - "exclude_entity": "Exclude this entity from Home Assistant", - "pooling_intensity": "Polling intensity", - "node_protection": "Node protection", - "protection": "Protection", - "set_protection": "Set Protection", - "node_group_associations": "Node group associations", - "group": "Group", - "node_to_control": "Node to control", - "nodes_in_group": "Other nodes in this group:", - "max_associations": "Max Associations:", - "add_to_group": "Add to Group", - "remove_from_group": "Remove from Group", - "remove_broadcast": "Remove Broadcast" - }, - "ozw_log": { - "header": "OZW Log", - "introduction": "View the log. 0 is the minimum (loads entire log) and 1000 is the maximum. Load will show a static log and tail will auto update with the last specified number of lines of the log.", - "last_log_lines": "Number of last log lines", - "load": "Load", - "tail": "Tail" - }, - "network_status": { - "network_stopped": "Z-Wave Network Stopped", - "network_starting": "Starting Z-Wave Network…", - "network_starting_note": "This may take a while depending on the size of your network.", - "network_started": "Z-Wave Network Started", - "network_started_note_some_queried": "Awake nodes have been queried. Sleeping nodes will be queried when they wake.", - "network_started_note_all_queried": "All nodes have been queried." - }, - "node_config": { - "header": "Node Configuration Options", - "seconds": "seconds", - "set_wakeup": "Set Wake-up Interval", - "config_parameter": "Configuration Parameter", - "config_value": "Configuration Value", - "true": "True", - "false": "False", - "set_config_parameter": "Set Configuration Parameter" - }, - "values": { - "header": "Node Values" - }, - "services": { - "start_network": "Start Network", - "stop_network": "Stop Network", - "heal_network": "Heal Network", - "test_network": "Test Network", - "soft_reset": "Soft Reset", - "save_config": "Save Configuration", - "add_node_secure": "Add Node Secure", - "add_node": "Add Node", - "remove_node": "Remove Node", - "cancel_command": "Cancel Command", - "refresh_node": "Refresh Node", - "remove_failed_node": "Remove Failed Node", - "replace_failed_node": "Replace Failed Node", - "print_node": "Print Node", - "heal_node": "Heal Node", - "test_node": "Test Node", - "node_info": "Node Information", - "refresh_entity": "Refresh Entity" - } - }, "zwave_js": { "navigation": { "network": "Network", diff --git a/yarn.lock b/yarn.lock index 10cde4b7b829..d20b3377e854 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3136,19 +3136,7 @@ __metadata: languageName: node linkType: hard -"@polymer/iron-dropdown@npm:^3.0.0-pre.26": - version: 3.0.1 - resolution: "@polymer/iron-dropdown@npm:3.0.1" - dependencies: - "@polymer/iron-behaviors": ^3.0.0-pre.26 - "@polymer/iron-overlay-behavior": ^3.0.0-pre.27 - "@polymer/neon-animation": ^3.0.0-pre.26 - "@polymer/polymer": ^3.0.0 - checksum: 2c1ba429c8f5553f8493f256691efa8a338e8c038c1102f482ecb612b61c079b5019f6c362aefb31b44d3429661152c1b6912408a69c67e9d6fff62914ad801f - languageName: node - linkType: hard - -"@polymer/iron-fit-behavior@npm:^3.0.0-pre.26, @polymer/iron-fit-behavior@npm:^3.1.0": +"@polymer/iron-fit-behavior@npm:^3.0.0-pre.26": version: 3.1.0 resolution: "@polymer/iron-fit-behavior@npm:3.1.0" dependencies: @@ -3295,17 +3283,6 @@ __metadata: languageName: node linkType: hard -"@polymer/neon-animation@npm:^3.0.0-pre.26": - version: 3.0.1 - resolution: "@polymer/neon-animation@npm:3.0.1" - dependencies: - "@polymer/iron-resizable-behavior": ^3.0.0-pre.26 - "@polymer/iron-selector": ^3.0.0-pre.26 - "@polymer/polymer": ^3.0.0 - checksum: c5ea5e1ef9f2017faaa5799ea108b26634dd7d986fe469369e629075efe382a5e5d4f9c537bacc77f9852453a2758c9f67e491d6ea5a1c4457f772bfdf06c707 - languageName: node - linkType: hard - "@polymer/paper-behaviors@npm:^3.0.0-pre.27": version: 3.0.1 resolution: "@polymer/paper-behaviors@npm:3.0.1" @@ -3318,25 +3295,6 @@ __metadata: languageName: node linkType: hard -"@polymer/paper-dropdown-menu@npm:^3.2.0": - version: 3.2.0 - resolution: "@polymer/paper-dropdown-menu@npm:3.2.0" - dependencies: - "@polymer/iron-a11y-keys-behavior": ^3.0.0-pre.26 - "@polymer/iron-form-element-behavior": ^3.0.0-pre.26 - "@polymer/iron-icon": ^3.0.0-pre.26 - "@polymer/iron-iconset-svg": ^3.0.0-pre.26 - "@polymer/iron-validatable-behavior": ^3.0.0-pre.26 - "@polymer/paper-behaviors": ^3.0.0-pre.27 - "@polymer/paper-input": ^3.1.0 - "@polymer/paper-menu-button": ^3.1.0 - "@polymer/paper-ripple": ^3.0.0-pre.26 - "@polymer/paper-styles": ^3.0.0-pre.26 - "@polymer/polymer": ^3.3.1 - checksum: dc7f6a8e3d449f37068ad5ee1d1c6d9037c9abd855ccc1d4e433d743c6378bb25af165ea86174edd1414887cf56a011ad0adda6d24dd55765e9458d49dc3e5e3 - languageName: node - linkType: hard - "@polymer/paper-icon-button@npm:^3.0.0-pre.26": version: 3.0.2 resolution: "@polymer/paper-icon-button@npm:3.0.2" @@ -3349,7 +3307,7 @@ __metadata: languageName: node linkType: hard -"@polymer/paper-input@npm:^3.0.0-pre.26, @polymer/paper-input@npm:^3.1.0, @polymer/paper-input@npm:^3.2.1": +"@polymer/paper-input@npm:^3.0.0-pre.26, @polymer/paper-input@npm:^3.2.1": version: 3.2.1 resolution: "@polymer/paper-input@npm:3.2.1" dependencies: @@ -3388,21 +3346,6 @@ __metadata: languageName: node linkType: hard -"@polymer/paper-menu-button@npm:^3.1.0": - version: 3.1.0 - resolution: "@polymer/paper-menu-button@npm:3.1.0" - dependencies: - "@polymer/iron-a11y-keys-behavior": ^3.0.0-pre.26 - "@polymer/iron-behaviors": ^3.0.0-pre.26 - "@polymer/iron-dropdown": ^3.0.0-pre.26 - "@polymer/iron-fit-behavior": ^3.1.0 - "@polymer/neon-animation": ^3.0.0-pre.26 - "@polymer/paper-styles": ^3.0.0-pre.26 - "@polymer/polymer": ^3.0.0 - checksum: 9243e104bac583189c6221f2df8dffeb331868cbf8084dd488cf2ddaba25987bfb3d4d2a9bd3168e6b49f28ba6b1b07ef7163fbfcf3af97978d34608e91cc605 - languageName: node - linkType: hard - "@polymer/paper-progress@npm:^3.0.0-pre.26": version: 3.0.1 resolution: "@polymer/paper-progress@npm:3.0.1" @@ -9113,7 +9056,6 @@ fsevents@^1.2.7: "@polymer/iron-icon": ^3.0.1 "@polymer/iron-input": ^3.0.1 "@polymer/iron-resizable-behavior": ^3.0.1 - "@polymer/paper-dropdown-menu": ^3.2.0 "@polymer/paper-input": ^3.2.1 "@polymer/paper-item": ^3.0.1 "@polymer/paper-listbox": ^3.0.1 @@ -9240,7 +9182,6 @@ fsevents@^1.2.7: vis-network: ^8.5.4 vue: ^2.6.12 vue2-daterange-picker: ^0.5.1 - web-animations-js: ^2.3.2 webpack: ^5.55.1 webpack-cli: ^4.8.0 webpack-dev-server: ^4.3.0 @@ -15703,13 +15644,6 @@ typescript@^4.4.3: languageName: node linkType: hard -"web-animations-js@npm:^2.3.2": - version: 2.3.2 - resolution: "web-animations-js@npm:2.3.2" - checksum: 194db111bb2f92c15100c33b63af320ccdc26066748e358a945b947c510216c78e0a1e2ae22fefbaacb585c8a0b41b62a1417d8b549636ee32e16f059bb488f2 - languageName: node - linkType: hard - "web-component-analyzer@npm:~1.1.1": version: 1.1.6 resolution: "web-component-analyzer@npm:1.1.6" From adefc7a4e23cda62c3e1607c8480c86b3b75fa60 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Mar 2022 23:15:10 +0100 Subject: [PATCH 07/35] Convert lovelace config dialogs to ha-form (#11910) --- .../dialog-lovelace-dashboard-detail.ts | 290 ++++++++---------- .../dialog-lovelace-resource-detail.ts | 227 +++++++------- 2 files changed, 240 insertions(+), 277 deletions(-) diff --git a/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts b/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts index 39b449d3d7bc..75db9e031cfb 100644 --- a/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts +++ b/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts @@ -1,20 +1,18 @@ import "@material/mwc-button/mwc-button"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; import { slugify } from "../../../../common/string/slugify"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-formfield"; -import "../../../../components/ha-icon-picker"; -import "../../../../components/ha-switch"; -import type { HaSwitch } from "../../../../components/ha-switch"; +import "../../../../components/ha-form/ha-form"; +import { HaFormSchema } from "../../../../components/ha-form/types"; +import { CoreFrontendUserData } from "../../../../data/frontend"; import { LovelaceDashboard, LovelaceDashboardCreateParams, - LovelaceDashboardMutableParams, } from "../../../../data/lovelace"; import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel"; -import { PolymerChangedEvent } from "../../../../polymer-types"; import { haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail"; @@ -25,62 +23,54 @@ export class DialogLovelaceDashboardDetail extends LitElement { @state() private _params?: LovelaceDashboardDetailsDialogParams; - @state() private _urlPath!: LovelaceDashboard["url_path"]; + @state() private _urlPathChanged = false; - @state() private _showInSidebar!: boolean; + @state() private _data?: Partial; - @state() private _icon!: string; - - @state() private _title!: string; - - @state() - private _requireAdmin!: LovelaceDashboard["require_admin"]; - - @state() private _error?: string; + @state() private _error?: Record; @state() private _submitting = false; - public async showDialog( - params: LovelaceDashboardDetailsDialogParams - ): Promise { + public showDialog(params: LovelaceDashboardDetailsDialogParams): void { this._params = params; this._error = undefined; - this._urlPath = this._params.urlPath || ""; + this._urlPathChanged = false; if (this._params.dashboard) { - this._showInSidebar = !!this._params.dashboard.show_in_sidebar; - this._icon = this._params.dashboard.icon || ""; - this._title = this._params.dashboard.title || ""; - this._requireAdmin = this._params.dashboard.require_admin || false; + this._data = this._params.dashboard; } else { - this._showInSidebar = true; - this._icon = ""; - this._title = ""; - this._requireAdmin = false; + this._data = { + show_in_sidebar: true, + icon: "", + title: "", + require_admin: false, + mode: "storage", + }; } - await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + this._data = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render(): TemplateResult { - if (!this._params) { + if (!this._params || !this._data) { return html``; } const defaultPanelUrlPath = this.hass.defaultPanel; - const urlInvalid = - this._params.urlPath !== "lovelace" && - !/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(this._urlPath); - const titleInvalid = !this._title.trim(); - const dir = computeRTLDirection(this.hass); + const titleInvalid = !this._data.title || !this._data.title.trim(); return html` ${this._error}
` - : ""} -
- - - ${!this._params.dashboard && this.hass.userData?.showAdvanced - ? html` - - ` - : ""} -
- - - - -
-
- - - - -
-
+ `}
${this._params.urlPath @@ -206,7 +134,9 @@ export class DialogLovelaceDashboardDetail extends LitElement { ${this._params.urlPath @@ -223,41 +153,97 @@ export class DialogLovelaceDashboardDetail extends LitElement { `; } - private _urlChanged(ev: PolymerChangedEvent) { - this._error = undefined; - this._urlPath = ev.detail.value; - } + private _schema = memoizeOne( + ( + params: LovelaceDashboardDetailsDialogParams, + userData: CoreFrontendUserData | null | undefined + ) => + [ + { + name: "title", + required: true, + selector: { + text: {}, + }, + }, + { + name: "icon", + required: true, + selector: { + icon: {}, + }, + }, + !params.dashboard && + userData?.showAdvanced && { + name: "url_path", + required: true, + selector: { text: {} }, + }, + { + name: "require_admin", + required: true, + selector: { + boolean: {}, + }, + }, + { + name: "show_in_sidebar", + required: true, + selector: { + boolean: {}, + }, + }, + ].filter(Boolean) + ); - private _iconChanged(ev: PolymerChangedEvent) { - this._error = undefined; - this._icon = ev.detail.value; - } + private _computeLabel = (entry: HaFormSchema): string => + this.hass.localize( + `ui.panel.config.lovelace.dashboards.detail.${ + entry.name === "show_in_sidebar" + ? "show_sidebar" + : entry.name === "url_path" + ? "url" + : entry.name + }` + ); - private _titleChanged(ev: PolymerChangedEvent) { + private _valueChanged(ev: CustomEvent) { this._error = undefined; - this._title = ev.detail.value; - if (!this.hass.userData?.showAdvanced) { - this._fillUrlPath(); + const value = ev.detail.value; + if (value.url_path !== this._data?.url_path) { + this._urlPathChanged = true; + if ( + !value.url_path || + value.url_path === "lovelace" || + !/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(value.url_path) + ) { + this._error = { + url_path: this.hass.localize( + "ui.panel.config.lovelace.dashboards.detail.url_error_msg" + ), + }; + } + } + if (value.title !== this._data?.title) { + this._data = value; + this._fillUrlPath(value.title); + } else { + this._data = value; } } - private _fillUrlPath() { - if ((this.hass.userData?.showAdvanced && this._urlPath) || !this._title) { + private _fillUrlPath(title: string) { + if ((this.hass.userData?.showAdvanced && this._urlPathChanged) || !title) { return; } - const slugifyTitle = slugify(this._title, "-"); - this._urlPath = slugifyTitle.includes("-") - ? slugifyTitle - : `lovelace-${slugifyTitle}`; - } - - private _showSidebarChanged(ev: Event) { - this._showInSidebar = (ev.target as HaSwitch).checked; - } - - private _requireAdminChanged(ev: Event) { - this._requireAdmin = (ev.target as HaSwitch).checked; + const slugifyTitle = slugify(title, "-"); + this._data = { + ...this._data, + url_path: slugifyTitle.includes("-") + ? slugifyTitle + : `lovelace-${slugifyTitle}`, + }; } private _toggleDefault() { @@ -273,29 +259,20 @@ export class DialogLovelaceDashboardDetail extends LitElement { private async _updateDashboard() { if (this._params?.urlPath && !this._params.dashboard?.id) { - this._close(); + this.closeDialog(); } this._submitting = true; try { - const values: Partial = { - require_admin: this._requireAdmin, - show_in_sidebar: this._showInSidebar, - icon: this._icon || undefined, - title: this._title, - }; if (this._params!.dashboard) { - await this._params!.updateDashboard(values); + await this._params!.updateDashboard(this._data as LovelaceDashboard); } else { - (values as LovelaceDashboardCreateParams).url_path = - this._urlPath.trim(); - (values as LovelaceDashboardCreateParams).mode = "storage"; await this._params!.createDashboard( - values as LovelaceDashboardCreateParams + this._data as LovelaceDashboardCreateParams ); } - this._close(); + this.closeDialog(); } catch (err: any) { - this._error = err?.message || "Unknown error"; + this._error = { base: err?.message || "Unknown error" }; } finally { this._submitting = false; } @@ -305,26 +282,15 @@ export class DialogLovelaceDashboardDetail extends LitElement { this._submitting = true; try { if (await this._params!.removeDashboard()) { - this._close(); + this.closeDialog(); } } finally { this._submitting = false; } } - private _close(): void { - this._params = undefined; - } - static get styles(): CSSResultGroup { - return [ - haStyleDialog, - css` - ha-switch { - padding: 16px 0; - } - `, - ]; + return [haStyleDialog, css``]; } } diff --git a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts index eb2ebe92d35b..283d1f29868d 100644 --- a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts +++ b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts @@ -1,21 +1,20 @@ import "@material/mwc-button/mwc-button"; -import "@material/mwc-list/mwc-list-item"; -import "@polymer/paper-input/paper-input"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-select"; -import { - LovelaceResource, - LovelaceResourcesMutableParams, -} from "../../../../data/lovelace"; -import { PolymerChangedEvent } from "../../../../polymer-types"; +import "../../../../components/ha-form/ha-form"; +import { HaFormSchema } from "../../../../components/ha-form/types"; +import { LovelaceResourcesMutableParams } from "../../../../data/lovelace"; import { haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail"; -const detectResourceType = (url: string) => { +const detectResourceType = (url?: string) => { + if (!url) { + return undefined; + } const ext = url.split(".").pop() || ""; if (ext === "css") { @@ -35,38 +34,41 @@ export class DialogLovelaceResourceDetail extends LitElement { @state() private _params?: LovelaceResourceDetailsDialogParams; - @state() private _url!: LovelaceResource["url"]; - - @state() private _type?: LovelaceResource["type"]; + @state() private _data?: Partial; - @state() private _error?: string; + @state() private _error?: Record; @state() private _submitting = false; - public async showDialog( - params: LovelaceResourceDetailsDialogParams - ): Promise { + public showDialog(params: LovelaceResourceDetailsDialogParams): void { this._params = params; this._error = undefined; if (this._params.resource) { - this._url = this._params.resource.url || ""; - this._type = this._params.resource.type || undefined; + this._data = { + url: this._params.resource.url, + res_type: this._params.resource.type, + }; } else { - this._url = ""; - this._type = undefined; + this._data = { + url: "", + }; } - await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render(): TemplateResult { if (!this._params) { return html``; } - const urlInvalid = this._url.trim() === ""; + const urlInvalid = !this._data?.url || this._data.url.trim() === ""; return html`
- ${this._error ? html`
${this._error}
` : ""} -
-

- ${this.hass!.localize( - "ui.panel.config.lovelace.resources.detail.warning_header" - )} -

+ ${this.hass!.localize( "ui.panel.config.lovelace.resources.detail.warning_text" )} - -
- - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.module" - )} - - ${this._type === "js" - ? html` - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.js" - )} - - ` - : ""} - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.css" - )} - - ${this._type === "html" - ? html` - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.html" - )} - - ` - : ""} - -
+ + +
${this._params.resource ? html` @@ -159,7 +118,7 @@ export class DialogLovelaceResourceDetail extends LitElement { ${this._params.resource ? this.hass!.localize( @@ -173,37 +132,86 @@ export class DialogLovelaceResourceDetail extends LitElement { `; } - private _urlChanged(ev: PolymerChangedEvent) { - this._error = undefined; - this._url = ev.detail.value; - if (!this._type) { - this._type = detectResourceType(this._url); + private _schema = memoizeOne((data) => [ + { + name: "url", + required: true, + selector: { + text: {}, + }, + }, + { + name: "res_type", + required: true, + selector: { + select: { + options: [ + { + value: "module", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.module" + ), + }, + { + value: "css", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.css" + ), + }, + data.type === "js" && { + value: "js", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.js" + ), + }, + data.type === "html" && { + value: "html", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.html" + ), + }, + ].filter(Boolean), + }, + }, + }, + ]); + + private _computeLabel = (entry: HaFormSchema): string => + this.hass.localize( + `ui.panel.config.lovelace.resources.detail.${entry.name}` + ); + + private _valueChanged(ev: CustomEvent) { + this._data = ev.detail.value; + if (!this._data!.res_type) { + const type = detectResourceType(this._data!.url); + if (!type) { + return; + } + this._data = { + ...this._data, + res_type: type, + }; } } - private _typeChanged(ev) { - this._type = ev.target.value; - } - private async _updateResource() { - if (!this._type) { + if (!this._data?.res_type) { return; } this._submitting = true; try { - const values: LovelaceResourcesMutableParams = { - url: this._url.trim(), - res_type: this._type, - }; if (this._params!.resource) { - await this._params!.updateResource(values); + await this._params!.updateResource(this._data!); } else { - await this._params!.createResource(values); + await this._params!.createResource( + this._data! as LovelaceResourcesMutableParams + ); } this._params = undefined; } catch (err: any) { - this._error = err?.message || "Unknown error"; + this._error = { base: err?.message || "Unknown error" }; } finally { this._submitting = false; } @@ -213,26 +221,15 @@ export class DialogLovelaceResourceDetail extends LitElement { this._submitting = true; try { if (await this._params!.removeResource()) { - this._close(); + this.closeDialog(); } } finally { this._submitting = false; } } - private _close(): void { - this._params = undefined; - } - static get styles(): CSSResultGroup { - return [ - haStyleDialog, - css` - .warning { - color: var(--error-color); - } - `, - ]; + return haStyleDialog; } } From 0936fd9ae407105cd4fc3cf98c5dfcb200c401a4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 4 Mar 2022 14:31:11 -0800 Subject: [PATCH 08/35] Guard setting up config flow for an unsupported domain (#11937) --- .../config-flow/dialog-data-entry-flow.ts | 13 ++++++++++--- .../integrations/ha-config-integrations.ts | 19 ++++++++++++++++++- src/translations/en.json | 1 + 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index 6a128d081a7c..6fbab75c745b 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -377,13 +377,20 @@ class DataEntryFlowDialog extends LitElement { step = await this._params!.flowConfig.createFlow(this.hass, handler); } catch (err: any) { this.closeDialog(); + const message = + err?.status_code === 404 + ? this.hass.localize( + "ui.panel.config.integrations.config_flow.no_config_flow" + ) + : `${this.hass.localize( + "ui.panel.config.integrations.config_flow.could_not_load" + )}: ${err?.body?.message || err?.message}`; + showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.error" ), - text: `${this.hass.localize( - "ui.panel.config.integrations.config_flow.could_not_load" - )}: ${err.message || err.body}`, + text: message, }); return; } finally { diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index f58a797a36ff..5255d1197e7d 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -30,6 +30,7 @@ import "../../../components/ha-check-list-item"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { ConfigEntry, getConfigEntries } from "../../../data/config_entries"; import { + getConfigFlowHandlers, getConfigFlowInProgressCollection, localizeConfigFlowTitle, subscribeConfigFlowInProgress, @@ -51,7 +52,10 @@ import { } from "../../../data/integration"; import { scanUSBDevices } from "../../../data/usb"; import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; -import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-tabs-subpage"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; @@ -652,6 +656,19 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { if (!domain) { return; } + const handlers = await getConfigFlowHandlers(this.hass); + + if (!handlers.includes(domain)) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_flow.error" + ), + text: this.hass.localize( + "ui.panel.config.integrations.config_flow.no_config_flow" + ), + }); + return; + } const localize = await localizePromise; if ( !(await showConfirmationDialog(this, { diff --git a/src/translations/en.json b/src/translations/en.json index f657f3e6dd65..b97fd52f093e 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2605,6 +2605,7 @@ "finish": "Finish", "submit": "Submit", "next": "Next", + "no_config_flow": "This integration does not support configuration via the UI. If you followed this link from the Home Assistant website, make sure you run the latest version of Home Assistant.", "not_all_required_fields": "Not all required fields are filled in.", "error_saving_area": "Error saving area: {error}", "created_config": "Created configuration for {name}.", From bd20c15a55a8de24f9f9a6922ac8f2990960fd1d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 4 Mar 2022 23:24:31 -0800 Subject: [PATCH 09/35] Show triggered vars on click (#11924) --- .../trigger/ha-automation-trigger-row.ts | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) 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 7844e7ba0978..60847b2c4311 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -16,12 +16,16 @@ import "../../../../components/ha-alert"; import "../../../../components/ha-button-menu"; import "../../../../components/ha-card"; import "../../../../components/ha-icon-button"; +import "../../../../components/ha-yaml-editor"; import "../../../../components/ha-select"; import type { HaSelect } from "../../../../components/ha-select"; import "../../../../components/ha-textfield"; import { subscribeTrigger, Trigger } from "../../../../data/automation"; import { validateConfig } from "../../../../data/config"; -import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import "./types/ha-automation-trigger-device"; @@ -94,7 +98,7 @@ export default class HaAutomationTriggerRow extends LitElement { @state() private _requestShowId = false; - @state() private _triggered = false; + @state() private _triggered?: Record; @state() private _triggerColor = false; @@ -231,9 +235,10 @@ export default class HaAutomationTriggerRow extends LitElement {
${this.hass.localize( "ui.panel.config.automation.editor.triggers.triggered" @@ -298,16 +303,16 @@ export default class HaAutomationTriggerRow extends LitElement { const triggerUnsub = subscribeTrigger( this.hass, - () => { + (result) => { if (untriggerTimeout !== undefined) { clearTimeout(untriggerTimeout); this._triggerColor = !this._triggerColor; } else { this._triggerColor = false; } - this._triggered = true; + this._triggered = result; untriggerTimeout = window.setTimeout(() => { - this._triggered = false; + this._triggered = undefined; untriggerTimeout = undefined; }, showTriggeredTime); }, @@ -416,6 +421,18 @@ export default class HaAutomationTriggerRow extends LitElement { this._yamlMode = !this._yamlMode; } + private _showTriggeredInfo() { + showAlertDialog(this, { + text: html` + + `, + }); + } + static get styles(): CSSResultGroup { return [ haStyle, @@ -426,12 +443,12 @@ export default class HaAutomationTriggerRow extends LitElement { --mdc-theme-text-primary-on-background: var(--primary-text-color); } .triggered { + cursor: pointer; position: absolute; top: 0px; right: 0px; left: 0px; text-transform: uppercase; - pointer-events: none; font-weight: bold; font-size: 14px; background-color: var(--primary-color); @@ -446,6 +463,9 @@ export default class HaAutomationTriggerRow extends LitElement { .triggered.active { max-height: 100px; } + .triggered:hover { + opacity: 0.8; + } .triggered.accent { background-color: var(--accent-color); color: var(--text-accent-color, var(--text-primary-color)); From ccb91e0b491ff00348670d54d1bdbda8f2ebfdde Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 Mar 2022 03:39:16 -0800 Subject: [PATCH 10/35] Allow marking YAML editor as read only (#11960) --- src/components/ha-yaml-editor.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ha-yaml-editor.ts b/src/components/ha-yaml-editor.ts index d315e804ef49..8f285cb43928 100644 --- a/src/components/ha-yaml-editor.ts +++ b/src/components/ha-yaml-editor.ts @@ -31,6 +31,8 @@ export class HaYamlEditor extends LitElement { @property() public label?: string; + @property({ type: Boolean }) public readOnly = false; + @state() private _yaml = ""; public setValue(value): void { @@ -61,6 +63,7 @@ export class HaYamlEditor extends LitElement { Date: Mon, 7 Mar 2022 12:45:39 +0100 Subject: [PATCH 11/35] Convert inputs (#11907) * Convert inputs * Update dialog-thingtalk.ts * imports --- hassio/src/components/hassio-upload-backup.ts | 1 - src/components/ha-date-input.ts | 2 +- src/components/ha-textfield.ts | 11 +++ src/components/search-input.ts | 2 +- src/dialogs/quick-bar/ha-quick-bar.ts | 2 +- .../ha-voice-command-dialog.ts | 29 ++++--- .../automation/blueprint-automation-editor.ts | 84 +++++++++++++------ .../automation/thingtalk/dialog-thingtalk.ts | 16 ++-- .../types/ha-automation-trigger-webhook.ts | 2 +- .../dialog-manage-cloudhook.ts | 40 ++++----- .../dialogs/dialog-energy-gas-settings.ts | 19 ++--- .../dialog-energy-grid-flow-settings.ts | 22 ++--- .../zwave_js/dialog-zwave_js-add-node.ts | 24 +++--- .../zwave_js/zwave_js-node-config.ts | 9 +- .../config/script/blueprint-script-editor.ts | 4 +- .../components/hui-input-list-editor.ts | 29 ++++--- .../config-elements/hui-map-card-editor.ts | 7 +- .../hui-picture-card-editor.ts | 1 - 18 files changed, 168 insertions(+), 136 deletions(-) diff --git a/hassio/src/components/hassio-upload-backup.ts b/hassio/src/components/hassio-upload-backup.ts index ae19a12c816c..d40345522ed6 100644 --- a/hassio/src/components/hassio-upload-backup.ts +++ b/hassio/src/components/hassio-upload-backup.ts @@ -1,5 +1,4 @@ import { mdiFolderUpload } from "@mdi/js"; -import "@polymer/paper-input/paper-input-container"; import { html, LitElement, TemplateResult } from "lit"; import { customElement, state } from "lit/decorators"; import { fireEvent } from "../../../src/common/dom/fire_event"; diff --git a/src/components/ha-date-input.ts b/src/components/ha-date-input.ts index 42308ee6c70e..7575e3bcef74 100644 --- a/src/components/ha-date-input.ts +++ b/src/components/ha-date-input.ts @@ -41,7 +41,7 @@ export class HaDateInput extends LitElement { return html` diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 3a7b3aaa9631..6135b6f4fd7c 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -162,7 +162,7 @@ export class QuickBar extends LitElement { "ui.dialogs.quick-bar.filter_placeholder" )} .value=${this._commandMode ? `>${this._search}` : this._search} - .icon=${true} + icon .iconTrailing=${this._search !== undefined || this._narrow} @input=${this._handleSearchChange} @keydown=${this._handleInputKeyDown} diff --git a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts index 4c8fa20780ee..d05e441781ce 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -1,7 +1,6 @@ /* eslint-disable lit/prefer-static-styles */ +import "@material/mwc-button/mwc-button"; import { mdiMicrophone } from "@mdi/js"; -import "@polymer/paper-input/paper-input"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, @@ -10,12 +9,16 @@ import { PropertyValues, TemplateResult, } from "lit"; -import { customElement, property, state, query } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../../common/dom/fire_event"; import { SpeechRecognition } from "../../common/dom/speech-recognition"; import { uid } from "../../common/util/uid"; +import "../../components/ha-dialog"; +import type { HaDialog } from "../../components/ha-dialog"; import "../../components/ha-icon-button"; +import "../../components/ha-textfield"; +import type { HaTextField } from "../../components/ha-textfield"; import { AgentInfo, getAgentInfo, @@ -24,9 +27,6 @@ import { } from "../../data/conversation"; import { haStyleDialog } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; -import "../../components/ha-dialog"; -import type { HaDialog } from "../../components/ha-dialog"; -import "@material/mwc-button/mwc-button"; interface Message { who: string; @@ -127,18 +127,19 @@ export class HaVoiceCommandDialog extends LitElement { : ""}
- ${SpeechRecognition ? html` - + ${this.results ? html`
@@ -155,7 +156,7 @@ export class HaVoiceCommandDialog extends LitElement { ` : ""} - + ${this._agentInfo && this._agentInfo.attribution ? html`
- - - + + ${this._showDescription + ? html` + + ` + : html` + + `}
${this.stateObj ? html` @@ -173,15 +199,14 @@ export class HaBlueprintAutomationEditor extends LitElement { value?.default} @value-changed=${this._inputChanged} >` - : html``} + @input=${this._inputChanged} + >`} ` ) : html`

@@ -221,7 +246,7 @@ export class HaBlueprintAutomationEditor extends LitElement { ev.stopPropagation(); const target = ev.target as any; const key = target.key; - const value = ev.detail.value; + const value = ev.detail?.value || target.value; if ( (this.config.use_blueprint.input && this.config.use_blueprint.input[key] === value) || @@ -262,6 +287,10 @@ export class HaBlueprintAutomationEditor extends LitElement { }); } + private _addDescription() { + this._showDescription = true; + } + static get styles(): CSSResultGroup { return [ haStyle, @@ -273,9 +302,16 @@ export class HaBlueprintAutomationEditor extends LitElement { .padding { padding: 16px; } + .link-button-row { + padding: 14px; + } .blueprint-picker-container { padding: 0 16px 16px; } + ha-textarea, + ha-textfield { + display: block; + } h3 { margin: 16px; } @@ -292,9 +328,7 @@ export class HaBlueprintAutomationEditor extends LitElement { --paper-time-input-justify-content: flex-end; border-top: 1px solid var(--divider-color); } - :host(:not([narrow])) ha-settings-row paper-input { - width: 60%; - } + :host(:not([narrow])) ha-settings-row ha-textfield, :host(:not([narrow])) ha-settings-row ha-selector { width: 60%; } diff --git a/src/panels/config/automation/thingtalk/dialog-thingtalk.ts b/src/panels/config/automation/thingtalk/dialog-thingtalk.ts index 46cc842c5a1d..8eae2ce4b6c4 100644 --- a/src/panels/config/automation/thingtalk/dialog-thingtalk.ts +++ b/src/panels/config/automation/thingtalk/dialog-thingtalk.ts @@ -1,10 +1,11 @@ import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state, query } from "lit/decorators"; +import { customElement, property, query, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-circular-progress"; +import "../../../../components/ha-dialog"; +import "../../../../components/ha-textfield"; +import type { HaTextField } from "../../../../components/ha-textfield"; import type { AutomationConfig } from "../../../../data/automation"; import { convertThingTalk } from "../../../../data/cloud"; import { haStyle, haStyleDialog } from "../../../../resources/styles"; @@ -12,7 +13,6 @@ import type { HomeAssistant } from "../../../../types"; import "./ha-thingtalk-placeholders"; import type { PlaceholderValues } from "./ha-thingtalk-placeholders"; import type { ThingtalkDialogParams } from "./show-dialog-thingtalk"; -import "../../../../components/ha-dialog"; export interface Placeholder { name: string; @@ -38,7 +38,7 @@ class DialogThingtalk extends LitElement { @state() private _placeholders?: PlaceholderContainer; - @query("#input") private _input?: PaperInputElement; + @query("#input") private _input?: HaTextField; private _value?: string; @@ -58,7 +58,7 @@ class DialogThingtalk extends LitElement { this._placeholders = undefined; this._params = undefined; if (this._input) { - this._input.value = null; + this._input.value = ""; } fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -127,13 +127,13 @@ class DialogThingtalk extends LitElement { - + > diff --git a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts index b018af295935..1dc48f780fe7 100644 --- a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts +++ b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts @@ -1,11 +1,12 @@ import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement } from "lit"; -import { state } from "lit/decorators"; +import { query, state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; +import { copyToClipboard } from "../../../../common/util/copy-clipboard"; +import type { HaTextField } from "../../../../components/ha-textfield"; +import "../../../../components/ha-textfield"; import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; -import { haStyle } from "../../../../resources/styles"; +import { haStyle, haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import { documentationUrl } from "../../../../util/documentation-url"; import { WebhookDialogParams } from "./show-dialog-manage-cloudhook"; @@ -17,6 +18,8 @@ export class DialogManageCloudhook extends LitElement { @state() private _params?: WebhookDialogParams; + @query("ha-textfield") _input!: HaTextField; + public showDialog(params: WebhookDialogParams) { this._params = params; } @@ -53,12 +56,12 @@ export class DialogManageCloudhook extends LitElement { "ui.panel.config.cloud.dialog_cloudhook.available_at" )}

- + >

${cloudhook.managed ? html` @@ -98,10 +101,6 @@ export class DialogManageCloudhook extends LitElement { `; } - private get _paperInput(): PaperInputElement { - return this.shadowRoot!.querySelector("paper-input")!; - } - private async _disableWebhook() { showConfirmationDialog(this, { text: this.hass!.localize( @@ -117,14 +116,10 @@ export class DialogManageCloudhook extends LitElement { } private _copyClipboard(ev: FocusEvent) { - // paper-input -> iron-input -> input - const paperInput = ev.currentTarget as PaperInputElement; - const input = (paperInput.inputElement as any) - .inputElement as HTMLInputElement; - input.setSelectionRange(0, input.value.length); + const textField = ev.currentTarget as HaTextField; try { - document.execCommand("copy"); - paperInput.label = this.hass!.localize( + copyToClipboard(textField.value); + textField.label = this.hass!.localize( "ui.panel.config.cloud.dialog_cloudhook.copied_to_clipboard" ); } catch (err: any) { @@ -133,18 +128,19 @@ export class DialogManageCloudhook extends LitElement { } private _restoreLabel() { - this._paperInput.label = inputLabel; + this._input.label = inputLabel; } static get styles(): CSSResultGroup { return [ haStyle, + haStyleDialog, css` ha-dialog { width: 650px; } - paper-input { - margin-top: -8px; + ha-textfield { + display: block; } button.link { color: var(--primary-color); diff --git a/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts index b180c5f15107..8a3b57d4271c 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts @@ -19,6 +19,7 @@ import "../../../../components/entity/ha-statistic-picker"; import "../../../../components/entity/ha-entity-picker"; import "../../../../components/ha-radio"; import "../../../../components/ha-formfield"; +import "../../../../components/ha-textfield"; import type { HaRadio } from "../../../../components/ha-radio"; @customElement("dialog-energy-gas-settings") @@ -188,20 +189,19 @@ export class DialogEnergyGasSettings > ${this._costs === "number" - ? html` - ${this.hass.config.currency}/${unit} - ` + ` : ""} @@ -223,10 +223,10 @@ export class DialogEnergyGasSettings this._costs = input.value as any; } - private _numberPriceChanged(ev: CustomEvent) { + private _numberPriceChanged(ev) { this._source = { ...this._source!, - number_energy_price: Number(ev.detail.value), + number_energy_price: Number(ev.target.value), entity_energy_price: null, stat_cost: null, }; @@ -295,13 +295,10 @@ export class DialogEnergyGasSettings ha-formfield { display: block; } - ha-statistic-picker { - width: 100%; - } .price-options { display: block; padding-left: 52px; - margin-top: -16px; + margin-top: -8px; } `, ]; diff --git a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts index d32e57f50557..2599f61ee3f2 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts @@ -190,24 +190,21 @@ export class DialogEnergyGridFlowSettings > ${this._costs === "number" - ? html` - ${this.hass.localize( - `ui.panel.config.energy.grid.flow_dialog.${this._params.direction}.cost_number_suffix`, - { currency: this.hass.config.currency } - )} - ` + ` : ""} @@ -302,13 +299,10 @@ export class DialogEnergyGridFlowSettings ha-formfield { display: block; } - ha-statistic-picker { - width: 100%; - } .price-options { display: block; padding-left: 52px; - margin-top: -16px; + margin-top: -8px; } `, ]; 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 4c9603ba0a94..c1a75807c737 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 @@ -1,7 +1,5 @@ import "@material/mwc-button/mwc-button"; import { mdiAlertCircle, mdiCheckCircle, mdiQrcodeScan } from "@mdi/js"; -import "@polymer/paper-input/paper-input"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, query, state } from "lit/decorators"; @@ -11,28 +9,30 @@ import { HaCheckbox } from "../../../../../components/ha-checkbox"; import "../../../../../components/ha-circular-progress"; import { createCloseHeading } from "../../../../../components/ha-dialog"; import "../../../../../components/ha-formfield"; +import "../../../../../components/ha-qr-scanner"; import "../../../../../components/ha-radio"; import "../../../../../components/ha-switch"; +import "../../../../../components/ha-textfield"; +import type { HaTextField } from "../../../../../components/ha-textfield"; import { - zwaveGrantSecurityClasses, InclusionStrategy, MINIMUM_QR_STRING_LENGTH, - zwaveParseQrCode, + PlannedProvisioningEntry, provisionZwaveSmartStartNode, QRProvisioningInformation, RequestedGrant, SecurityClass, stopZwaveInclusion, subscribeAddZwaveNode, + ZWaveFeature, + zwaveGrantSecurityClasses, + zwaveParseQrCode, zwaveSupportsFeature, zwaveValidateDskAndEnterPin, - ZWaveFeature, - PlannedProvisioningEntry, } from "../../../../../data/zwave_js"; import { haStyle, haStyleDialog } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; import { ZWaveJSAddNodeDialogParams } from "./show-dialog-zwave_js-add-node"; -import "../../../../../components/ha-qr-scanner"; export interface ZWaveJSAddNodeDevice { id: string; @@ -98,7 +98,7 @@ class DialogZWaveJSAddNode extends LitElement { this._startInclusion(); } - @query("#pin-input") private _pinInput?: PaperInputElement; + @query("#pin-input") private _pinInput?: HaTextField; protected render(): TemplateResult { if (!this._entryId) { @@ -202,12 +202,11 @@ class DialogZWaveJSAddNode extends LitElement { : "" }

- + > ${this._dsk}
${item.metadata.unit ? html`${item.metadata.unit}` : ""} - `; + `; } if (item.configuration_value_type === "enumerated") { @@ -492,7 +493,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) { font-size: 1.3em; } - :host(:not([narrow])) ha-settings-row paper-input { + :host(:not([narrow])) ha-settings-row ha-textfield { width: 30%; text-align: right; } diff --git a/src/panels/config/script/blueprint-script-editor.ts b/src/panels/config/script/blueprint-script-editor.ts index 738fdb841937..6edae7d2cc02 100644 --- a/src/panels/config/script/blueprint-script-editor.ts +++ b/src/panels/config/script/blueprint-script-editor.ts @@ -186,9 +186,7 @@ export class HaBlueprintScriptEditor extends LitElement { --paper-time-input-justify-content: flex-end; border-top: 1px solid var(--divider-color); } - :host(:not([narrow])) ha-settings-row paper-input { - width: 60%; - } + :host(:not([narrow])) ha-settings-row ha-textfield, :host(:not([narrow])) ha-settings-row ha-selector { width: 60%; } diff --git a/src/panels/lovelace/components/hui-input-list-editor.ts b/src/panels/lovelace/components/hui-input-list-editor.ts index fe53e72c111c..a7c6ea75e05e 100644 --- a/src/panels/lovelace/components/hui-input-list-editor.ts +++ b/src/panels/lovelace/components/hui-input-list-editor.ts @@ -1,11 +1,11 @@ import { mdiClose } from "@mdi/js"; -import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-icon-button"; import { HomeAssistant } from "../../../types"; import { EditorTarget } from "../editor/types"; +import "../../../components/ha-textfield"; @customElement("hui-input-list-editor") export class HuiInputListEditor extends LitElement { @@ -23,30 +23,31 @@ export class HuiInputListEditor extends LitElement { return html` ${this.value.map( (listEntry, index) => html` - Clear + > + + ` )} - + > `; } @@ -103,10 +104,12 @@ export class HuiInputListEditor extends LitElement { static get styles(): CSSResultGroup { return css` ha-icon-button { - --mdc-icon-button-size: 24px; - padding: 2px; + margin-right: -24px; color: var(--secondary-text-color); } + ha-textfield { + display: block; + } `; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts index 50c8fd65d37a..ea0741ae3363 100644 --- a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts @@ -1,5 +1,4 @@ import "../../../../components/ha-form/ha-form"; -import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { @@ -24,7 +23,7 @@ import { EntityConfig } from "../../entity-rows/types"; import { LovelaceCardEditor } from "../../types"; import { processEditorEntities } from "../process-editor-entities"; import { entitiesConfigStruct } from "../structs/entities-struct"; -import { EditorTarget, EntitiesEditorEvent } from "../types"; +import { EntitiesEditorEvent } from "../types"; import { configElementStyle } from "./config-elements-style"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { HaFormSchema } from "../../../../components/ha-form/types"; @@ -127,10 +126,6 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor { if (!this._config || !this.hass) { return; } - const target = ev.target! as EditorTarget; - if (!target.configValue) { - return; - } const value = ev.detail.value; diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts index 53288a843bbc..9b1f46f96b9e 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts @@ -1,4 +1,3 @@ -import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { assert, object, optional, string, assign } from "superstruct"; From b4b52d387286363d4c08a9d6cc28c4eb3d3d13a9 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 7 Mar 2022 06:49:51 -0500 Subject: [PATCH 12/35] Remove some additional old zwave code (#11941) --- src/common/entity/domain_icon.ts | 16 ---------------- src/common/style/icon_color_css.ts | 3 --- 2 files changed, 19 deletions(-) diff --git a/src/common/entity/domain_icon.ts b/src/common/entity/domain_icon.ts index af56f1135b1c..b64d93f8a04b 100644 --- a/src/common/entity/domain_icon.ts +++ b/src/common/entity/domain_icon.ts @@ -9,7 +9,6 @@ import { mdiCast, mdiCastConnected, mdiClock, - mdiEmoticonDead, mdiFlash, mdiGestureTapButton, mdiLanConnect, @@ -22,14 +21,11 @@ import { mdiPowerPlug, mdiPowerPlugOff, mdiRestart, - mdiSleep, - mdiTimerSand, mdiToggleSwitch, mdiToggleSwitchOff, mdiCheckCircleOutline, mdiCloseCircleOutline, mdiWeatherNight, - mdiZWave, } from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; /** @@ -115,18 +111,6 @@ export const domainIcon = ( return mdiFlash; } - case "zwave": - switch (compareState) { - case "dead": - return mdiEmoticonDead; - case "sleeping": - return mdiSleep; - case "initializing": - return mdiTimerSand; - default: - return mdiZWave; - } - case "sensor": { const icon = sensorIcon(stateObj); if (icon) { diff --git a/src/common/style/icon_color_css.ts b/src/common/style/icon_color_css.ts index 7e00bf1c77ed..c4d4c7b9ee3d 100644 --- a/src/common/style/icon_color_css.ts +++ b/src/common/style/icon_color_css.ts @@ -70,9 +70,6 @@ export const iconColorCSS = css` } ha-state-icon[data-domain="plant"][data-state="problem"], - ha-state-icon[data-domain="zwave"][data-state="dead"] { - color: var(--state-icon-error-color); - } /* Color the icon if unavailable */ ha-state-icon[data-state="unavailable"] { From 2e10eb04b65ae1b88e41e0946f770e956e9a7072 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Mon, 7 Mar 2022 13:08:54 +0100 Subject: [PATCH 13/35] Correct media upload error + add file name (#11949) Co-authored-by: Bram Kragten --- src/data/image.ts | 2 +- src/data/media_source.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/image.ts b/src/data/image.ts index 497f2ffa3ab8..94198de6dc87 100644 --- a/src/data/image.ts +++ b/src/data/image.ts @@ -29,7 +29,7 @@ export const createImage = async ( body: fd, }); if (resp.status === 413) { - throw new Error("Uploaded image is too large"); + throw new Error(`Uploaded image is too large (${file.name})`); } else if (resp.status !== 200) { throw new Error("Unknown error"); } diff --git a/src/data/media_source.ts b/src/data/media_source.ts index 61494ee3f808..a6e93d83cf93 100644 --- a/src/data/media_source.ts +++ b/src/data/media_source.ts @@ -43,7 +43,7 @@ export const uploadLocalMedia = async ( } ); if (resp.status === 413) { - throw new Error("Uploaded image is too large"); + throw new Error(`Uploaded file is too large (${file.name})`); } else if (resp.status !== 200) { throw new Error("Unknown error"); } From dfcb0f6ba0409a23edf7a6fa893697eb668535c8 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Mar 2022 14:25:38 +0100 Subject: [PATCH 14/35] Fix humidifier more info mode dropdown (#11964) --- .../controls/more-info-humidifier.ts | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-humidifier.ts b/src/dialogs/more-info/controls/more-info-humidifier.ts index bce976b45bb3..b5618e8619ed 100644 --- a/src/dialogs/more-info/controls/more-info-humidifier.ts +++ b/src/dialogs/more-info/controls/more-info-humidifier.ts @@ -1,3 +1,4 @@ +import "@material/mwc-list/mwc-list-item"; import { css, CSSResultGroup, @@ -12,6 +13,7 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import { supportsFeature } from "../../../common/entity/supports-feature"; import { computeRTLDirection } from "../../../common/util/compute_rtl"; +import "../../../components/ha-select"; import "../../../components/ha-slider"; import "../../../components/ha-switch"; import { @@ -19,8 +21,6 @@ import { HUMIDIFIER_SUPPORT_MODES, } from "../../../data/humidifier"; import { HomeAssistant } from "../../../types"; -import "@material/mwc-list/mwc-list"; -import "@material/mwc-list/mwc-list-item"; class MoreInfoHumidifier extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -67,26 +67,24 @@ class MoreInfoHumidifier extends LitElement { ${supportModes ? html` -
- - ${stateObj.attributes.available_modes!.map( - (mode) => html` - - ${hass.localize( - `state_attributes.humidifier.mode.${mode}` - ) || mode} - - ` - )} - -
+ + ${stateObj.attributes.available_modes!.map( + (mode) => html` + + ${hass.localize( + `state_attributes.humidifier.mode.${mode}` + ) || mode} + + ` + )} + ` : ""}
From 8258641443b576a69a38f01dc76ead47b5364ec6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Mar 2022 14:55:44 +0100 Subject: [PATCH 15/35] Make min width of select configurable (#11965) --- src/components/ha-select.ts | 3 +++ src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/src/components/ha-select.ts b/src/components/ha-select.ts index 59269bde4a05..b90133b8eba5 100644 --- a/src/components/ha-select.ts +++ b/src/components/ha-select.ts @@ -44,6 +44,9 @@ export class HaSelect extends SelectBase { .mdc-select:not(.mdc-select--disabled) .mdc-select__icon { color: var(--secondary-text-color); } + .mdc-select__anchor { + width: var(--ha-select-min-width, 200px); + } `, ]; } diff --git a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts index 0b8845212957..1db8a8c1b679 100644 --- a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts @@ -87,6 +87,7 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow { } ha-select { width: 100%; + --ha-select-min-width: 0; } `; From 9d28df31bd1e5788c98ae3115fb91a2b09b3d3d1 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Mon, 7 Mar 2022 08:29:00 -0600 Subject: [PATCH 16/35] Fix for Statistics Editor (#11942) Co-authored-by: Bram Kragten --- .../config-elements/hui-statistics-graph-card-editor.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts index b68ecaebdae6..c695e56b5490 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistics-graph-card-editor.ts @@ -153,7 +153,7 @@ export class HuiStatisticsGraphCardEditor .pickedStatisticLabel=${`Statistic`} .value=${this._configEntities} .configValue=${"entities"} - @value-changed=${this._valueChanged} + @value-changed=${this._entitiesChanged} >
`; @@ -163,6 +163,12 @@ export class HuiStatisticsGraphCardEditor fireEvent(this, "config-changed", { config: ev.detail.value }); } + private _entitiesChanged(ev: CustomEvent): void { + fireEvent(this, "config-changed", { + config: { ...this._config!, entities: ev.detail.value }, + }); + } + private _computeLabelCallback = (schema: HaFormSchema) => this.hass!.localize( `ui.panel.lovelace.editor.card.generic.${schema.name}` From 4b8b14a69dfa677907ab79f0fb0dfcf73df73dea Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Mar 2022 15:40:19 +0100 Subject: [PATCH 17/35] A11y expansion panel (#11967) --- src/components/ha-expansion-panel.ts | 54 +++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/components/ha-expansion-panel.ts b/src/components/ha-expansion-panel.ts index 705b5a6b81be..b3dc414f1e2f 100644 --- a/src/components/ha-expansion-panel.ts +++ b/src/components/ha-expansion-panel.ts @@ -1,6 +1,13 @@ import { mdiChevronDown } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, query } from "lit/decorators"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../common/dom/fire_event"; import { nextRender } from "../common/util/render-status"; @@ -16,11 +23,21 @@ class HaExpansionPanel extends LitElement { @property() secondary?: string; + @state() _showContent = this.expanded; + @query(".container") private _container!: HTMLDivElement; protected render(): TemplateResult { return html` -
+
${this.header} ${this.secondary} @@ -33,21 +50,37 @@ class HaExpansionPanel extends LitElement {
- + ${this._showContent ? html`` : ""}
`; } + protected willUpdate(changedProps: PropertyValues) { + if (changedProps.has("expanded") && this.expanded) { + this._showContent = this.expanded; + } + } + private _handleTransitionEnd() { this._container.style.removeProperty("height"); + this._showContent = this.expanded; } - private async _toggleContainer(): Promise { + private async _toggleContainer(ev): Promise { + if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") { + return; + } + ev.preventDefault(); const newExpanded = !this.expanded; fireEvent(this, "expanded-will-change", { expanded: newExpanded }); if (newExpanded) { + this._showContent = true; // allow for dynamic content to be rendered await nextRender(); } @@ -80,17 +113,21 @@ class HaExpansionPanel extends LitElement { var(--divider-color, #e0e0e0) ); border-radius: var(--ha-card-border-radius, 4px); - padding: 0 8px; } - .summary { + #summary { display: flex; - padding: var(--expansion-panel-summary-padding, 0); + padding: var(--expansion-panel-summary-padding, 0 8px); min-height: 48px; align-items: center; cursor: pointer; overflow: hidden; font-weight: 500; + outline: none; + } + + #summary:focus { + background: var(--input-fill-color); } .summary-icon { @@ -103,6 +140,7 @@ class HaExpansionPanel extends LitElement { } .container { + padding: var(--expansion-panel-content-padding, 0 8px); overflow: hidden; transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); height: 0px; From 0dac10aa23a6d6d5ac65d1de430755960af6c357 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Mar 2022 15:42:40 +0100 Subject: [PATCH 18/35] Convert file upload to mdc (#11906) --- src/components/ha-file-upload.ts | 211 +++++++++++------- .../config/person/dialog-person-detail.ts | 3 + 2 files changed, 129 insertions(+), 85 deletions(-) diff --git a/src/components/ha-file-upload.ts b/src/components/ha-file-upload.ts index 31dd853e06fc..9c81a3f17a0c 100644 --- a/src/components/ha-file-upload.ts +++ b/src/components/ha-file-upload.ts @@ -1,6 +1,5 @@ +import { styles } from "@material/mwc-textfield/mwc-textfield.css"; import { mdiClose } from "@mdi/js"; -import "@polymer/iron-input/iron-input"; -import "@polymer/paper-input/paper-input-container"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -21,7 +20,7 @@ export class HaFileUpload extends LitElement { @property() public accept!: string; - @property() public icon!: string; + @property() public icon?: string; @property() public label!: string; @@ -39,15 +38,7 @@ export class HaFileUpload extends LitElement { protected firstUpdated(changedProperties: PropertyValues) { super.firstUpdated(changedProperties); if (this.autoOpenFileDialog) { - this._input?.click(); - } - } - - protected updated(changedProperties: PropertyValues) { - if (changedProperties.has("_drag") && !this.uploading) { - ( - this.shadowRoot!.querySelector("paper-input-container") as any - )._setFocused(this._drag); + this._openFilePicker(); } } @@ -60,51 +51,75 @@ export class HaFileUpload extends LitElement { active >` : html` - `} `; } + private _openFilePicker() { + this._input?.click(); + } + private _handleDrop(ev: DragEvent) { ev.preventDefault(); ev.stopPropagation(); @@ -137,40 +152,66 @@ export class HaFileUpload extends LitElement { } static get styles() { - return css` - paper-input-container { - position: relative; - padding: 8px; - margin: 0 -8px; - } - paper-input-container.dragged:before { - position: var(--layout-fit_-_position); - top: var(--layout-fit_-_top); - right: var(--layout-fit_-_right); - bottom: var(--layout-fit_-_bottom); - left: var(--layout-fit_-_left); - background: currentColor; - content: ""; - opacity: var(--dark-divider-opacity); - pointer-events: none; - border-radius: 4px; - } - input.file { - display: none; - } - img { - max-width: 125px; - max-height: 125px; - } - ha-icon-button { - --mdc-icon-button-size: 24px; - --mdc-icon-size: 20px; - } - ha-circular-progress { - display: block; - text-align-last: center; - } - `; + return [ + styles, + css` + :host { + display: block; + } + .mdc-text-field--filled { + height: auto; + padding-top: 16px; + cursor: pointer; + } + .mdc-text-field--filled.mdc-text-field--with-trailing-icon { + padding-top: 28px; + } + .mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon { + color: var(--secondary-text-color); + } + .mdc-text-field--filled.mdc-text-field--with-trailing-icon + .mdc-text-field__icon { + align-self: flex-end; + } + .mdc-text-field__icon--leading { + margin-bottom: 12px; + } + .mdc-text-field--filled .mdc-floating-label--float-above { + transform: scale(0.75); + top: 8px; + } + .dragged:before { + position: var(--layout-fit_-_position); + top: var(--layout-fit_-_top); + right: var(--layout-fit_-_right); + bottom: var(--layout-fit_-_bottom); + left: var(--layout-fit_-_left); + background: currentColor; + content: ""; + opacity: var(--dark-divider-opacity); + pointer-events: none; + border-radius: 4px; + } + .value { + width: 100%; + } + input.file { + display: none; + } + img { + max-width: 100%; + max-height: 125px; + } + ha-icon-button { + --mdc-icon-button-size: 24px; + --mdc-icon-size: 20px; + } + ha-circular-progress { + display: block; + text-align-last: center; + } + `, + ]; } } diff --git a/src/panels/config/person/dialog-person-detail.ts b/src/panels/config/person/dialog-person-detail.ts index 185056404fbb..9406d469f862 100644 --- a/src/panels/config/person/dialog-person-detail.ts +++ b/src/panels/config/person/dialog-person-detail.ts @@ -464,6 +464,9 @@ class DialogPersonDetail extends LitElement { ha-textfield { display: block; } + ha-picture-upload { + margin-top: 16px; + } ha-formfield { display: block; padding: 16px 0; From 4cdff3faeaa57423c38b87b7e371e4e58eae59aa Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Mar 2022 15:47:20 +0100 Subject: [PATCH 19/35] Add location selector, convert zone editor (#11902) --- gallery/src/pages/components/ha-form.ts | 5 + gallery/src/pages/components/ha-selector.ts | 5 + src/components/ha-form/ha-form-constant.ts | 4 +- src/components/ha-form/ha-form.ts | 4 +- src/components/ha-form/types.ts | 2 +- .../ha-selector/ha-selector-location.ts | 80 +++++ src/components/ha-selector/ha-selector.ts | 1 + src/data/selector.ts | 13 +- src/data/zone.ts | 6 +- src/panels/config/zone/dialog-zone-detail.ts | 310 +++++++----------- 10 files changed, 226 insertions(+), 204 deletions(-) create mode 100644 src/components/ha-selector/ha-selector-location.ts diff --git a/gallery/src/pages/components/ha-form.ts b/gallery/src/pages/components/ha-form.ts index b6ff6d1711c8..0722040cc002 100644 --- a/gallery/src/pages/components/ha-form.ts +++ b/gallery/src/pages/components/ha-form.ts @@ -38,6 +38,7 @@ const SCHEMAS: { select: "Select", icon: "Icon", media: "Media", + location: "Location", }, schema: [ { name: "addon", selector: { addon: {} } }, @@ -75,6 +76,10 @@ const SCHEMAS: { media: {}, }, }, + { + name: "location", + selector: { location: { radius: true, icon: "mdi:home" } }, + }, ], }, { diff --git a/gallery/src/pages/components/ha-selector.ts b/gallery/src/pages/components/ha-selector.ts index ea4a89201521..600176d02491 100644 --- a/gallery/src/pages/components/ha-selector.ts +++ b/gallery/src/pages/components/ha-selector.ts @@ -168,6 +168,11 @@ const SCHEMAS: { }, icon: { name: "Icon", selector: { icon: {} } }, media: { name: "Media", selector: { media: {} } }, + location: { name: "Location", selector: { location: {} } }, + location_radius: { + name: "Location with radius", + selector: { location: { radius: true, icon: "mdi:home" } }, + }, }, }, ]; diff --git a/src/components/ha-form/ha-form-constant.ts b/src/components/ha-form/ha-form-constant.ts index f20c057dfc5d..c1b6c67e9e1f 100644 --- a/src/components/ha-form/ha-form-constant.ts +++ b/src/components/ha-form/ha-form-constant.ts @@ -9,7 +9,9 @@ export class HaFormConstant extends LitElement implements HaFormElement { @property() public label!: string; protected render(): TemplateResult { - return html`${this.label}: ${this.schema.value}`; + return html`${this.label}${this.schema.value + ? `: ${this.schema.value}` + : ""}`; } static get styles(): CSSResultGroup { diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index c824d8fb89ca..e1e87d86f90f 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -25,6 +25,8 @@ import { HomeAssistant } from "../../types"; const getValue = (obj, item) => obj ? (!item.name ? obj : obj[item.name]) : null; +const getError = (obj, item) => (obj && item.name ? obj[item.name] : null); + let selectorImported = false; @customElement("ha-form") @@ -84,7 +86,7 @@ export class HaForm extends LitElement implements HaFormElement { ` : ""} ${this.schema.map((item) => { - const error = getValue(this.error, item); + const error = getError(this.error, item); return html` ${error diff --git a/src/components/ha-form/types.ts b/src/components/ha-form/types.ts index fc45bc3ef1fa..2c3a9912d4d6 100644 --- a/src/components/ha-form/types.ts +++ b/src/components/ha-form/types.ts @@ -40,7 +40,7 @@ export interface HaFormSelector extends HaFormBaseSchema { export interface HaFormConstantSchema extends HaFormBaseSchema { type: "constant"; - value: string; + value?: string; } export interface HaFormIntegerSchema extends HaFormBaseSchema { diff --git a/src/components/ha-selector/ha-selector-location.ts b/src/components/ha-selector/ha-selector-location.ts new file mode 100644 index 000000000000..18372f525def --- /dev/null +++ b/src/components/ha-selector/ha-selector-location.ts @@ -0,0 +1,80 @@ +import { html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../common/dom/fire_event"; +import type { + LocationSelector, + LocationSelectorValue, +} from "../../data/selector"; +import "../../panels/lovelace/components/hui-theme-select-editor"; +import type { HomeAssistant } from "../../types"; +import type { MarkerLocation } from "../map/ha-locations-editor"; +import "../map/ha-locations-editor"; + +@customElement("ha-selector-location") +export class HaLocationSelector extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public selector!: LocationSelector; + + @property() public value?: LocationSelectorValue; + + @property() public label?: string; + + @property({ type: Boolean, reflect: true }) public disabled = false; + + protected render() { + return html` + + `; + } + + private _location = memoizeOne( + ( + selector: LocationSelector, + value?: LocationSelectorValue + ): MarkerLocation[] => { + const computedStyles = getComputedStyle(this); + const zoneRadiusColor = selector.location.radius + ? computedStyles.getPropertyValue("--zone-radius-color") || + computedStyles.getPropertyValue("--accent-color") + : undefined; + return [ + { + id: "location", + latitude: value?.latitude || this.hass.config.latitude, + longitude: value?.longitude || this.hass.config.longitude, + radius: selector.location.radius ? value?.radius || 1000 : undefined, + radius_color: zoneRadiusColor, + icon: selector.location.icon, + location_editable: true, + radius_editable: true, + }, + ]; + } + ); + + private _locationChanged(ev: CustomEvent) { + const [latitude, longitude] = ev.detail.location; + fireEvent(this, "value-changed", { + value: { ...this.value, latitude, longitude }, + }); + } + + private _radiusChanged(ev: CustomEvent) { + const radius = ev.detail.radius; + fireEvent(this, "value-changed", { value: { ...this.value, radius } }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-selector-location": HaLocationSelector; + } +} diff --git a/src/components/ha-selector/ha-selector.ts b/src/components/ha-selector/ha-selector.ts index 5f6a31aebcbf..61b790cd980b 100644 --- a/src/components/ha-selector/ha-selector.ts +++ b/src/components/ha-selector/ha-selector.ts @@ -20,6 +20,7 @@ import "./ha-selector-time"; import "./ha-selector-icon"; import "./ha-selector-media"; import "./ha-selector-theme"; +import "./ha-selector-location"; @customElement("ha-selector") export class HaSelector extends LitElement { diff --git a/src/data/selector.ts b/src/data/selector.ts index d8cb9a4e63e7..c8cc87b36e69 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -15,7 +15,8 @@ export type Selector = | SelectSelector | IconSelector | MediaSelector - | ThemeSelector; + | ThemeSelector + | LocationSelector; export interface EntitySelector { entity: { @@ -164,6 +165,16 @@ export interface MediaSelector { media: {}; } +export interface LocationSelector { + location: { radius?: boolean; icon?: string }; +} + +export interface LocationSelectorValue { + latitude: number; + longitude: number; + radius?: number; +} + export interface MediaSelectorValue { entity_id?: string; media_content_id?: string; diff --git a/src/data/zone.ts b/src/data/zone.ts index a4e091c30eb7..52ea733317b4 100644 --- a/src/data/zone.ts +++ b/src/data/zone.ts @@ -12,12 +12,12 @@ export interface Zone { } export interface ZoneMutableParams { + name: string; icon?: string; latitude: number; longitude: number; - name: string; - passive: boolean; - radius: number; + passive?: boolean; + radius?: number; } export const fetchZones = (hass: HomeAssistant) => diff --git a/src/panels/config/zone/dialog-zone-detail.ts b/src/panels/config/zone/dialog-zone-detail.ts index 81c3e7880791..07b9fcb4fad2 100644 --- a/src/panels/config/zone/dialog-zone-detail.ts +++ b/src/panels/config/zone/dialog-zone-detail.ts @@ -1,17 +1,12 @@ import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../common/dom/fire_event"; import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord"; -import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { createCloseHeading } from "../../../components/ha-dialog"; -import "../../../components/ha-formfield"; -import "../../../components/ha-icon-picker"; -import "../../../components/ha-switch"; -import "../../../components/map/ha-locations-editor"; -import type { MarkerLocation } from "../../../components/map/ha-locations-editor"; +import "../../../components/ha-form/ha-form"; +import { HaFormSchema } from "../../../components/ha-form/types"; import { getZoneEditorInitData, ZoneMutableParams } from "../../../data/zone"; import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; @@ -20,19 +15,9 @@ import { ZoneDetailDialogParams } from "./show-dialog-zone-detail"; class DialogZoneDetail extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private _name!: string; + @state() private _error?: Record; - @state() private _icon!: string; - - @state() private _latitude!: number; - - @state() private _longitude!: number; - - @state() private _passive!: boolean; - - @state() private _radius!: number; - - @state() private _error?: string; + @state() private _data?: ZoneMutableParams; @state() private _params?: ZoneDetailDialogParams; @@ -42,13 +27,7 @@ class DialogZoneDetail extends LitElement { this._params = params; this._error = undefined; if (this._params.entry) { - this._name = this._params.entry.name || ""; - this._icon = this._params.entry.icon || ""; - this._latitude = this._params.entry.latitude || this.hass.config.latitude; - this._longitude = - this._params.entry.longitude || this.hass.config.longitude; - this._passive = this._params.entry.passive || false; - this._radius = this._params.entry.radius || 100; + this._data = this._params.entry; } else { const initConfig = getZoneEditorInitData(); let movedHomeLocation; @@ -59,30 +38,34 @@ class DialogZoneDetail extends LitElement { Math.random() * 500 * (Math.random() < 0.5 ? -1 : 1) ); } - this._latitude = initConfig?.latitude || movedHomeLocation[0]; - this._longitude = initConfig?.longitude || movedHomeLocation[1]; - this._name = initConfig?.name || ""; - this._icon = initConfig?.icon || "mdi:map-marker"; - - this._passive = false; - this._radius = 100; + this._data = { + latitude: initConfig?.latitude || movedHomeLocation[0], + longitude: initConfig?.longitude || movedHomeLocation[1], + name: initConfig?.name || "", + icon: initConfig?.icon || "mdi:map-marker", + passive: false, + radius: 100, + }; } } public closeDialog(): void { this._params = undefined; + this._data = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render(): TemplateResult { - if (!this._params) { + if (!this._params || !this._data) { return html``; } - const nameInvalid = this._name.trim() === ""; - const iconInvalid = Boolean(this._icon && !this._icon.trim().includes(":")); - const latInvalid = String(this._latitude) === ""; - const lngInvalid = String(this._longitude) === ""; - const radiusInvalid = String(this._radius) === ""; + const nameInvalid = this._data.name.trim() === ""; + const iconInvalid = Boolean( + this._data.icon && !this._data.icon.trim().includes(":") + ); + const latInvalid = String(this._data.latitude) === ""; + const lngInvalid = String(this._data.longitude) === ""; + const radiusInvalid = String(this._data.radius) === ""; const valid = !nameInvalid && @@ -105,96 +88,15 @@ class DialogZoneDetail extends LitElement { )} >
- ${this._error ? html`
${this._error}
` : ""} -
- - - -
- - -
- -

- ${this.hass!.localize("ui.panel.config.zone.detail.passive_note")} -

- - - -
+
${this._params.entry ? html` @@ -221,74 +123,94 @@ class DialogZoneDetail extends LitElement { `; } - private _location = memoizeOne( - ( - lat: number, - lng: number, - radius: number, - passive: boolean, - icon: string - ): MarkerLocation[] => { - const computedStyles = getComputedStyle(this); - const zoneRadiusColor = computedStyles.getPropertyValue("--accent-color"); - const passiveRadiusColor = computedStyles.getPropertyValue( - "--secondary-text-color" - ); - return [ + private _schema = memoizeOne((icon?: string): HaFormSchema[] => [ + { + name: "name", + required: true, + selector: { + text: {}, + }, + }, + { + name: "icon", + required: false, + selector: { + icon: {}, + }, + }, + { + name: "location", + required: true, + selector: { location: { radius: true, icon } }, + }, + { + name: "", + type: "grid", + schema: [ { - id: "location", - latitude: Number(lat), - longitude: Number(lng), - radius, - radius_color: passive ? passiveRadiusColor : zoneRadiusColor, - icon, - location_editable: true, - radius_editable: true, + name: "latitude", + required: true, + selector: { text: {} }, }, - ]; - } - ); - - private _locationChanged(ev: CustomEvent) { - [this._latitude, this._longitude] = ev.detail.location; - } + { + name: "longitude", + required: true, - private _radiusChanged(ev: CustomEvent) { - this._radius = ev.detail.radius; - } + selector: { text: {} }, + }, + ], + }, + { name: "passive_note", type: "constant" }, + { name: "passive", selector: { boolean: {} } }, + { + name: "radius", + required: false, + selector: { number: { min: 0, max: 999999, mode: "box" } }, + }, + ]); - private _passiveChanged(ev) { - this._passive = ev.target.checked; - } + private _formData = memoizeOne((data: ZoneMutableParams) => ({ + ...data, + location: { + latitude: data.latitude, + longitude: data.longitude, + radius: data.radius, + }, + })); private _valueChanged(ev: CustomEvent) { - const configValue = (ev.target as any).configValue; - this._error = undefined; - this[`_${configValue}`] = ev.detail.value; + const value = ev.detail.value; + if ( + value.location.latitude !== this._data!.latitude || + value.location.longitude !== this._data!.longitude || + value.location.radius !== this._data!.radius + ) { + value.latitude = value.location.latitude; + value.longitude = value.location.longitude; + value.radius = Math.round(value.location.radius); + } + delete value.location; + if (!value.icon) { + delete value.icon; + } + this._data = value; } + private _computeLabel = (entry: HaFormSchema): string => + this.hass.localize(`ui.panel.config.zone.detail.${entry.name}`); + private async _updateEntry() { this._submitting = true; try { - const values: ZoneMutableParams = { - name: this._name.trim(), - latitude: this._latitude, - longitude: this._longitude, - passive: this._passive, - radius: this._radius, - }; - if (this._icon) { - values.icon = this._icon.trim(); - } if (this._params!.entry) { - await this._params!.updateEntry!(values); + await this._params!.updateEntry!(this._data!); } else { - await this._params!.createEntry(values); + await this._params!.createEntry(this._data!); } - this._params = undefined; + this.closeDialog(); } catch (err: any) { - this._error = err ? err.message : "Unknown error"; + this._error = { base: err ? err.message : "Unknown error" }; } finally { this._submitting = false; } @@ -309,24 +231,18 @@ class DialogZoneDetail extends LitElement { return [ haStyleDialog, css` - .location { - display: flex; - } - .location > * { - flex-grow: 1; - min-width: 0; - } - .location > *:first-child { - margin-right: 4px; - } - .location > *:last-child { - margin-left: 4px; + ha-dialog { + --mdc-dialog-min-width: 600px; } - ha-locations-editor { - margin-top: 16px; + @media all and (max-width: 450px), all and (max-height: 500px) { + ha-dialog { + --mdc-dialog-min-width: calc( + 100vw - env(safe-area-inset-right) - env(safe-area-inset-left) + ); + } } - a { - color: var(--primary-color); + ha-form.passive { + --zone-radius-color: var(--secondary-text-color); } `, ]; From 68e7ce188397eb57eac6500881d03707427fa9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 7 Mar 2022 17:42:49 +0100 Subject: [PATCH 20/35] Add systemd_resolved unsupported reason (#11971) --- src/translations/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index b97fd52f093e..91badbfe5fc4 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4323,7 +4323,8 @@ "privileged": "Supervisor is not privileged", "software": "Unsupported software detected", "source_mods": "Source modifications", - "systemd": "Systemd" + "systemd": "Systemd", + "systemd_resolved": "Systemd-Resolved" }, "unhealthy_reason": { "privileged": "Supervisor is not privileged", From fc6b594a2735f84cea107d105f11da4ea6a4bca0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 8 Mar 2022 10:09:45 -0800 Subject: [PATCH 21/35] Allow selecting multiple entities (#11986) --- gallery/src/pages/components/ha-selector.ts | 6 ++ .../ha-selector/ha-selector-entity.ts | 99 +++++++++++++++++-- src/data/selector.ts | 1 + 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/gallery/src/pages/components/ha-selector.ts b/gallery/src/pages/components/ha-selector.ts index 600176d02491..c3855147ed0f 100644 --- a/gallery/src/pages/components/ha-selector.ts +++ b/gallery/src/pages/components/ha-selector.ts @@ -175,6 +175,12 @@ const SCHEMAS: { }, }, }, + { + name: "Multiples", + input: { + entity: { name: "Entity", selector: { entity: { multiple: true } } }, + }, + }, ]; @customElement("demo-components-ha-selector") diff --git a/src/components/ha-selector/ha-selector-entity.ts b/src/components/ha-selector/ha-selector-entity.ts index 566dfc0a454b..36d0ce57adfd 100644 --- a/src/components/ha-selector/ha-selector-entity.ts +++ b/src/components/ha-selector/ha-selector-entity.ts @@ -1,6 +1,8 @@ import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { repeat } from "lit/directives/repeat"; +import { fireEvent } from "../../common/dom/fire_event"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { subscribeEntityRegistry } from "../../data/entity_registry"; import { EntitySelector } from "../../data/selector"; @@ -23,14 +25,45 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { @property({ type: Boolean }) public disabled = false; protected render() { - return html``; + if (!this.selector.entity.multiple) { + return html``; + } + + // For multiple, the value is a list. + const value = this._normalizedValue as string[]; + + return html` + ${this.label ? html`
${this.label}
` : ""} + ${repeat( + value, + (val) => val, + (entityId, index) => html` + + ` + )} + + `; } public hassSubscribe(): UnsubscribeFunc[] { @@ -48,6 +81,17 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { ]; } + private get _normalizedValue() { + if (!this.selector.entity.multiple) { + return this.value; + } + + if (!this.value) { + return []; + } + return Array.isArray(this.value) ? this.value : [this.value]; + } + private _filterEntities = (entity: HassEntity): boolean => { if (this.selector.entity?.domain) { const filterDomain = this.selector.entity.domain; @@ -79,6 +123,43 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { } return true; }; + + // this method is only used when multiple: true + private _valueChanged(ev: any) { + ev.stopPropagation(); + + // undefined = new value + const index = ev.target.index as number | undefined; + // undefined = remove + const newValue = ev.detail.value as string | undefined; + + let updatedValue: string[] | undefined; + + if (index === undefined) { + if (newValue) { + updatedValue = [...this._normalizedValue, newValue]; + } + ev.target.value = ""; + } else if (newValue) { + updatedValue = [...this._normalizedValue]; + updatedValue[index] = newValue; + } else { + updatedValue = this._normalizedValue.filter((_, i) => i !== index); + } + + if (updatedValue) { + fireEvent(this, "value-changed", { + value: updatedValue, + }); + } + } + + static styles = css` + ha-entity-picker + ha-entity-picker { + display: block; + margin-top: 16px; + } + `; } declare global { diff --git a/src/data/selector.ts b/src/data/selector.ts index c8cc87b36e69..76f673b12515 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -23,6 +23,7 @@ export interface EntitySelector { integration?: string; domain?: string | string[]; device_class?: string; + multiple?: boolean; }; } From db830e9014ed24985407dcfd90844597d81f4d69 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Mar 2022 19:13:08 +0100 Subject: [PATCH 22/35] Fix theme setting (#11977) --- gallery/src/components/demo-cards.ts | 3 +++ hassio/src/hassio-main.ts | 3 ++- src/auth/ha-authorize.ts | 20 +++++++++++++------- src/common/dom/apply_themes_on_element.ts | 9 +++++---- src/fake_data/provide_hass.ts | 4 +++- src/layouts/supervisor-error-screen.ts | 3 ++- src/onboarding/ha-onboarding.ts | 20 +++++++++++++------- src/state/themes-mixin.ts | 23 +++++++++++++++-------- 8 files changed, 56 insertions(+), 29 deletions(-) diff --git a/gallery/src/components/demo-cards.ts b/gallery/src/components/demo-cards.ts index fa91ca209769..d2c485e76ede 100644 --- a/gallery/src/components/demo-cards.ts +++ b/gallery/src/components/demo-cards.ts @@ -78,6 +78,9 @@ class DemoCards extends LitElement { ha-formfield { margin-right: 16px; } + #container { + background-color: var(--primary-background-color); + } `; } diff --git a/hassio/src/hassio-main.ts b/hassio/src/hassio-main.ts index b64e505d377a..53df93274806 100644 --- a/hassio/src/hassio-main.ts +++ b/hassio/src/hassio-main.ts @@ -121,7 +121,8 @@ export class HassioMain extends SupervisorBaseElement { this.parentElement, this.hass.themes, themeName, - themeSettings + themeSettings, + true ); } } diff --git a/src/auth/ha-authorize.ts b/src/auth/ha-authorize.ts index 8f91bfe8bf8a..add29139bc54 100644 --- a/src/auth/ha-authorize.ts +++ b/src/auth/ha-authorize.ts @@ -101,13 +101,19 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) { this._fetchAuthProviders(); if (matchMedia("(prefers-color-scheme: dark)").matches) { - applyThemesOnElement(document.documentElement, { - default_theme: "default", - default_dark_theme: null, - themes: {}, - darkMode: true, - theme: "default", - }); + applyThemesOnElement( + document.documentElement, + { + default_theme: "default", + default_dark_theme: null, + themes: {}, + darkMode: true, + theme: "default", + }, + undefined, + undefined, + true + ); } if (!this.redirectUri) { diff --git a/src/common/dom/apply_themes_on_element.ts b/src/common/dom/apply_themes_on_element.ts index dd5aebc42377..ab80caf54b9c 100644 --- a/src/common/dom/apply_themes_on_element.ts +++ b/src/common/dom/apply_themes_on_element.ts @@ -31,11 +31,12 @@ export const applyThemesOnElement = ( element, themes: HomeAssistant["themes"], selectedTheme?: string, - themeSettings?: Partial + themeSettings?: Partial, + main?: boolean ) => { - // If there is no explicitly desired theme provided, we automatically + // If there is no explicitly desired theme provided, and the element is the main element we automatically // use the active one from `themes`. - const themeToApply = selectedTheme || themes.theme; + const themeToApply = selectedTheme || (main ? themes.theme : undefined); // If there is no explicitly desired dark mode provided, we automatically // use the active one from `themes`. @@ -47,7 +48,7 @@ export const applyThemesOnElement = ( let cacheKey = themeToApply; let themeRules: Partial = {}; - if (darkMode) { + if (themeToApply && darkMode) { cacheKey = `${cacheKey}__dark`; themeRules = { ...darkStyles }; } diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts index dc9e875b32ab..3fd29bcdbffc 100644 --- a/src/fake_data/provide_hass.ts +++ b/src/fake_data/provide_hass.ts @@ -300,7 +300,9 @@ export const provideHass = ( applyThemesOnElement( document.documentElement, themes, - selectedTheme!.theme + selectedTheme!.theme, + undefined, + true ); }, diff --git a/src/layouts/supervisor-error-screen.ts b/src/layouts/supervisor-error-screen.ts index cc9a9be2adf6..eabb00e439e3 100644 --- a/src/layouts/supervisor-error-screen.ts +++ b/src/layouts/supervisor-error-screen.ts @@ -101,7 +101,8 @@ class SupervisorErrorScreen extends LitElement { this.parentElement, this.hass.themes, themeName, - themeSettings + themeSettings, + true ); } diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts index eb01f058770c..3c05e5b68052 100644 --- a/src/onboarding/ha-onboarding.ts +++ b/src/onboarding/ha-onboarding.ts @@ -133,13 +133,19 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { import("./particles"); } if (matchMedia("(prefers-color-scheme: dark)").matches) { - applyThemesOnElement(document.documentElement, { - default_theme: "default", - default_dark_theme: null, - themes: {}, - darkMode: true, - theme: "default", - }); + applyThemesOnElement( + document.documentElement, + { + default_theme: "default", + default_dark_theme: null, + themes: {}, + darkMode: true, + theme: "default", + }, + undefined, + undefined, + true + ); } } diff --git a/src/state/themes-mixin.ts b/src/state/themes-mixin.ts index 3595a54a5937..d56443be0f1c 100644 --- a/src/state/themes-mixin.ts +++ b/src/state/themes-mixin.ts @@ -38,13 +38,19 @@ export default >(superClass: T) => }); mql.addListener((ev) => this._applyTheme(ev.matches)); if (!this._themeApplied && mql.matches) { - applyThemesOnElement(document.documentElement, { - default_theme: "default", - default_dark_theme: null, - themes: {}, - darkMode: true, - theme: "default", - }); + applyThemesOnElement( + document.documentElement, + { + default_theme: "default", + default_dark_theme: null, + themes: {}, + darkMode: true, + theme: "default", + }, + undefined, + undefined, + true + ); } } @@ -93,7 +99,8 @@ export default >(superClass: T) => document.documentElement, this.hass.themes, themeName, - themeSettings + themeSettings, + true ); if (darkMode !== this.hass.themes.darkMode) { From d968fe41ee71629fdceb18b966bb223aa9546569 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 8 Mar 2022 12:19:18 -0600 Subject: [PATCH 23/35] Update Style of Design Page (#11982) --- gallery/src/components/page-description.ts | 22 ++++- gallery/src/ha-gallery.ts | 94 +++++++++++-------- .../src/pages/components/ha-alert.markdown | 1 + 3 files changed, 76 insertions(+), 41 deletions(-) diff --git a/gallery/src/components/page-description.ts b/gallery/src/components/page-description.ts index 8e449195a63f..e9680e59b380 100644 --- a/gallery/src/components/page-description.ts +++ b/gallery/src/components/page-description.ts @@ -12,7 +12,14 @@ class PageDescription extends HaMarkdown { if (!PAGES[this.page].description) { return html``; } + return html` +
+
+ ${PAGES[this.page].metadata.title || this.page.split("/")[1]} +
+
${PAGES[this.page].metadata.subtitle}
+
${until( PAGES[this.page] .description() @@ -25,9 +32,22 @@ class PageDescription extends HaMarkdown { static styles = [ HaMarkdown.styles, css` + .heading { + padding: 16px; + border-bottom: 1px solid var(--secondary-background-color); + } + .title { + font-size: 42px; + line-height: 56px; + padding-bottom: 8px; + } + .subtitle { + font-size: 18px; + line-height: 24px; + } .root { max-width: 800px; - margin: 0 auto; + margin: 16px auto; } .root > *:first-child { margin-top: 0; diff --git a/gallery/src/ha-gallery.ts b/gallery/src/ha-gallery.ts index 86e64e8d35b3..e602a1916dbd 100644 --- a/gallery/src/ha-gallery.ts +++ b/gallery/src/ha-gallery.ts @@ -5,6 +5,7 @@ import { html, css, LitElement, PropertyValues } from "lit"; import { customElement, property, query } from "lit/decorators"; import "../../src/components/ha-icon-button"; import "../../src/managers/notification-manager"; +import "../../src/components/ha-expansion-panel"; import { haStyle } from "../../src/resources/styles"; import { PAGES, SIDEBAR } from "../build/import-pages"; import { dynamicElement } from "../../src/common/dom/dynamic-element-directive"; @@ -53,10 +54,9 @@ class HaGallery extends LitElement { sidebar.push( group.header ? html` -
- ${group.header} + ${links} -
+ ` : links ); @@ -92,27 +92,34 @@ class HaGallery extends LitElement { ${dynamicElement(`demo-${this._page.replace("/", "-")}`)}
@@ -186,27 +193,16 @@ class HaGallery extends LitElement { padding: 4px; } - .sidebar details { - margin-top: 1em; - margin-left: 1em; - } - - .sidebar summary { - cursor: pointer; - font-weight: bold; - margin-bottom: 8px; - } - .sidebar a { color: var(--primary-text-color); display: block; - padding: 4px 12px; + padding: 12px; text-decoration: none; position: relative; } .sidebar a[active]::before { - border-radius: 4px; + border-radius: 12px; position: absolute; top: 0; right: 2px; @@ -237,14 +233,32 @@ class HaGallery extends LitElement { .page-footer { text-align: center; - margin: 16px 0; - padding-top: 16px; - border-top: 1px solid rgba(0, 0, 0, 0.12); + margin: 16px; + padding: 16px; + border-radius: 12px; + background-color: var(--primary-background-color); + } + + .page-footer div { + margin-top: 4px; + } + + .page-footer .header { + font-size: 16px; + font-weight: 500; + line-height: 28px; + text-align: center; + } + + .page-footer .secondary { + line-height: 23px; + text-align: center; } .page-footer a { display: inline-block; margin: 0 8px; + text-decoration: none; } `, ]; diff --git a/gallery/src/pages/components/ha-alert.markdown b/gallery/src/pages/components/ha-alert.markdown index 2488b8309052..e37a2054819e 100644 --- a/gallery/src/pages/components/ha-alert.markdown +++ b/gallery/src/pages/components/ha-alert.markdown @@ -1,5 +1,6 @@ --- title: Alerts +subtitle: An alert displays a short, important message in a way that attracts the user's attention without interrupting the user's task. --- # Alert `` From 9f1e9b43fef706718628efd5acfca89e1c5c96c5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 8 Mar 2022 19:33:23 -0800 Subject: [PATCH 24/35] Use entities-picker in entity selector (#11990) --- gallery/src/pages/components/ha-form.ts | 102 +++++++++++++++++- src/components/entity/ha-entities-picker.ts | 6 +- .../ha-selector/ha-selector-entity.ts | 81 ++------------ 3 files changed, 111 insertions(+), 78 deletions(-) diff --git a/gallery/src/pages/components/ha-form.ts b/gallery/src/pages/components/ha-form.ts index 0722040cc002..cab77bacfce3 100644 --- a/gallery/src/pages/components/ha-form.ts +++ b/gallery/src/pages/components/ha-form.ts @@ -12,6 +12,98 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import { HomeAssistant } from "../../../../src/types"; +import { getEntity } from "../../../../src/fake_data/entity"; + +const ENTITIES = [ + getEntity("alarm_control_panel", "alarm", "disarmed", { + friendly_name: "Alarm", + }), + getEntity("media_player", "livingroom", "playing", { + friendly_name: "Livingroom", + }), + getEntity("media_player", "lounge", "idle", { + friendly_name: "Lounge", + supported_features: 444983, + }), + getEntity("light", "bedroom", "on", { + friendly_name: "Bedroom", + }), + getEntity("switch", "coffee", "off", { + friendly_name: "Coffee", + }), +]; + +const DEVICES = [ + { + area_id: "bedroom", + configuration_url: null, + config_entries: ["config_entry_1"], + connections: [], + disabled_by: null, + entry_type: null, + id: "device_1", + identifiers: [["demo", "volume1"] as [string, string]], + manufacturer: null, + model: null, + name_by_user: null, + name: "Dishwasher", + sw_version: null, + hw_version: null, + via_device_id: null, + }, + { + area_id: "backyard", + configuration_url: null, + config_entries: ["config_entry_2"], + connections: [], + disabled_by: null, + entry_type: null, + id: "device_2", + identifiers: [["demo", "pwm1"] as [string, string]], + manufacturer: null, + model: null, + name_by_user: null, + name: "Lamp", + sw_version: null, + hw_version: null, + via_device_id: null, + }, + { + area_id: null, + configuration_url: null, + config_entries: ["config_entry_3"], + connections: [], + disabled_by: null, + entry_type: null, + id: "device_3", + identifiers: [["demo", "pwm1"] as [string, string]], + manufacturer: null, + model: null, + name_by_user: "User name", + name: "Technical name", + sw_version: null, + hw_version: null, + via_device_id: null, + }, +]; + +const AREAS = [ + { + area_id: "backyard", + name: "Backyard", + picture: null, + }, + { + area_id: "bedroom", + name: "Bedroom", + picture: null, + }, + { + area_id: "livingroom", + name: "Livingroom", + picture: null, + }, +]; const SCHEMAS: { title: string; @@ -39,6 +131,7 @@ const SCHEMAS: { icon: "Icon", media: "Media", location: "Location", + entities: "Entities", }, schema: [ { name: "addon", selector: { addon: {} } }, @@ -80,6 +173,10 @@ const SCHEMAS: { name: "location", selector: { location: { radius: true, icon: "mdi:home" } }, }, + { + name: "entities", + selector: { entity: { multiple: true } }, + }, ], }, { @@ -320,9 +417,10 @@ class DemoHaForm extends LitElement { const hass = provideHass(this); hass.updateTranslations(null, "en"); hass.updateTranslations("config", "en"); + hass.addEntities(ENTITIES); mockEntityRegistry(hass); - mockDeviceRegistry(hass); - mockAreaRegistry(hass); + mockDeviceRegistry(hass, DEVICES); + mockAreaRegistry(hass, AREAS); mockHassioSupervisor(hass); } diff --git a/src/components/entity/ha-entities-picker.ts b/src/components/entity/ha-entities-picker.ts index 35e21dc9509d..062b79eced4c 100644 --- a/src/components/entity/ha-entities-picker.ts +++ b/src/components/entity/ha-entities-picker.ts @@ -51,6 +51,8 @@ class HaEntitiesPickerLight extends LitElement { @property({ attribute: "pick-entity-label" }) public pickEntityLabel?: string; + @property() public entityFilter?: HaEntityPickerEntityFilterFunc; + protected render(): TemplateResult { if (!this.hass) { return html``; @@ -94,7 +96,9 @@ class HaEntitiesPickerLight extends LitElement { private _entityFilter: HaEntityPickerEntityFilterFunc = ( stateObj: HassEntity - ) => !this.value || !this.value.includes(stateObj.entity_id); + ) => + (!this.value || !this.value.includes(stateObj.entity_id)) && + (!this.entityFilter || this.entityFilter(stateObj)); private get _currentEntities() { return this.value || []; diff --git a/src/components/ha-selector/ha-selector-entity.ts b/src/components/ha-selector/ha-selector-entity.ts index 36d0ce57adfd..cbc5366953d4 100644 --- a/src/components/ha-selector/ha-selector-entity.ts +++ b/src/components/ha-selector/ha-selector-entity.ts @@ -1,14 +1,13 @@ import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; -import { css, html, LitElement } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { repeat } from "lit/directives/repeat"; -import { fireEvent } from "../../common/dom/fire_event"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { subscribeEntityRegistry } from "../../data/entity_registry"; import { EntitySelector } from "../../data/selector"; import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { HomeAssistant } from "../../types"; import "../entity/ha-entity-picker"; +import "../entity/ha-entities-picker"; @customElement("ha-selector-entity") export class HaEntitySelector extends SubscribeMixin(LitElement) { @@ -36,33 +35,13 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { >`; } - // For multiple, the value is a list. - const value = this._normalizedValue as string[]; - return html` - ${this.label ? html`
${this.label}
` : ""} - ${repeat( - value, - (val) => val, - (entityId, index) => html` - - ` - )} - ${this.label}` : ""} + + > `; } @@ -81,17 +60,6 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { ]; } - private get _normalizedValue() { - if (!this.selector.entity.multiple) { - return this.value; - } - - if (!this.value) { - return []; - } - return Array.isArray(this.value) ? this.value : [this.value]; - } - private _filterEntities = (entity: HassEntity): boolean => { if (this.selector.entity?.domain) { const filterDomain = this.selector.entity.domain; @@ -123,43 +91,6 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) { } return true; }; - - // this method is only used when multiple: true - private _valueChanged(ev: any) { - ev.stopPropagation(); - - // undefined = new value - const index = ev.target.index as number | undefined; - // undefined = remove - const newValue = ev.detail.value as string | undefined; - - let updatedValue: string[] | undefined; - - if (index === undefined) { - if (newValue) { - updatedValue = [...this._normalizedValue, newValue]; - } - ev.target.value = ""; - } else if (newValue) { - updatedValue = [...this._normalizedValue]; - updatedValue[index] = newValue; - } else { - updatedValue = this._normalizedValue.filter((_, i) => i !== index); - } - - if (updatedValue) { - fireEvent(this, "value-changed", { - value: updatedValue, - }); - } - } - - static styles = css` - ha-entity-picker + ha-entity-picker { - display: block; - margin-top: 16px; - } - `; } declare global { From 246e426182eed40b20f003ba21183c6ad601ef54 Mon Sep 17 00:00:00 2001 From: Emil Stjerneman Date: Thu, 10 Mar 2022 02:54:40 +0100 Subject: [PATCH 25/35] #11971 Change order of alarm panel buttons (#11998) --- src/dialogs/more-info/controls/more-info-alarm_control_panel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts b/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts index 1efb0094f7d1..de41f1c173c9 100644 --- a/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts +++ b/src/dialogs/more-info/controls/more-info-alarm_control_panel.ts @@ -12,7 +12,7 @@ import { import type { HomeAssistant } from "../../../types"; const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"]; -const ARM_ACTIONS = ["arm_away", "arm_home"]; +const ARM_ACTIONS = ["arm_home", "arm_away"]; const DISARM_ACTIONS = ["disarm"]; @customElement("more-info-alarm_control_panel") From 99fd3a1b6f9e9420ca79dad0fd46e61d0f9d85f6 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 10 Mar 2022 08:45:12 -0500 Subject: [PATCH 26/35] Fix zwave_js 'add/remove device' disabled bug (#12000) * Fix zwave_js 'add/remove device' disabled bug * revert extra change --- .../zwave_js/zwave_js-config-dashboard.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 ac4f75522fe0..3c75b59df0e9 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 @@ -237,8 +237,10 @@ class ZWaveJSConfigDashboard extends LitElement { ${this.hass.localize( "ui.panel.config.zwave_js.common.remove_node" @@ -304,7 +306,9 @@ class ZWaveJSConfigDashboard extends LitElement { ?rtl=${computeRTL(this.hass)} @click=${this._addNodeClicked} .disabled=${this._status !== "connected" || - this._network?.controller.inclusion_state !== InclusionState.Idle} + (this._network?.controller.inclusion_state !== InclusionState.Idle && + this._network?.controller.inclusion_state !== + InclusionState.SmartStart)} > From f89b8cffcf7f32032e72805727b572737e8d8798 Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Thu, 10 Mar 2022 10:21:04 -0500 Subject: [PATCH 27/35] Fix zwave_js set config dropdown default value (#11974) Co-authored-by: Paulus Schoutsen --- .../integration-panels/zwave_js/zwave_js-node-config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5426b494a3df..70e9d26390d4 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 @@ -289,7 +289,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
Date: Fri, 11 Mar 2022 11:37:22 -0600 Subject: [PATCH 28/35] Fix changing cost number in energy settings (#12009) --- .../config/energy/dialogs/dialog-energy-grid-flow-settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts index 2599f61ee3f2..c0186402634e 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-grid-flow-settings.ts @@ -240,7 +240,7 @@ export class DialogEnergyGridFlowSettings this._costStat = null; this._source = { ...this._source!, - number_energy_price: Number(ev.detail.value), + number_energy_price: Number((ev.target as any).value), entity_energy_price: null, }; } From f4e28da0a3d603d7bb1a4b0c05e8e656f2591eda Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Fri, 11 Mar 2022 11:38:18 -0600 Subject: [PATCH 29/35] Fix Dashboard Editing (#12011) --- .../dashboards/dialog-lovelace-dashboard-detail.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts b/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts index 75db9e031cfb..f48ef1f43950 100644 --- a/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts +++ b/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts @@ -11,6 +11,7 @@ import { CoreFrontendUserData } from "../../../../data/frontend"; import { LovelaceDashboard, LovelaceDashboardCreateParams, + LovelaceDashboardMutableParams, } from "../../../../data/lovelace"; import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel"; import { haStyleDialog } from "../../../../resources/styles"; @@ -40,7 +41,7 @@ export class DialogLovelaceDashboardDetail extends LitElement { } else { this._data = { show_in_sidebar: true, - icon: "", + icon: undefined, title: "", require_admin: false, mode: "storage", @@ -264,7 +265,13 @@ export class DialogLovelaceDashboardDetail extends LitElement { this._submitting = true; try { if (this._params!.dashboard) { - await this._params!.updateDashboard(this._data as LovelaceDashboard); + const values: Partial = { + require_admin: this._data!.require_admin, + show_in_sidebar: this._data!.show_in_sidebar, + icon: this._data!.icon || undefined, + title: this._data!.title, + }; + await this._params!.updateDashboard(values); } else { await this._params!.createDashboard( this._data as LovelaceDashboardCreateParams From 9b6c935ffb19fa15d6e92ed5e2c0c9d7b750ff8e Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Fri, 11 Mar 2022 11:39:04 -0600 Subject: [PATCH 30/35] Fix For Selecting Device Class (#12010) --- src/panels/config/entities/entity-registry-settings.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/panels/config/entities/entity-registry-settings.ts b/src/panels/config/entities/entity-registry-settings.ts index 4c59b0d99ee8..72854d838927 100644 --- a/src/panels/config/entities/entity-registry-settings.ts +++ b/src/panels/config/entities/entity-registry-settings.ts @@ -11,6 +11,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; +import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeDomain } from "../../../common/entity/compute_domain"; import { domainIcon } from "../../../common/entity/domain_icon"; import "../../../components/ha-alert"; @@ -166,7 +167,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) { "ui.dialogs.entity_registry.editor.device_class" )} .value=${this._deviceClass} + naturalMenuWidth + fixedMenuPosition @selected=${this._deviceClassChanged} + @closed=${stopPropagation} > ${OVERRIDE_DEVICE_CLASSES[domain].map( (deviceClass: string) => html` @@ -422,6 +426,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) { } ha-select { width: 100%; + margin: 8px 0; } ha-switch { margin-right: 16px; From 5dfe17a43a27cb0d41d1281546650349add71e8e Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Fri, 11 Mar 2022 19:07:56 -0600 Subject: [PATCH 31/35] Fix: Allow for deleting Input_select options (#12007) --- src/panels/config/helpers/forms/ha-input_select-form.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/helpers/forms/ha-input_select-form.ts b/src/panels/config/helpers/forms/ha-input_select-form.ts index cec1eb952ee3..c7abc8e1764a 100644 --- a/src/panels/config/helpers/forms/ha-input_select-form.ts +++ b/src/panels/config/helpers/forms/ha-input_select-form.ts @@ -85,7 +85,7 @@ class HaInputSelectForm extends LitElement { ${this._options.length ? this._options.map( (option, index) => html` - + ${option} Date: Fri, 11 Mar 2022 23:25:09 -0600 Subject: [PATCH 32/35] Script ID update with Alias (#12008) --- src/panels/config/script/ha-script-editor.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index 2aeb262d47a4..611e52abec78 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -569,9 +569,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { } private _aliasChanged(alias: string) { - if (this.scriptEntityId || this._entityId) { + if ( + this.scriptEntityId || + (this._entityId && this._entityId !== slugify(this._config!.alias)) + ) { return; } + const aliasSlugify = slugify(alias); let id = aliasSlugify; let i = 2; @@ -595,6 +599,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { private _valueChanged(ev: CustomEvent) { ev.stopPropagation(); const values = ev.detail.value as any; + const currentId = this._entityId; for (const key of Object.keys(values)) { if (key === "sequence") { @@ -603,7 +608,10 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { const value = values[key]; - if (value === this._config![key]) { + if ( + value === this._config![key] || + (key === "id" && currentId === value) + ) { continue; } From fad8a27232760988a62dcc6a62b9fdb4823f5a5e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 12 Mar 2022 11:56:25 -0800 Subject: [PATCH 33/35] HAWS 6.1 (#12016) --- package.json | 2 +- src/components/entity/ha-entity-picker.ts | 31 ++++++++++--------- .../media-browser/browser-media-player.ts | 2 +- yarn.lock | 10 +++--- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 24c7cccf92f0..0725b0af6092 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "fuse.js": "^6.0.0", "google-timezones-json": "^1.0.2", "hls.js": "^1.1.5", - "home-assistant-js-websocket": "^6.0.1", + "home-assistant-js-websocket": "^6.1.1", "idb-keyval": "^5.1.3", "intl-messageformat": "^9.9.1", "js-yaml": "^4.1.0", diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index d24f6fbac489..a8d17084c690 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -15,18 +15,21 @@ import "../ha-icon-button"; import "../ha-svg-icon"; import "./state-badge"; +interface HassEntityWithCachedName extends HassEntity { + friendly_name: string; +} + export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean; // eslint-disable-next-line lit/prefer-static-styles -const rowRenderer: ComboBoxLitRenderer = - (item) => - html` - ${item.state - ? html`` - : ""} - ${item.friendly_name} - ${item.entity_id} - `; +const rowRenderer: ComboBoxLitRenderer = (item) => + html` + ${item.state + ? html`` + : ""} + ${item.friendly_name} + ${item.entity_id} + `; @customElement("ha-entity-picker") export class HaEntityPicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -96,7 +99,7 @@ export class HaEntityPicker extends LitElement { private _initedStates = false; - private _states: HassEntity[] = []; + private _states: HassEntityWithCachedName[] = []; private _getStates = memoizeOne( ( @@ -107,8 +110,8 @@ export class HaEntityPicker extends LitElement { entityFilter: this["entityFilter"], includeDeviceClasses: this["includeDeviceClasses"], includeUnitOfMeasurement: this["includeUnitOfMeasurement"] - ) => { - let states: HassEntity[] = []; + ): HassEntityWithCachedName[] => { + let states: HassEntityWithCachedName[] = []; if (!hass) { return []; @@ -122,7 +125,7 @@ export class HaEntityPicker extends LitElement { state: "", last_changed: "", last_updated: "", - context: { id: "", user_id: null }, + context: { id: "", user_id: null, parent_id: null }, friendly_name: this.hass!.localize( "ui.components.entity.entity-picker.no_entities" ), @@ -190,7 +193,7 @@ export class HaEntityPicker extends LitElement { state: "", last_changed: "", last_updated: "", - context: { id: "", user_id: null }, + context: { id: "", user_id: null, parent_id: null }, friendly_name: this.hass!.localize( "ui.components.entity.entity-picker.no_match" ), diff --git a/src/panels/media-browser/browser-media-player.ts b/src/panels/media-browser/browser-media-player.ts index b4096f9a7ff4..c4c4bddc2285 100644 --- a/src/panels/media-browser/browser-media-player.ts +++ b/src/panels/media-browser/browser-media-player.ts @@ -84,7 +84,7 @@ export class BrowserMediaPlayer { last_changed: now, last_updated: now, attributes: {}, - context: { id: "", user_id: null }, + context: { id: "", user_id: null, parent_id: null }, }; } diff --git a/yarn.lock b/yarn.lock index d20b3377e854..ab97532484da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9128,7 +9128,7 @@ fsevents@^1.2.7: gulp-rename: ^2.0.0 gulp-zopfli-green: ^3.0.1 hls.js: ^1.1.5 - home-assistant-js-websocket: ^6.0.1 + home-assistant-js-websocket: ^6.1.1 html-minifier: ^4.0.0 husky: ^1.3.1 idb-keyval: ^5.1.3 @@ -9198,10 +9198,10 @@ fsevents@^1.2.7: languageName: unknown linkType: soft -"home-assistant-js-websocket@npm:^6.0.1": - version: 6.0.1 - resolution: "home-assistant-js-websocket@npm:6.0.1" - checksum: 566d6de6a4eb0e05ca434a45433cfe6fdd6b5cb2008e9a165709e08335df1c9b70903564c479ab8d48c6f5468a9784f47697192f9023170d2d86d43a461d6126 +"home-assistant-js-websocket@npm:^6.1.1": + version: 6.1.1 + resolution: "home-assistant-js-websocket@npm:6.1.1" + checksum: b46dd44ac1b393ecfdda152743fe44dcd6534463d1df631c448a71e71166c04d4fea2a0827dbb2e55c85fcd8d9ccdea4f168713bf18e832c7f688b9b2fdc8d72 languageName: node linkType: hard From 4916527e5fd4e3bc583ca0dbcec1f3a062d93e48 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 12 Mar 2022 13:42:42 -0800 Subject: [PATCH 34/35] Bumped version to 20220301.1 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 13b918a742e7..3b273584c00c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = home-assistant-frontend -version = 20220301.0 +version = 20220301.1 author = The Home Assistant Authors author_email = hello@home-assistant.io license = Apache-2.0 From c4dc6bfb0d633932d95afa309e8d9ccf0771c3c8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 12 Mar 2022 13:45:34 -0800 Subject: [PATCH 35/35] Bumped version to 20220301.2 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 3b273584c00c..a73c6937e083 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = home-assistant-frontend -version = 20220301.1 +version = 20220301.2 author = The Home Assistant Authors author_email = hello@home-assistant.io license = Apache-2.0