diff --git a/gallery/public/images/brand/logo-exclusion-zone.png b/gallery/public/images/brand/logo-exclusion-zone.png index 6a8bb93a49..94de93cb79 100644 Binary files a/gallery/public/images/brand/logo-exclusion-zone.png and b/gallery/public/images/brand/logo-exclusion-zone.png differ diff --git a/gallery/public/images/brand/logo-layout-variants.png b/gallery/public/images/brand/logo-layout-variants.png index 567fd88620..91f8db9975 100644 Binary files a/gallery/public/images/brand/logo-layout-variants.png and b/gallery/public/images/brand/logo-layout-variants.png differ diff --git a/gallery/public/images/brand/logo.png b/gallery/public/images/brand/logo.png index 6267f9a97e..0cd9de316b 100644 Binary files a/gallery/public/images/brand/logo.png and b/gallery/public/images/brand/logo.png differ diff --git a/package.json b/package.json index c66b208b92..7ee538db46 100644 --- a/package.json +++ b/package.json @@ -182,7 +182,7 @@ "@types/luxon": "3.3.2", "@types/mocha": "10.0.1", "@types/qrcode": "1.5.2", - "@types/serve-handler": "6.1.1", + "@types/serve-handler": "6.1.2", "@types/sortablejs": "1.15.2", "@types/tar": "6.1.6", "@types/ua-parser-js": "0.7.37", diff --git a/public/static/icons/favicon-1024x1024.png b/public/static/icons/favicon-1024x1024.png index 5092790999..ff2305532d 100644 Binary files a/public/static/icons/favicon-1024x1024.png and b/public/static/icons/favicon-1024x1024.png differ diff --git a/public/static/icons/favicon-512x512.png b/public/static/icons/favicon-512x512.png index a2a45b3012..3ff438e6d5 100644 Binary files a/public/static/icons/favicon-512x512.png and b/public/static/icons/favicon-512x512.png differ diff --git a/public/static/icons/favicon-apple-180x180.png b/public/static/icons/favicon-apple-180x180.png index 8dd156bdae..3a431359a4 100644 Binary files a/public/static/icons/favicon-apple-180x180.png and b/public/static/icons/favicon-apple-180x180.png differ diff --git a/public/static/icons/maskable_icon-128x128.png b/public/static/icons/maskable_icon-128x128.png index 9dd3b8b335..b6a9ead6ec 100644 Binary files a/public/static/icons/maskable_icon-128x128.png and b/public/static/icons/maskable_icon-128x128.png differ diff --git a/public/static/icons/maskable_icon-192x192.png b/public/static/icons/maskable_icon-192x192.png index 4160bc541a..e7c0aa9019 100644 Binary files a/public/static/icons/maskable_icon-192x192.png and b/public/static/icons/maskable_icon-192x192.png differ diff --git a/public/static/icons/maskable_icon-384x384.png b/public/static/icons/maskable_icon-384x384.png index 205303b9c3..3819c4ef35 100644 Binary files a/public/static/icons/maskable_icon-384x384.png and b/public/static/icons/maskable_icon-384x384.png differ diff --git a/public/static/icons/maskable_icon-48x48.png b/public/static/icons/maskable_icon-48x48.png index 5d04f82317..4a156aab24 100644 Binary files a/public/static/icons/maskable_icon-48x48.png and b/public/static/icons/maskable_icon-48x48.png differ diff --git a/public/static/icons/maskable_icon-512x512.png b/public/static/icons/maskable_icon-512x512.png index 3bcfa65b76..3bc8086f32 100644 Binary files a/public/static/icons/maskable_icon-512x512.png and b/public/static/icons/maskable_icon-512x512.png differ diff --git a/public/static/icons/maskable_icon-72x72.png b/public/static/icons/maskable_icon-72x72.png index aa3daa53b1..a938f17e63 100644 Binary files a/public/static/icons/maskable_icon-72x72.png and b/public/static/icons/maskable_icon-72x72.png differ diff --git a/public/static/icons/maskable_icon-96x96.png b/public/static/icons/maskable_icon-96x96.png index 135ce6516a..01a4184966 100644 Binary files a/public/static/icons/maskable_icon-96x96.png and b/public/static/icons/maskable_icon-96x96.png differ diff --git a/public/static/icons/tile-win-310x150.png b/public/static/icons/tile-win-310x150.png index 52f423b690..0e14253431 100644 Binary files a/public/static/icons/tile-win-310x150.png and b/public/static/icons/tile-win-310x150.png differ diff --git a/public/static/icons/tile-win-310x310.png b/public/static/icons/tile-win-310x310.png index 81a241206c..8eb5e21ee1 100644 Binary files a/public/static/icons/tile-win-310x310.png and b/public/static/icons/tile-win-310x310.png differ diff --git a/public/static/images/color_wheel.png b/public/static/images/color_wheel.png index 62b9e48ce8..d25bfb135f 100644 Binary files a/public/static/images/color_wheel.png and b/public/static/images/color_wheel.png differ diff --git a/public/static/images/notification-badge.png b/public/static/images/notification-badge.png index bf4017bc47..79f925d05b 100644 Binary files a/public/static/images/notification-badge.png and b/public/static/images/notification-badge.png differ diff --git a/pyproject.toml b/pyproject.toml index 078bcd1894..c92ba7b5e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20230926.0" +version = "20230928.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index 73725f7f8e..8d91721ed4 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -138,41 +138,54 @@ const tryDescribeTrigger = ( // Numeric State Trigger if (trigger.platform === "numeric_state" && trigger.entity_id) { - let base = "When"; const stateObj = hass.states[trigger.entity_id]; const entity = stateObj ? computeStateName(stateObj) : trigger.entity_id; - if (trigger.attribute) { - base += ` ${computeAttributeNameDisplay( - hass.localize, - stateObj, - hass.entities, - trigger.attribute - )} from`; + const attribute = trigger.attribute + ? computeAttributeNameDisplay( + hass.localize, + stateObj, + hass.entities, + trigger.attribute + ) + : undefined; + + const duration = trigger.for ? describeDuration(trigger.for) : undefined; + + if (trigger.above && trigger.below) { + return hass.localize( + `${triggerTranslationBaseKey}.numeric_state.description.above-below`, + { + attribute: attribute, + entity: entity, + above: trigger.above, + below: trigger.below, + duration: duration, + } + ); } - - base += ` ${entity} is`; - - if (trigger.above !== undefined) { - base += ` above ${trigger.above}`; + if (trigger.above) { + return hass.localize( + `${triggerTranslationBaseKey}.numeric_state.description.above`, + { + attribute: attribute, + entity: entity, + above: trigger.above, + duration: duration, + } + ); } - - if (trigger.below !== undefined && trigger.above !== undefined) { - base += " and"; + if (trigger.below) { + return hass.localize( + `${triggerTranslationBaseKey}.numeric_state.description.below`, + { + attribute: attribute, + entity: entity, + below: trigger.below, + duration: duration, + } + ); } - - if (trigger.below !== undefined) { - base += ` below ${trigger.below}`; - } - - if (trigger.for) { - const duration = describeDuration(trigger.for); - if (duration) { - base += ` for ${duration}`; - } - } - - return base; } // State Trigger @@ -825,29 +838,49 @@ const tryDescribeCondition = ( // Numeric State Condition if (condition.condition === "numeric_state" && condition.entity_id) { - let base = "Confirm"; const stateObj = hass.states[condition.entity_id]; const entity = stateObj ? computeStateName(stateObj) : condition.entity_id; - if ("attribute" in condition) { - base += ` ${condition.attribute} from`; + const attribute = condition.attribute + ? computeAttributeNameDisplay( + hass.localize, + stateObj, + hass.entities, + condition.attribute + ) + : undefined; + + if (condition.above && condition.below) { + return hass.localize( + `${conditionsTranslationBaseKey}.numeric_state.description.above-below`, + { + attribute: attribute, + entity: entity, + above: condition.above, + below: condition.below, + } + ); } - - base += ` ${entity} is`; - - if ("above" in condition) { - base += ` above ${condition.above}`; + if (condition.above) { + return hass.localize( + `${conditionsTranslationBaseKey}.numeric_state.description.above`, + { + attribute: attribute, + entity: entity, + above: condition.above, + } + ); } - - if ("below" in condition && "above" in condition) { - base += " and"; + if (condition.below) { + return hass.localize( + `${conditionsTranslationBaseKey}.numeric_state.description.below`, + { + attribute: attribute, + entity: entity, + below: condition.below, + } + ); } - - if ("below" in condition) { - base += ` below ${condition.below}`; - } - - return base; } // Time condition diff --git a/src/data/zwave_js.ts b/src/data/zwave_js.ts index 623516ce59..1c73185656 100644 --- a/src/data/zwave_js.ts +++ b/src/data/zwave_js.ts @@ -192,7 +192,7 @@ export interface ZWaveJSController { supported_function_types: number[]; suc_node_id: number; supports_timers: boolean; - is_heal_network_active: boolean; + is_rebuilding_routes: boolean; inclusion_state: InclusionState; nodes: ZWaveJSNodeStatus[]; } @@ -278,9 +278,9 @@ export interface ZWaveJSRefreshNodeStatusMessage { stage?: string; } -export interface ZWaveJSHealNetworkStatusMessage { +export interface ZWaveJSRebuildRoutesStatusMessage { event: string; - heal_node_status: { [key: number]: string }; + rebuild_routes_status: { [key: number]: string }; } export interface ZWaveJSControllerStatisticsUpdatedMessage { @@ -651,12 +651,12 @@ export const reinterviewZwaveNode = ( } ); -export const healZwaveNode = ( +export const rebuildZwaveNodeRoutes = ( hass: HomeAssistant, device_id: string ): Promise => hass.callWS({ - type: "zwave_js/heal_node", + type: "zwave_js/rebuild_node_routes", device_id, }); @@ -673,33 +673,33 @@ export const removeFailedZwaveNode = ( } ); -export const healZwaveNetwork = ( +export const rebuildZwaveNetworkRoutes = ( hass: HomeAssistant, entry_id: string ): Promise => hass.callWS({ - type: "zwave_js/begin_healing_network", + type: "zwave_js/begin_rebuilding_routes", entry_id, }); -export const stopHealZwaveNetwork = ( +export const stopRebuildingZwaveNetworkRoutes = ( hass: HomeAssistant, entry_id: string ): Promise => hass.callWS({ - type: "zwave_js/stop_healing_network", + type: "zwave_js/stop_rebuilding_routes", entry_id, }); -export const subscribeHealZwaveNetworkProgress = ( +export const subscribeRebuildZwaveNetworkRoutesProgress = ( hass: HomeAssistant, entry_id: string, - callbackFunction: (message: ZWaveJSHealNetworkStatusMessage) => void + callbackFunction: (message: ZWaveJSRebuildRoutesStatusMessage) => void ): Promise => hass.connection.subscribeMessage( (message: any) => callbackFunction(message), { - type: "zwave_js/subscribe_heal_network_progress", + type: "zwave_js/subscribe_rebuild_routes_progress", entry_id, } ); diff --git a/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts b/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts index 935b6377ac..c13bb56ea1 100644 --- a/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts +++ b/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts @@ -53,9 +53,8 @@ class DialogLightColorFavorite extends LitElement { ): Promise { this._entry = dialogParams.entry; this._dialogParams = dialogParams; + this._color = dialogParams.initialColor ?? this._computeCurrentColor(); this._updateModes(); - this._loadCurrentColorAndMode(dialogParams.add, dialogParams.defaultMode); - await this.updateComplete; } public closeDialog(): void { @@ -82,19 +81,20 @@ class DialogLightColorFavorite extends LitElement { } this._modes = modes; + + if (this._color) { + this._mode = "color_temp_kelvin" in this._color ? "color_temp" : "color"; + } else { + this._mode = this._modes[0]; + } } - private _loadCurrentColorAndMode( - add?: boolean, - defaultMode?: LightPickerMode - ) { + private _computeCurrentColor() { const attributes = this.stateObj!.attributes; const color_mode = attributes.color_mode; let currentColor: LightColor | undefined; - let currentMode: LightPickerMode | undefined; if (color_mode === LightColorMode.XY) { - currentMode = "color"; // XY color not supported for favorites. Try to grab the hs or rgb instead. if (attributes.hs_color) { currentColor = { hs_color: attributes.hs_color }; @@ -105,21 +105,16 @@ class DialogLightColorFavorite extends LitElement { color_mode === LightColorMode.COLOR_TEMP && attributes.color_temp_kelvin ) { - currentMode = LightColorMode.COLOR_TEMP; currentColor = { color_temp_kelvin: attributes.color_temp_kelvin, }; } else if (attributes[color_mode + "_color"]) { - currentMode = "color"; currentColor = { [color_mode + "_color"]: attributes[color_mode + "_color"], } as LightColor; } - if (add) { - this._color = currentColor; - } - this._mode = defaultMode ?? currentMode ?? this._modes[0]; + return currentColor; } private _colorChanged(ev: CustomEvent) { @@ -230,7 +225,10 @@ class DialogLightColorFavorite extends LitElement { ${this.hass.localize("ui.common.cancel")} - ${this.hass.localize("ui.common.save")} diff --git a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts index 3b1ac50329..1de238a81d 100644 --- a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts +++ b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts @@ -1,12 +1,12 @@ import { mdiCheck, mdiMinus, mdiPlus } from "@mdi/js"; import { - css, CSSResultGroup, - html, LitElement, - nothing, PropertyValues, TemplateResult, + css, + html, + nothing, } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -19,18 +19,17 @@ import { updateEntityRegistryEntry, } from "../../../../data/entity_registry"; import { - computeDefaultFavoriteColors, LightColor, LightEntity, + computeDefaultFavoriteColors, } from "../../../../data/light"; import { actionHandler } from "../../../../panels/lovelace/common/directives/action-handler-directive"; import { - loadSortable, SortableInstance, + loadSortable, } from "../../../../resources/sortable.ondemand"; import { HomeAssistant } from "../../../../types"; import { showConfirmationDialog } from "../../../generic/show-dialog-box"; -import type { LightPickerMode } from "./dialog-light-color-favorite"; import "./ha-favorite-color-button"; import { showLightColorFavoriteDialog } from "./show-dialog-light-color-favorite"; @@ -141,7 +140,6 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { private _add = async () => { const color = await showLightColorFavoriteDialog(this, { entry: this.entry!, - add: true, title: this.hass.localize( "ui.dialogs.more_info_control.light.favorite_color.add_title" ), @@ -156,13 +154,9 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { // Make sure the current favorite color is set fireEvent(this, "favorite-color-edit-started"); await this._apply(index); - const defaultMode: LightPickerMode = - "color_temp_kelvin" in this._favoriteColors[index] - ? "color_temp" - : "color"; const color = await showLightColorFavoriteDialog(this, { entry: this.entry!, - defaultMode, + initialColor: this._favoriteColors[index], title: this.hass.localize( "ui.dialogs.more_info_control.light.favorite_color.edit_title" ), diff --git a/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts b/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts index e91e3223ef..73aea4b81c 100644 --- a/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts +++ b/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts @@ -1,13 +1,11 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import { ExtEntityRegistryEntry } from "../../../../data/entity_registry"; import { LightColor } from "../../../../data/light"; -import type { LightPickerMode } from "./dialog-light-color-favorite"; export interface LightColorFavoriteDialogParams { entry: ExtEntityRegistryEntry; title: string; - defaultMode?: LightPickerMode; - add?: boolean; + initialColor?: LightColor; submit?: (color?: LightColor) => void; cancel?: () => void; } diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts index 297f4d4f8c..111ebf6e31 100644 --- a/src/fake_data/provide_hass.ts +++ b/src/fake_data/provide_hass.ts @@ -271,6 +271,7 @@ export const provideHass = ( }, dockedSidebar: "auto", vibrate: true, + debugConnection: false, suspendWhenHidden: false, moreInfoEntityId: null as any, // @ts-ignore diff --git a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts index ff37acf6f9..d6d4af86de 100644 --- a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts +++ b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts @@ -15,7 +15,7 @@ import { } from "../../../../../../data/zwave_js"; import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../../../../types"; -import { showZWaveJSHealNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node"; +import { showZWaveJSRebuildNodeRoutesDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-node-routes"; import { showZWaveJSNodeStatisticsDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-node-statistics"; import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node"; import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node"; @@ -69,10 +69,12 @@ export const getZwaveDeviceActions = async ( }), }, { - label: hass.localize("ui.panel.config.zwave_js.device_info.heal_node"), + label: hass.localize( + "ui.panel.config.zwave_js.device_info.rebuild_routes" + ), icon: mdiHospitalBox, action: () => - showZWaveJSHealNodeDialog(el, { + showZWaveJSRebuildNodeRoutesDialog(el, { device, }), }, diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-heal-network.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-network-routes.ts similarity index 74% rename from src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-heal-network.ts rename to src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-network-routes.ts index db11625195..ef0eafe475 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-heal-network.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-network-routes.ts @@ -8,18 +8,18 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; import { createCloseHeading } from "../../../../../components/ha-dialog"; import { fetchZwaveNetworkStatus, - healZwaveNetwork, - stopHealZwaveNetwork, - subscribeHealZwaveNetworkProgress, - ZWaveJSHealNetworkStatusMessage, + rebuildZwaveNetworkRoutes, + stopRebuildingZwaveNetworkRoutes, + subscribeRebuildZwaveNetworkRoutesProgress, + ZWaveJSRebuildRoutesStatusMessage, ZWaveJSNetwork, } from "../../../../../data/zwave_js"; import { haStyleDialog } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; -import { ZWaveJSHealNetworkDialogParams } from "./show-dialog-zwave_js-heal-network"; +import { ZWaveJSRebuildNetworkRoutesDialogParams } from "./show-dialog-zwave_js-rebuild-network-routes"; -@customElement("dialog-zwave_js-heal-network") -class DialogZWaveJSHealNetwork extends LitElement { +@customElement("dialog-zwave_js-rebuild-network-routes") +class DialogZWaveJSRebuildNetworkRoutes extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @state() private entry_id?: string; @@ -34,7 +34,7 @@ class DialogZWaveJSHealNetwork extends LitElement { private _subscribed?: Promise; - public showDialog(params: ZWaveJSHealNetworkDialogParams): void { + public showDialog(params: ZWaveJSRebuildNetworkRoutesDialogParams): void { this._progress_total = 0; this.entry_id = params.entry_id; this._fetchData(); @@ -61,7 +61,9 @@ class DialogZWaveJSHealNetwork extends LitElement { @closed=${this.closeDialog} .heading=${createCloseHeading( this.hass, - this.hass.localize("ui.panel.config.zwave_js.heal_network.title") + this.hass.localize( + "ui.panel.config.zwave_js.rebuild_network_routes.title" + ) )} > ${!this._status @@ -74,7 +76,7 @@ class DialogZWaveJSHealNetwork extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.introduction" + "ui.panel.config.zwave_js.rebuild_network_routes.introduction" )}

@@ -82,13 +84,16 @@ class DialogZWaveJSHealNetwork extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.traffic_warning" + "ui.panel.config.zwave_js.rebuild_network_routes.traffic_warning" )}

- + ${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.start_heal" + "ui.panel.config.zwave_js.rebuild_network_routes.start_rebuilding_routes" )} ` @@ -99,13 +104,13 @@ class DialogZWaveJSHealNetwork extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.in_progress" + "ui.panel.config.zwave_js.rebuild_network_routes.in_progress" )}

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.run_in_background" + "ui.panel.config.zwave_js.rebuild_network_routes.run_in_background" )}

@@ -114,9 +119,12 @@ class DialogZWaveJSHealNetwork extends LitElement { ` : ""} - + ${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.stop_heal" + "ui.panel.config.zwave_js.rebuild_network_routes.stop_rebuilding_routes" )} @@ -134,7 +142,7 @@ class DialogZWaveJSHealNetwork extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.healing_failed" + "ui.panel.config.zwave_js.rebuild_network_routes.rebuilding_routes_failed" )}

@@ -154,7 +162,7 @@ class DialogZWaveJSHealNetwork extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.healing_complete" + "ui.panel.config.zwave_js.rebuild_network_routes.rebuilding_routes_complete" )}

@@ -174,7 +182,7 @@ class DialogZWaveJSHealNetwork extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_network.healing_cancelled" + "ui.panel.config.zwave_js.rebuild_network_routes.rebuilding_routes_cancelled" )}

@@ -205,9 +213,9 @@ class DialogZWaveJSHealNetwork extends LitElement { const network: ZWaveJSNetwork = await fetchZwaveNetworkStatus(this.hass!, { entry_id: this.entry_id!, }); - if (network.controller.is_heal_network_active) { + if (network.controller.is_rebuilding_routes) { this._status = "started"; - this._subscribed = subscribeHealZwaveNetworkProgress( + this._subscribed = subscribeRebuildZwaveNetworkRoutesProgress( this.hass, this.entry_id!, this._handleMessage.bind(this) @@ -215,33 +223,33 @@ class DialogZWaveJSHealNetwork extends LitElement { } } - private _startHeal(): void { + private _startRebuildingRoutes(): void { if (!this.hass) { return; } - healZwaveNetwork(this.hass, this.entry_id!); + rebuildZwaveNetworkRoutes(this.hass, this.entry_id!); this._status = "started"; - this._subscribed = subscribeHealZwaveNetworkProgress( + this._subscribed = subscribeRebuildZwaveNetworkRoutesProgress( this.hass, this.entry_id!, this._handleMessage.bind(this) ); } - private _stopHeal(): void { + private _stopRebuildingRoutes(): void { if (!this.hass) { return; } - stopHealZwaveNetwork(this.hass, this.entry_id!); + stopRebuildingZwaveNetworkRoutes(this.hass, this.entry_id!); this._unsubscribe(); this._status = "cancelled"; } - private _handleMessage(message: ZWaveJSHealNetworkStatusMessage): void { - if (message.event === "heal network progress") { + private _handleMessage(message: ZWaveJSRebuildRoutesStatusMessage): void { + if (message.event === "rebuild routes progress") { let finished = 0; let in_progress = 0; - for (const status of Object.values(message.heal_node_status)) { + for (const status of Object.values(message.rebuild_routes_status)) { if (status === "pending") { in_progress++; } @@ -249,11 +257,11 @@ class DialogZWaveJSHealNetwork extends LitElement { finished++; } } - this._progress_total = Object.keys(message.heal_node_status).length; + this._progress_total = Object.keys(message.rebuild_routes_status).length; this._progress_finished = finished / this._progress_total; this._progress_in_progress = in_progress / this._progress_total; } - if (message.event === "heal network done") { + if (message.event === "rebuild routes done") { this._unsubscribe(); this._status = "finished"; } @@ -306,6 +314,6 @@ class DialogZWaveJSHealNetwork extends LitElement { declare global { interface HTMLElementTagNameMap { - "dialog-zwave_js-heal-network": DialogZWaveJSHealNetwork; + "dialog-zwave_js-rebuild-network-routes": DialogZWaveJSRebuildNetworkRoutes; } } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-heal-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts similarity index 80% rename from src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-heal-node.ts rename to src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts index 776f316f44..46df0f7edc 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-heal-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-rebuild-node-routes.ts @@ -11,15 +11,15 @@ import { } from "../../../../../data/device_registry"; import { fetchZwaveNetworkStatus, - healZwaveNode, + rebuildZwaveNodeRoutes, ZWaveJSNetwork, } from "../../../../../data/zwave_js"; import { haStyleDialog } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; -import { ZWaveJSHealNodeDialogParams } from "./show-dialog-zwave_js-heal-node"; +import { ZWaveJSRebuildNodeRoutesDialogParams } from "./show-dialog-zwave_js-rebuild-node-routes"; -@customElement("dialog-zwave_js-heal-node") -class DialogZWaveJSHealNode extends LitElement { +@customElement("dialog-zwave_js-rebuild-node-routes") +class DialogZWaveJSRebuildNodeRoutes extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @state() private device?: DeviceRegistryEntry; @@ -28,7 +28,7 @@ class DialogZWaveJSHealNode extends LitElement { @state() private _error?: string; - public showDialog(params: ZWaveJSHealNodeDialogParams): void { + public showDialog(params: ZWaveJSRebuildNodeRoutesDialogParams): void { this.device = params.device; this._fetchData(); } @@ -52,7 +52,9 @@ class DialogZWaveJSHealNode extends LitElement { @closed=${this.closeDialog} .heading=${createCloseHeading( this.hass, - this.hass.localize("ui.panel.config.zwave_js.heal_node.title") + this.hass.localize( + "ui.panel.config.zwave_js.rebuild_node_routes.title" + ) )} > ${!this._status @@ -65,7 +67,7 @@ class DialogZWaveJSHealNode extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.introduction", + "ui.panel.config.zwave_js.rebuild_node_routes.introduction", { device: html`${computeDeviceName(this.device, this.hass!)} ${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.traffic_warning" + "ui.panel.config.zwave_js.rebuild_node_routes.traffic_warning" )}

- + ${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.start_heal" + "ui.panel.config.zwave_js.rebuild_node_routes.start_rebuilding_routes" )} ` @@ -96,7 +101,7 @@ class DialogZWaveJSHealNode extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.in_progress", + "ui.panel.config.zwave_js.rebuild_node_routes.in_progress", { device: html`${computeDeviceName(this.device, this.hass!)}

${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.healing_failed", + "ui.panel.config.zwave_js.rebuild_node_routes.rebuilding_routes_failed", { device: html`${computeDeviceName(this.device, this.hass!)}${this._error} ` : ` ${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.healing_failed_check_logs" + "ui.panel.config.zwave_js.rebuild_node_routes.rebuilding_routes_failed_check_logs" )} `}

@@ -155,7 +160,7 @@ class DialogZWaveJSHealNode extends LitElement {

${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.healing_complete", + "ui.panel.config.zwave_js.rebuild_node_routes.rebuilding_routes_complete", { device: html`${computeDeviceName(this.device, this.hass!)} ` : ``} - ${this._status === "network-healing" + ${this._status === "rebuilding-routes" ? html`

${this.hass.localize( - "ui.panel.config.zwave_js.heal_node.network_heal_in_progress" + "ui.panel.config.zwave_js.rebuild_node_routes.routes_rebuild_in_progress" )}

@@ -201,18 +206,18 @@ class DialogZWaveJSHealNode extends LitElement { const network: ZWaveJSNetwork = await fetchZwaveNetworkStatus(this.hass!, { device_id: this.device!.id, }); - if (network.controller.is_heal_network_active) { - this._status = "network-healing"; + if (network.controller.is_rebuilding_routes) { + this._status = "rebuilding-routes"; } } - private async _startHeal(): Promise { + private async _startRebuildingRoutes(): Promise { if (!this.hass) { return; } this._status = "started"; try { - this._status = (await healZwaveNode(this.hass, this.device!.id)) + this._status = (await rebuildZwaveNodeRoutes(this.hass, this.device!.id)) ? "finished" : "failed"; } catch (err: any) { @@ -258,6 +263,6 @@ class DialogZWaveJSHealNode extends LitElement { declare global { interface HTMLElementTagNameMap { - "dialog-zwave_js-heal-node": DialogZWaveJSHealNode; + "dialog-zwave_js-rebuild-node-routes": DialogZWaveJSRebuildNodeRoutes; } } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-network.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-network.ts deleted file mode 100644 index cb46ad9d43..0000000000 --- a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-network.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { fireEvent } from "../../../../../common/dom/fire_event"; - -export interface ZWaveJSHealNetworkDialogParams { - entry_id: string; -} - -export const loadHealNetworkDialog = () => - import("./dialog-zwave_js-heal-network"); - -export const showZWaveJSHealNetworkDialog = ( - element: HTMLElement, - healNetworkDialogParams: ZWaveJSHealNetworkDialogParams -): void => { - fireEvent(element, "show-dialog", { - dialogTag: "dialog-zwave_js-heal-network", - dialogImport: loadHealNetworkDialog, - dialogParams: healNetworkDialogParams, - }); -}; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node.ts deleted file mode 100644 index 047cebdc47..0000000000 --- a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { fireEvent } from "../../../../../common/dom/fire_event"; -import { DeviceRegistryEntry } from "../../../../../data/device_registry"; - -export interface ZWaveJSHealNodeDialogParams { - device: DeviceRegistryEntry; -} - -export const loadHealNodeDialog = () => import("./dialog-zwave_js-heal-node"); - -export const showZWaveJSHealNodeDialog = ( - element: HTMLElement, - healNodeDialogParams: ZWaveJSHealNodeDialogParams -): void => { - fireEvent(element, "show-dialog", { - dialogTag: "dialog-zwave_js-heal-node", - dialogImport: loadHealNodeDialog, - dialogParams: healNodeDialogParams, - }); -}; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-network-routes.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-network-routes.ts new file mode 100644 index 0000000000..56d7791b7f --- /dev/null +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-network-routes.ts @@ -0,0 +1,19 @@ +import { fireEvent } from "../../../../../common/dom/fire_event"; + +export interface ZWaveJSRebuildNetworkRoutesDialogParams { + entry_id: string; +} + +export const loadRebuildNetworkRoutesDialog = () => + import("./dialog-zwave_js-rebuild-network-routes"); + +export const showZWaveJSRebuildNetworkRoutesDialog = ( + element: HTMLElement, + rebuildNetworkRoutesDialogParams: ZWaveJSRebuildNetworkRoutesDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-zwave_js-rebuild-network-routes", + dialogImport: loadRebuildNetworkRoutesDialog, + dialogParams: rebuildNetworkRoutesDialogParams, + }); +}; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-node-routes.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-node-routes.ts new file mode 100644 index 0000000000..073c19127f --- /dev/null +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-node-routes.ts @@ -0,0 +1,20 @@ +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { DeviceRegistryEntry } from "../../../../../data/device_registry"; + +export interface ZWaveJSRebuildNodeRoutesDialogParams { + device: DeviceRegistryEntry; +} + +export const loadRebuildNodeRoutesDialog = () => + import("./dialog-zwave_js-rebuild-node-routes"); + +export const showZWaveJSRebuildNodeRoutesDialog = ( + element: HTMLElement, + rebuildNodeRoutesDialogParams: ZWaveJSRebuildNodeRoutesDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-zwave_js-rebuild-node-routes", + dialogImport: loadRebuildNodeRoutesDialog, + dialogParams: rebuildNodeRoutesDialogParams, + }); +}; 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 5fedec41f8..4c9a309a10 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 @@ -52,7 +52,7 @@ import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant, Route } from "../../../../../types"; import "../../../ha-config-section"; import { showZWaveJSAddNodeDialog } from "./show-dialog-zwave_js-add-node"; -import { showZWaveJSHealNetworkDialog } from "./show-dialog-zwave_js-heal-network"; +import { showZWaveJSRebuildNetworkRoutesDialog } from "./show-dialog-zwave_js-rebuild-network-routes"; import { showZWaveJSRemoveNodeDialog } from "./show-dialog-zwave_js-remove-node"; import { configTabs } from "./zwave_js-config-router"; @@ -430,11 +430,11 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { )} ${this.hass.localize( - "ui.panel.config.zwave_js.common.heal_network" + "ui.panel.config.zwave_js.common.rebuild_network_routes" )} @@ -612,8 +612,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { }); } - private async _healNetworkClicked() { - showZWaveJSHealNetworkDialog(this, { + private async _rebuildNetworkRoutesClicked() { + showZWaveJSRebuildNetworkRoutesDialog(this, { entry_id: this.configEntryId!, }); } diff --git a/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-wakeword.ts b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-wakeword.ts index dfa6f0fd39..316d21b516 100644 --- a/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-wakeword.ts +++ b/src/panels/config/voice-assistants/assist-pipeline-detail/assist-pipeline-detail-wakeword.ts @@ -1,4 +1,11 @@ -import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, +} from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { LocalizeKeys } from "../../../../common/translations/localize"; @@ -6,6 +13,7 @@ import "../../../../components/ha-form/ha-form"; import { AssistPipeline } from "../../../../data/assist_pipeline"; import { HomeAssistant } from "../../../../types"; import { fetchWakeWordInfo, WakeWord } from "../../../../data/wake_word"; +import { documentationUrl } from "../../../../util/documentation-url"; @customElement("assist-pipeline-detail-wakeword") export class AssistPipelineDetailWakeWord extends LitElement { @@ -67,7 +75,12 @@ export class AssistPipelineDetailWakeWord extends LitElement { } } + private _hasWakeWorkEntities = memoizeOne((states: HomeAssistant["states"]) => + Object.keys(states).some((entityId) => entityId.startsWith("wake_word.")) + ); + protected render() { + const hasWakeWorkEntities = this._hasWakeWorkEntities(this.hass.states); return html`
@@ -83,11 +96,25 @@ export class AssistPipelineDetailWakeWord extends LitElement { )}

+ ${!hasWakeWorkEntities + ? html`${this.hass.localize( + `ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.no_wake_words` + )} + ${this.hass.localize( + `ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.no_wake_words_link` + )}` + : nothing}
@@ -129,6 +156,9 @@ export class AssistPipelineDetailWakeWord extends LitElement { margin-top: 0; margin-bottom: 0; } + a { + color: var(--primary-color); + } `; } } diff --git a/src/panels/config/voice-assistants/assist-pref.ts b/src/panels/config/voice-assistants/assist-pref.ts index 65665b9ce7..f38173e84f 100644 --- a/src/panels/config/voice-assistants/assist-pref.ts +++ b/src/panels/config/voice-assistants/assist-pref.ts @@ -25,6 +25,7 @@ import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box import type { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assistant-pipeline-detail"; +import { documentationUrl } from "../../../util/documentation-url"; export class AssistPref extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -73,9 +74,9 @@ export class AssistPref extends LitElement {
*:not(:last-child) { margin-bottom: 16px; display: block; } diff --git a/src/panels/developer-tools/debug/developer-tools-debug.ts b/src/panels/developer-tools/debug/developer-tools-debug.ts new file mode 100644 index 0000000000..6a5aa730cd --- /dev/null +++ b/src/panels/developer-tools/debug/developer-tools-debug.ts @@ -0,0 +1,52 @@ +import { CSSResultGroup, LitElement, css, html } from "lit"; +import { customElement, property } from "lit/decorators"; +import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import "./ha-debug-connection-row"; + +@customElement("developer-tools-debug") +class HaPanelDevDebug extends SubscribeMixin(LitElement) { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow!: boolean; + + protected render() { + return html` +
+ +
+ + +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .content { + padding: 28px 20px 16px; + display: block; + max-width: 600px; + margin: 0 auto; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "developer-tools-debug": HaPanelDevDebug; + } +} diff --git a/src/panels/developer-tools/debug/ha-debug-connection-row.ts b/src/panels/developer-tools/debug/ha-debug-connection-row.ts new file mode 100644 index 0000000000..e4b1c5c3ff --- /dev/null +++ b/src/panels/developer-tools/debug/ha-debug-connection-row.ts @@ -0,0 +1,50 @@ +import { html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../components/ha-settings-row"; +import "../../../components/ha-switch"; +import type { HaSwitch } from "../../../components/ha-switch"; +import type { HomeAssistant } from "../../../types"; +import { storeState } from "../../../util/ha-pref-storage"; + +@customElement("ha-debug-connection-row") +class HaDebugConnectionRow extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public narrow!: boolean; + + protected render(): TemplateResult { + return html` + + + ${this.hass.localize( + "ui.panel.developer-tools.tabs.debug.debug_connection.title" + )} + + + ${this.hass.localize( + "ui.panel.developer-tools.tabs.debug.debug_connection.description" + )} + + + + `; + } + + private async _checkedChanged(ev: Event) { + const debugConnection = (ev.target as HaSwitch).checked; + if (debugConnection === this.hass.debugConnection) { + return; + } + this.hass.debugConnection = debugConnection; + storeState(this.hass); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-debug-connection-row": HaDebugConnectionRow; + } +} diff --git a/src/panels/developer-tools/developer-tools-router.ts b/src/panels/developer-tools/developer-tools-router.ts index 5b8baf3899..72b9795a2c 100644 --- a/src/panels/developer-tools/developer-tools-router.ts +++ b/src/panels/developer-tools/developer-tools-router.ts @@ -49,6 +49,10 @@ class DeveloperToolsRouter extends HassRouterPage { tag: "developer-tools-assist", load: () => import("./assist/developer-tools-assist"), }, + debug: { + tag: "developer-tools-debug", + load: () => import("./debug/developer-tools-debug"), + }, }, }; diff --git a/src/panels/developer-tools/event/developer-tools-event.js b/src/panels/developer-tools/event/developer-tools-event.js deleted file mode 100644 index b4925bf130..0000000000 --- a/src/panels/developer-tools/event/developer-tools-event.js +++ /dev/null @@ -1,219 +0,0 @@ -import "@material/mwc-button"; -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { load } from "js-yaml"; -import "../../../components/ha-code-editor"; -import "../../../components/ha-textfield"; -import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; -import { EventsMixin } from "../../../mixins/events-mixin"; -import LocalizeMixin from "../../../mixins/localize-mixin"; -import "../../../styles/polymer-ha-style"; -import { documentationUrl } from "../../../util/documentation-url"; -import "./event-subscribe-card"; -import "./events-list"; - -const ERROR_SENTINEL = {}; -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) { - static get template() { - return html` - - - -
-
-

- [[localize( 'ui.panel.developer-tools.tabs.events.description' )]] - - [[localize( 'ui.panel.developer-tools.tabs.events.documentation' - )]] - -

-
- -

[[localize( 'ui.panel.developer-tools.tabs.events.data' )]]

-
-
- -
- [[localize( 'ui.panel.developer-tools.tabs.events.fire_event' - )]] - -
- -
-
- [[localize( 'ui.panel.developer-tools.tabs.events.active_listeners' - )]] -
- -
-
- `; - } - - static get properties() { - return { - hass: { - type: Object, - }, - - eventType: { - type: String, - value: "", - }, - - eventData: { - type: String, - value: "", - }, - - parsedJSON: { - type: Object, - computed: "_computeParsedEventData(eventData)", - }, - - validJSON: { - type: Boolean, - computed: "_computeValidJSON(parsedJSON)", - }, - }; - } - - eventSelected(ev) { - this.eventType = ev.detail.eventType; - } - - eventTypeChanged(ev) { - this.eventType = ev.target.value; - } - - _computeParsedEventData(eventData) { - try { - return eventData.trim() ? load(eventData) : {}; - } catch (err) { - return ERROR_SENTINEL; - } - } - - _computeDocumentationUrl(hass) { - return documentationUrl(hass, "/docs/configuration/events/"); - } - - _computeValidJSON(parsedJSON) { - return parsedJSON !== ERROR_SENTINEL; - } - - _yamlChanged(ev) { - this.eventData = ev.detail.value; - } - - fireEvent() { - if (!this.eventType) { - showAlertDialog(this, { - text: this.hass.localize( - "ui.panel.developer-tools.tabs.events.alert_event_type" - ), - }); - return; - } - this.hass - .callApi("POST", "events/" + this.eventType, this.parsedJSON) - .then(() => { - this.fire("hass-notification", { - message: this.hass.localize( - "ui.panel.developer-tools.tabs.events.notification_event_fired", - "type", - this.eventType - ), - }); - }); - } - - computeFormClasses(narrow) { - return narrow ? "content" : "content layout horizontal"; - } -} - -customElements.define("developer-tools-event", HaPanelDevEvent); diff --git a/src/panels/developer-tools/event/developer-tools-event.ts b/src/panels/developer-tools/event/developer-tools-event.ts new file mode 100644 index 0000000000..f0885df338 --- /dev/null +++ b/src/panels/developer-tools/event/developer-tools-event.ts @@ -0,0 +1,184 @@ +import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "@material/mwc-button"; +import "../../../components/ha-yaml-editor"; +import "../../../components/ha-textfield"; +import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; +import { documentationUrl } from "../../../util/documentation-url"; +import "./event-subscribe-card"; +import "./events-list"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import { fireEvent } from "../../../common/dom/fire_event"; + +@customElement("developer-tools-event") +class HaPanelDevEvent extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow!: boolean; + + @state() private _eventType: string = ""; + + @state() private _eventData: object = {}; + + @state() private _isValid: boolean = true; + + protected render(): TemplateResult { + return html` +
+
+

+ ${this.hass.localize( + "ui.panel.developer-tools.tabs.events.description" + )} + + ${this.hass.localize( + "ui.panel.developer-tools.tabs.events.documentation" + )} + +

+
+ +

+ ${this.hass.localize("ui.panel.developer-tools.tabs.events.data")} +

+
+
+ +
+ ${this.hass.localize( + "ui.panel.developer-tools.tabs.events.fire_event" + )} + +
+ +
+
+ ${this.hass.localize( + "ui.panel.developer-tools.tabs.events.active_listeners" + )} +
+ +
+
+ `; + } + + private _eventSelected(ev) { + this._eventType = ev.detail.eventType; + } + + private _eventTypeChanged(ev) { + this._eventType = ev.target.value; + } + + private _yamlChanged(ev) { + this._eventData = ev.detail.value; + this._isValid = ev.detail.isValid; + } + + private async _fireEvent() { + if (!this._eventType) { + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.developer-tools.tabs.events.alert_event_type" + ), + }); + return; + } + await this.hass.callApi( + "POST", + `events/${this._eventType}`, + this._eventData + ); + fireEvent(this, "hass-notification", { + message: this.hass.localize( + "ui.panel.developer-tools.tabs.events.notification_event_fired", + { type: this._eventType } + ), + }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .content { + padding: 16px; + padding: max(16px, env(safe-area-inset-top)) + max(16px, env(safe-area-inset-right)) + max(16px, env(safe-area-inset-bottom)) + max(16px, env(safe-area-inset-left)); + max-width: 1200px; + margin: auto; + } + + :host { + -ms-user-select: initial; + -webkit-user-select: initial; + -moz-user-select: initial; + @apply --paper-font-body1; + display: block; + } + + .inputs { + max-width: 400px; + } + + mwc-button { + margin-top: 8px; + } + + ha-textfield { + display: block; + } + + .header { + @apply --paper-font-title; + } + + event-subscribe-card { + display: block; + margin: 16px 16px 0 0; + margin-inline-start: initial; + margin-inline-end: 16px; + direction: var(--direction); + } + + a { + color: var(--primary-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "developer-tools-event": HaPanelDevEvent; + } +} diff --git a/src/panels/developer-tools/event/events-list.js b/src/panels/developer-tools/event/events-list.js deleted file mode 100644 index a6893d269e..0000000000 --- a/src/panels/developer-tools/event/events-list.js +++ /dev/null @@ -1,73 +0,0 @@ -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { stringCompare } from "../../../common/string/compare"; -import { EventsMixin } from "../../../mixins/events-mixin"; -import LocalizeMixin from "../../../mixins/localize-mixin"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -class EventsList extends EventsMixin(LocalizeMixin(PolymerElement)) { - static get template() { - return html` - - -
    - -
- `; - } - - static get properties() { - return { - hass: { - type: Object, - }, - - events: { - type: Array, - }, - }; - } - - connectedCallback() { - super.connectedCallback(); - this.hass.callApi("GET", "events").then((events) => { - this.events = events.sort((e1, e2) => - stringCompare(e1.event, e2.event, this.hass.locale.language) - ); - }); - } - - eventSelected(ev) { - ev.preventDefault(); - this.fire("event-selected", { eventType: ev.model.event.event }); - } -} - -customElements.define("events-list", EventsList); diff --git a/src/panels/developer-tools/event/events-list.ts b/src/panels/developer-tools/event/events-list.ts new file mode 100644 index 0000000000..7007ca6eab --- /dev/null +++ b/src/panels/developer-tools/event/events-list.ts @@ -0,0 +1,84 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { stringCompare } from "../../../common/string/compare"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { HomeAssistant } from "../../../types"; + +interface EventListenerCount { + event: string; + listener_count: number; +} + +@customElement("events-list") +class EventsList extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public events: EventListenerCount[] = []; + + protected render(): TemplateResult { + return html` +
    + ${this.events.map( + (event) => html` +
  • + ${event.event} + + ${this.hass.localize( + "ui.panel.developer-tools.tabs.events.count_listeners", + { + count: event.listener_count, + } + )} +
  • + ` + )} +
+ `; + } + + protected async firstUpdated() { + const events = await this.hass.callApi( + "GET", + "events" + ); + this.events = events.sort((e1, e2) => + stringCompare(e1.event, e2.event, this.hass.locale.language) + ); + } + + private _eventSelected(ev: Event) { + ev.preventDefault(); + const event: string = (ev.currentTarget! as any).event; + fireEvent(this, "event-selected", { eventType: event }); + } + + static get styles(): CSSResultGroup { + return css` + ul { + margin: 0; + padding: 0; + } + + li { + list-style: none; + line-height: 2em; + } + + a { + color: var(--primary-color); + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "events-list": EventsList; + } + interface HASSDomEvents { + "event-selected": { eventType: string }; + } +} diff --git a/src/panels/developer-tools/ha-panel-developer-tools.ts b/src/panels/developer-tools/ha-panel-developer-tools.ts index 813cc014e7..de7ec5777c 100644 --- a/src/panels/developer-tools/ha-panel-developer-tools.ts +++ b/src/panels/developer-tools/ha-panel-developer-tools.ts @@ -1,9 +1,14 @@ +import { mdiDotsVertical } from "@mdi/js"; import "@polymer/paper-tabs/paper-tab"; import "@polymer/paper-tabs/paper-tabs"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; +import type { ActionDetail } from "@material/mwc-list"; import { navigate } from "../../common/navigate"; import "../../components/ha-menu-button"; +import "../../components/ha-button-menu"; +import "../../components/ha-icon-button"; +import "../../components/ha-list-item"; import { haStyle } from "../../resources/styles"; import { HomeAssistant, Route } from "../../types"; import "./developer-tools-router"; @@ -34,6 +39,16 @@ class PanelDeveloperTools extends LitElement {
${this.hass.localize("panel.developer_tools")}
+ + + + ${this.hass.localize("ui.panel.developer-tools.tabs.debug.title")} + +
) { + switch (ev.detail.index) { + case 0: + navigate(`/developer-tools/debug`); + break; + } + } + private get _page() { return this.route.path.substr(1); } diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts index bfcbabfd70..08113c821a 100644 --- a/src/state/connection-mixin.ts +++ b/src/state/connection-mixin.ts @@ -73,6 +73,7 @@ export const connectionMixin = >( translationMetadata, dockedSidebar: "docked", vibrate: true, + debugConnection: false, suspendWhenHidden: true, enableShortcuts: true, moreInfoEntityId: null, @@ -84,7 +85,7 @@ export const connectionMixin = >( target, notifyOnError = true ) => { - if (__DEV__) { + if (__DEV__ || this.hass?.debugConnection) { // eslint-disable-next-line no-console console.log( "Calling service", @@ -109,7 +110,7 @@ export const connectionMixin = >( ) { return { context: { id: "" } }; } - if (__DEV__) { + if (__DEV__ || this.hass?.debugConnection) { // eslint-disable-next-line no-console console.error( "Error calling service", @@ -146,7 +147,7 @@ export const connectionMixin = >( ) => fetchWithAuth(auth, `${auth.data.hassUrl}${path}`, init), // For messages that do not get a response sendWS: (msg) => { - if (__DEV__) { + if (__DEV__ || this.hass?.debugConnection) { // eslint-disable-next-line no-console console.log("Sending", msg); } @@ -154,14 +155,14 @@ export const connectionMixin = >( }, // For messages that expect a response callWS: (msg) => { - if (__DEV__) { + if (__DEV__ || this.hass?.debugConnection) { // eslint-disable-next-line no-console console.log("Sending", msg); } const resp = conn.sendMessagePromise(msg); - if (__DEV__) { + if (__DEV__ || this.hass?.debugConnection) { resp.then( // eslint-disable-next-line no-console (result) => console.log("Received", result), diff --git a/src/translations/en.json b/src/translations/en.json index c21d6da366..3c9bfce684 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2196,7 +2196,9 @@ }, "wakeword": { "title": "Wake word", - "description": "If a device supports wake words, you can activate Assist by saying this word." + "description": "If a device supports wake words, you can activate Assist by saying this word.", + "no_wake_words": "It looks like you don't have a wake word engine setup yet.", + "no_wake_words_link": "Find out more about wake words." } }, "no_cloud_message": "You should have an active cloud subscription to use cloud speech services.", @@ -2446,7 +2448,12 @@ "mode_below": "Below mode", "value_template": "Value template", "type_value": "Fixed number", - "type_input": "Numeric value of another entity" + "type_input": "Numeric value of another entity", + "description": { + "above": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} is above {above}{duration, select, \n undefined {} \n other { for {duration}}\n }", + "below": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} is below {below}{duration, select, \n undefined {} \n other { for {duration}}\n }", + "above-below": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} is above {above} and below {below}{duration, select, \n undefined {} \n other { for {duration}}\n }" + } }, "persistent_notification": { "label": "Persistent notification", @@ -2595,7 +2602,12 @@ "below": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::below%]", "mode_above": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::mode_above%]", "mode_below": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::mode_below%]", - "value_template": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::value_template%]" + "value_template": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::value_template%]", + "description": { + "above": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} is above {above}", + "below": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} is below {below}", + "above-below": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} is above {above} and below {below}" + } }, "or": { "label": "Or", @@ -3922,7 +3934,7 @@ "add_node": "Add device", "remove_node": "Remove device", "reconfigure_server": "Re-configure server", - "heal_network": "Heal Network", + "rebuild_network_routes": "Rebuild network routes", "in_progress_inclusion_exclusion": "Z-Wave JS is searching for devices", "cancel_inclusion_exclusion": "Stop searching" }, @@ -3982,7 +3994,7 @@ "node_ready": "Ready", "device_config": "Configure", "reinterview_device": "Re-interview", - "heal_node": "Heal", + "rebuild_routes": "Rebuild routes", "remove_failed": "Remove failed", "update_firmware": "Update", "highest_security": "Highest security", @@ -4164,28 +4176,28 @@ "interview_failed": "The device interview failed. Additional information may be available in the logs.", "interview_complete": "Device interview complete." }, - "heal_network": { - "title": "Heal your Z-Wave network", - "introduction": "Start a network heal on your Z-Wave network. A network heal will cause all devices to re-calculate their routes back to the controller and is recommended if you have recently moved devices or your controller.", - "traffic_warning": "The healing process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the heal is in progress.", - "start_heal": "Start healing", - "in_progress": "Network healing is in progress. This will take some time.", - "run_in_background": "You can close this dialog and the network healing will continue in the background.", - "stop_heal": "Stop Healing", - "healing_complete": "Network healing is complete.", - "healing_failed": "Healing failed. Additional information may be available in the logs.", - "healing_cancelled": "Network healing has been cancelled." + "rebuild_network_routes": { + "title": "Rebuild routes for your Z-Wave network", + "introduction": "Start rebuilding routes on your Z-Wave network. Rebuilding routes will cause all devices to re-calculate their routes back to the controller and is recommended if you have recently moved devices or your controller.", + "traffic_warning": "The rebuilding process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the rebuild is in progress.", + "start_rebuilding_routes": "Start rebuilding routes", + "in_progress": "Rebuild of network routes is in progress. This will take some time.", + "run_in_background": "You can close this dialog and the rebuild of network routes will continue in the background.", + "stop_rebuilding_routes": "Stop rebuilding routes", + "rebuilding_routes_complete": "Routes have been rebuilt.", + "rebuilding_routes_failed": "Route rebuilding failed. Additional information may be available in the logs.", + "rebuilding_routes_cancelled": "Rebuilding network routes has been cancelled." }, - "heal_node": { - "title": "Heal a Z-Wave device", + "rebuild_node_routes": { + "title": "Rebuild routes for a Z-Wave device", "introduction": "Tell {device} to update its routes back to the controller. This can help with communication issues if you have recently moved the device or your controller.", - "traffic_warning": "The healing process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the heal is in progress.", - "start_heal": "Heal Device", - "healing_failed": "{device} could not be healed.", - "healing_failed_check_logs": "Additional information may be available in the logs.", - "healing_complete": "{device} has been healed.", - "in_progress": "{device} healing is in progress.", - "network_heal_in_progress": "A Z-Wave network heal is already in progress. Please wait for it to finish before healing an individual device." + "traffic_warning": "The route rebuilding process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the rebuilding is in progress.", + "start_rebuilding_routes": "Rebuild Routes for Device", + "rebuilding_routes_failed": "{device} routes could not be rebuild.", + "rebuilding_routes_failed_check_logs": "Additional information may be available in the logs.", + "rebuilding_routes_complete": "{device} routes have been rebuilt.", + "in_progress": "{device} routes rebuild is in progress.", + "routes_rebuild_in_progress": "A Z-Wave routes rebuild is already in progress. Please wait for it to finish before rebuilding routes for an individual device." }, "update_firmware": { "title": "Update device firmware", @@ -5562,6 +5574,13 @@ "no_match": "No intent matched", "language": "[%key:ui::components::language-picker::language%]" }, + "debug": { + "title": "Debug tools", + "debug_connection": { + "title": "Debug connection", + "description": "Observe requests to the server and responses from the server in browser console." + } + }, "events": { "title": "Events", "description": "Fire an event on the event bus.", diff --git a/src/types.ts b/src/types.ts index ced909e0ef..8d0d37ee86 100644 --- a/src/types.ts +++ b/src/types.ts @@ -230,6 +230,7 @@ export interface HomeAssistant { suspendWhenHidden: boolean; enableShortcuts: boolean; vibrate: boolean; + debugConnection: boolean; dockedSidebar: "docked" | "always_hidden" | "auto"; defaultPanel: string; moreInfoEntityId: string | null; diff --git a/src/util/ha-pref-storage.ts b/src/util/ha-pref-storage.ts index eb9b722914..3b3bb9bff7 100644 --- a/src/util/ha-pref-storage.ts +++ b/src/util/ha-pref-storage.ts @@ -5,6 +5,7 @@ const STORED_STATE = [ "selectedTheme", "selectedLanguage", "vibrate", + "debugConnection", "suspendWhenHidden", "enableShortcuts", "defaultPanel", diff --git a/yarn.lock b/yarn.lock index 1b7c5fefb0..3b1cce76ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4539,12 +4539,12 @@ __metadata: languageName: node linkType: hard -"@types/serve-handler@npm:6.1.1": - version: 6.1.1 - resolution: "@types/serve-handler@npm:6.1.1" +"@types/serve-handler@npm:6.1.2": + version: 6.1.2 + resolution: "@types/serve-handler@npm:6.1.2" dependencies: "@types/node": "*" - checksum: f519f83b18d7dea80f188f387a56dfe30fe944196849c470902fabf9db344c083c470f67e3362ad3f2294512950bc55924282c5c7a911ec8d507c37c6861d8ce + checksum: 6b206cba18bf77b938e340e1015c631d92b492918ba78dfdff57590a25da8c4bc63fba90f43d763f854c5aa523b243c283ba84d14cfeb1841795337c8b978bd6 languageName: node linkType: hard @@ -9072,9 +9072,9 @@ __metadata: linkType: hard "get-func-name@npm:^2.0.0": - version: 2.0.0 - resolution: "get-func-name@npm:2.0.0" - checksum: 8d82e69f3e7fab9e27c547945dfe5cc0c57fc0adf08ce135dddb01081d75684a03e7a0487466f478872b341d52ac763ae49e660d01ab83741f74932085f693c3 + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 3f62f4c23647de9d46e6f76d2b3eafe58933a9b3830c60669e4180d6c601ce1b4aa310ba8366143f55e52b139f992087a9f0647274e8745621fa2af7e0acf13b languageName: node linkType: hard @@ -9722,7 +9722,7 @@ __metadata: "@types/luxon": 3.3.2 "@types/mocha": 10.0.1 "@types/qrcode": 1.5.2 - "@types/serve-handler": 6.1.1 + "@types/serve-handler": 6.1.2 "@types/sortablejs": 1.15.2 "@types/tar": 6.1.6 "@types/ua-parser-js": 0.7.37