From df33e979961459a912780146ce754372aa6e0c98 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 26 Feb 2025 15:27:09 +0100 Subject: [PATCH 01/38] Fix energy gauge tooltip (#24404) --- .../cards/energy/hui-energy-carbon-consumed-gauge-card.ts | 1 + .../cards/energy/hui-energy-grid-neutrality-gauge-card.ts | 2 +- .../cards/energy/hui-energy-self-sufficiency-gauge-card.ts | 1 + .../cards/energy/hui-energy-solar-consumed-gauge-card.ts | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts index 52bf1cad74..6281f395a1 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-carbon-consumed-gauge-card.ts @@ -150,6 +150,7 @@ class HuiEnergyCarbonGaugeCard "ui.panel.lovelace.cards.energy.carbon_consumed_gauge.card_indicates_energy_used" )} placement="left" + hoist > diff --git a/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts index 646b90f638..180967aea8 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-grid-neutrality-gauge-card.ts @@ -125,7 +125,7 @@ class HuiEnergyGridGaugeCard label="kWh" needle > - + ${this.hass.localize( "ui.panel.lovelace.cards.energy.grid_neutrality_gauge.energy_dependency" diff --git a/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts index a5f9cc84ce..e9b5acba71 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-self-sufficiency-gauge-card.ts @@ -192,6 +192,7 @@ class HuiEnergySelfSufficiencyGaugeCard .content=${this.hass.localize( "ui.panel.lovelace.cards.energy.self_sufficiency_gauge.card_indicates_self_sufficiency_quota" )} + hoist > diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts index c91801255b..061dd2f7a5 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-consumed-gauge-card.ts @@ -117,7 +117,7 @@ class HuiEnergySolarGaugeCard "--gauge-color": this._computeSeverity(value), })} > - + ${this.hass.localize( "ui.panel.lovelace.cards.energy.solar_consumed_gauge.card_indicates_solar_energy_used" From c80247d99265010b4a73f163781cf5f32ac6dd1c Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Thu, 27 Feb 2025 10:46:29 +0100 Subject: [PATCH 02/38] Swap button positions of toggle feature (#24416) --- src/panels/lovelace/card-features/hui-toggle-card-feature.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts index ce9b2294cf..bf233f5a9a 100644 --- a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts @@ -53,7 +53,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature { const color = stateColorCss(this.stateObj); - const options = ["on", "off"].map((entityState) => ({ + const options = ["off", "on"].map((entityState) => ({ value: entityState, label: this.hass!.formatEntityState(this.stateObj!, entityState), path: entityState === "on" ? mdiPower : mdiPowerOff, From 014db0e60f9eb746e592b0c93846094168d72950 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Thu, 27 Feb 2025 12:20:38 +0100 Subject: [PATCH 03/38] Translate state in entity table (#24417) --- src/panels/config/entities/ha-config-entities.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 51884252a6..7ff75039a2 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -288,7 +288,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { : entry.entity ? html` Date: Thu, 27 Feb 2025 10:46:01 +0100 Subject: [PATCH 04/38] Swap default positions of increment and decrement in counter actions feature (#24418) --- src/panels/lovelace/card-features/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 3b30ac46d8..743f1a607f 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -83,7 +83,7 @@ export interface ClimatePresetModesCardFeatureConfig { preset_modes?: string[]; } -export const COUNTER_ACTIONS = ["increment", "reset", "decrement"] as const; +export const COUNTER_ACTIONS = ["decrement", "reset", "increment"] as const; export type CounterActions = (typeof COUNTER_ACTIONS)[number]; From 71a4dceedc806b176f5a40a9ed55e0f29862f1cb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Feb 2025 13:07:50 +0100 Subject: [PATCH 05/38] Don't show features settings if none is compatible (#24419) --- .../hui-card-features-editor.ts | 62 +++++++++++-------- .../config-elements/hui-tile-card-editor.ts | 32 +++++++--- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index cb779eb5d2..0a2c058ada 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -151,6 +151,38 @@ customCardFeatures.forEach((feature) => { CUSTOM_FEATURE_ENTRIES[feature.type] = feature; }); +export const getSupportedFeaturesType = ( + stateObj: HassEntity, + featuresTypes?: string[] +) => { + const filteredFeaturesTypes = UI_FEATURE_TYPES.filter( + (type) => !featuresTypes || featuresTypes.includes(type) + ) as string[]; + + const customFeaturesTypes = customCardFeatures.map( + (feature) => `${CUSTOM_TYPE_PREFIX}${feature.type}` + ); + return filteredFeaturesTypes + .concat(customFeaturesTypes) + .filter((type) => supportsFeaturesType(stateObj, type)); +}; + +export const supportsFeaturesType = (stateObj: HassEntity, type: string) => { + if (isCustomType(type)) { + const customType = stripCustomPrefix(type); + const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType]; + if (!customFeatureEntry?.supported) return true; + try { + return customFeatureEntry.supported(stateObj); + } catch { + return false; + } + } + + const supportsFeature = SUPPORTS_FEATURE_TYPES[type]; + return !supportsFeature || supportsFeature(stateObj); +}; + declare global { interface HASSDomEvents { "features-changed": { @@ -178,20 +210,12 @@ export class HuiCardFeaturesEditor extends LitElement { private _supportsFeatureType(type: string): boolean { if (!this.stateObj) return false; + return supportsFeaturesType(this.stateObj, type); + } - if (isCustomType(type)) { - const customType = stripCustomPrefix(type); - const customFeatureEntry = CUSTOM_FEATURE_ENTRIES[customType]; - if (!customFeatureEntry?.supported) return true; - try { - return customFeatureEntry.supported(this.stateObj); - } catch { - return false; - } - } - - const supportsFeature = SUPPORTS_FEATURE_TYPES[type]; - return !supportsFeature || supportsFeature(this.stateObj); + private _getSupportedFeaturesType() { + if (!this.stateObj) return []; + return getSupportedFeaturesType(this.stateObj, this.featuresTypes); } private _isFeatureTypeEditable(type: string) { @@ -225,18 +249,6 @@ export class HuiCardFeaturesEditor extends LitElement { return this._featuresKeys.get(feature)!; } - private _getSupportedFeaturesType() { - const featuresTypes = UI_FEATURE_TYPES.filter( - (type) => !this.featuresTypes || this.featuresTypes.includes(type) - ) as readonly string[]; - const customFeaturesTypes = customCardFeatures.map( - (feature) => `${CUSTOM_TYPE_PREFIX}${feature.type}` - ); - return featuresTypes - .concat(customFeaturesTypes) - .filter((type) => this._supportsFeatureType(type)); - } - protected render() { if (!this.features || !this.hass) { return nothing; diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts index 7e9bf434fd..2d05a80a93 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts @@ -14,6 +14,7 @@ import { string, union, } from "superstruct"; +import type { HassEntity } from "home-assistant-js-websocket"; import type { HASSDomEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event"; import type { LocalizeFunc } from "../../../../common/translations/localize"; @@ -36,7 +37,7 @@ import { actionConfigStruct } from "../structs/action-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import type { EditDetailElementEvent, EditSubElementEvent } from "../types"; import { configElementStyle } from "./config-elements-style"; -import "./hui-card-features-editor"; +import { getSupportedFeaturesType } from "./hui-card-features-editor"; const cardConfigStruct = assign( baseLovelaceCardConfig, @@ -259,6 +260,10 @@ export class HuiTileCardEditor ] as const satisfies readonly HaFormSchema[] ); + private _hasCompatibleFeatures = memoizeOne( + (stateObj: HassEntity) => getSupportedFeaturesType(stateObj).length > 0 + ); + protected render() { if (!this.hass || !this._config) { return nothing; @@ -289,6 +294,9 @@ export class HuiTileCardEditor data.features_position = "bottom"; } + const hasCompatibleFeatures = + (stateObj && this._hasCompatibleFeatures(stateObj)) || false; + return html`
- + ${hasCompatibleFeatures + ? html` + + ` + : nothing} Date: Thu, 27 Feb 2025 13:08:42 +0100 Subject: [PATCH 06/38] Reverse the order of all modes features and toggle (#24420) Reverse the order of all modes features --- .../lovelace/card-features/hui-alarm-modes-card-feature.ts | 2 +- .../card-features/hui-climate-hvac-modes-card-feature.ts | 3 ++- .../card-features/hui-humidifier-toggle-card-feature.ts | 2 +- .../hui-water-heater-operation-modes-card-feature.ts | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-alarm-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-alarm-modes-card-feature.ts index 6aec00c670..6aa71fe590 100644 --- a/src/panels/lovelace/card-features/hui-alarm-modes-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-alarm-modes-card-feature.ts @@ -119,7 +119,7 @@ class HuiAlarmModeCardFeature const color = stateColorCss(this.stateObj); - const supportedModes = supportedAlarmModes(this.stateObj); + const supportedModes = supportedAlarmModes(this.stateObj).reverse(); const options = filterModes( supportedModes, diff --git a/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts index 08619e18dd..d8ada2c5d9 100644 --- a/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-climate-hvac-modes-card-feature.ts @@ -122,7 +122,8 @@ class HuiClimateHvacModesCardFeature const ordererHvacModes = (this.stateObj.attributes.hvac_modes || []) .concat() - .sort(compareClimateHvacModes); + .sort(compareClimateHvacModes) + .reverse(); const options = filterModes( ordererHvacModes, diff --git a/src/panels/lovelace/card-features/hui-humidifier-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-humidifier-toggle-card-feature.ts index dd5ced0a59..575437c04d 100644 --- a/src/panels/lovelace/card-features/hui-humidifier-toggle-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-humidifier-toggle-card-feature.ts @@ -93,7 +93,7 @@ class HuiHumidifierToggleCardFeature const color = stateColorCss(this.stateObj); - const options = ["on", "off"].map((entityState) => ({ + const options = ["off", "on"].map((entityState) => ({ value: entityState, label: this.hass!.formatEntityState(this.stateObj!, entityState), path: entityState === "on" ? mdiWaterPercent : mdiPower, diff --git a/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts index 78457c53c4..a74041cbdd 100644 --- a/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-water-heater-operation-modes-card-feature.ts @@ -110,7 +110,8 @@ class HuiWaterHeaterOperationModeCardFeature const orderedModes = (this.stateObj.attributes.operation_list || []) .concat() - .sort(compareWaterHeaterOperationMode); + .sort(compareWaterHeaterOperationMode) + .reverse(); const options = filterModes( orderedModes, From 7e0cd35ea8a2f65e88e4b2ab31657039b13b9bee Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Feb 2025 12:08:36 +0100 Subject: [PATCH 07/38] Add features position description in tile card editor (#24421) --- .../lovelace/editor/config-elements/hui-tile-card-editor.ts | 3 +++ src/translations/en.json | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts index 2d05a80a93..a5e3c9e528 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts @@ -246,6 +246,9 @@ export class HuiTileCardEditor label: localize( `ui.panel.lovelace.editor.card.tile.features_position_options.${value}` ), + description: localize( + `ui.panel.lovelace.editor.card.tile.features_position_options.${value}_description` + ), value, image: { src: `/static/images/form/tile_features_position_${value}.svg`, diff --git a/src/translations/en.json b/src/translations/en.json index c3f9671b40..d04fa625a1 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -7186,7 +7186,9 @@ "features_position": "Features position", "features_position_options": { "bottom": "Bottom", - "inline": "Inline" + "bottom_description": "Displays all features stacked", + "inline": "Inline", + "inline_description": "Displays only the first feature" }, "features_position_helper_vertical": "Always displayed at the bottom if the content layout is vertical", "content_layout": "Content layout", From ac18c6c018039566e69e549b64ff53b458999e14 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Feb 2025 13:08:16 +0100 Subject: [PATCH 08/38] Fix select box radio click on firefox (#24422) --- src/components/ha-select-box.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ha-select-box.ts b/src/components/ha-select-box.ts index 61fdb3533f..a53506df74 100644 --- a/src/components/ha-select-box.ts +++ b/src/components/ha-select-box.ts @@ -7,6 +7,7 @@ import type { HaRadio } from "./ha-radio"; import { fireEvent } from "../common/dom/fire_event"; import type { HomeAssistant } from "../types"; import { computeRTL } from "../common/util/compute_rtl"; +import { stopPropagation } from "../common/dom/stop_propagation"; interface SelectBoxOptionImage { src: string; @@ -76,6 +77,7 @@ export class HaSelectBox extends LitElement { .value=${option.value} .disabled=${disabled} @change=${this._radioChanged} + @click=${stopPropagation} >
${option.label} @@ -99,6 +101,7 @@ export class HaSelectBox extends LitElement { } private _radioChanged(ev: CustomEvent) { + ev.stopPropagation(); const radio = ev.currentTarget as HaRadio; const value = radio.value; if (this.disabled || value === undefined || value === (this.value ?? "")) { From 92681e70366118283ecfe79bad6a0ded86a7274e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Feb 2025 12:09:12 +0100 Subject: [PATCH 09/38] Fix header hidden when no badges (#24423) --- src/panels/lovelace/views/hui-view-header.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/lovelace/views/hui-view-header.ts b/src/panels/lovelace/views/hui-view-header.ts index 469270d0d9..278e17f4bd 100644 --- a/src/panels/lovelace/views/hui-view-header.ts +++ b/src/panels/lovelace/views/hui-view-header.ts @@ -81,6 +81,7 @@ export class HuiViewHeader extends LitElement { } else { this.card = undefined; } + this._checkHidden(); return; } From 7833a680a9530e83e3baf5591ee48271c7435a2f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Feb 2025 15:35:12 +0100 Subject: [PATCH 10/38] Use switch and add support for light, fan and valve (#24426) * Use switch and add support for light and fan * Add icon per domains --- .../more-info/controls/more-info-light.ts | 6 +- .../common/card-feature-styles.ts | 8 + .../card-features/hui-toggle-card-feature.ts | 186 +++++++++++++----- 3 files changed, 144 insertions(+), 56 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 30b3fa1d4f..9d81cfdc01 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -3,8 +3,8 @@ import { mdiBrightness6, mdiCreation, mdiFileWordBox, - mdiLightbulb, mdiLightbulbOff, + mdiLightbulbOn, mdiPower, } from "@mdi/js"; import type { CSSResultGroup, PropertyValues } from "lit"; @@ -12,8 +12,8 @@ import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import { supportsFeature } from "../../../common/entity/supports-feature"; -import "../../../components/ha-attributes"; import "../../../components/ha-attribute-icon"; +import "../../../components/ha-attributes"; import "../../../components/ha-control-select-menu"; import "../../../components/ha-icon-button-group"; import "../../../components/ha-icon-button-toggle"; @@ -121,7 +121,7 @@ class MoreInfoLight extends LitElement { ` diff --git a/src/panels/lovelace/card-features/common/card-feature-styles.ts b/src/panels/lovelace/card-features/common/card-feature-styles.ts index 6a0fdcb1c0..5787f1d1d2 100644 --- a/src/panels/lovelace/card-features/common/card-feature-styles.ts +++ b/src/panels/lovelace/card-features/common/card-feature-styles.ts @@ -27,4 +27,12 @@ export const cardFeatureStyles = css` --control-slider-thickness: var(--feature-height); --control-slider-border-radius: var(--feature-border-radius); } + ha-control-switch { + --control-switch-on-color: var(--feature-color); + --control-switch-off-color: var(--feature-color); + --control-switch-background-opacity: 0.2; + --control-switch-thickness: var(--feature-height); + --control-switch-border-radius: var(--feature-border-radius); + --control-switch-padding: 0px; + } `; diff --git a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts index bf233f5a9a..96cfce4161 100644 --- a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts @@ -1,23 +1,50 @@ -import { mdiPowerOff, mdiPower } from "@mdi/js"; +import { + mdiFan, + mdiFanOff, + mdiLightbulbOff, + mdiLightbulbOn, + mdiPower, + mdiPowerOff, + mdiVolumeHigh, + mdiVolumeOff, +} from "@mdi/js"; import type { HassEntity } from "home-assistant-js-websocket"; -import type { TemplateResult } from "lit"; -import { LitElement, html } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; import { computeDomain } from "../../../common/entity/compute_domain"; import { stateColorCss } from "../../../common/entity/state_color"; -import "../../../components/ha-control-select"; -import type { ControlSelectOption } from "../../../components/ha-control-select"; -import { UNAVAILABLE } from "../../../data/entity"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; +import "../../../components/ha-control-switch"; +import { UNAVAILABLE, UNKNOWN } from "../../../data/entity"; +import { forwardHaptic } from "../../../data/haptics"; import type { HomeAssistant } from "../../../types"; import type { LovelaceCardFeature } from "../types"; import { cardFeatureStyles } from "./common/card-feature-styles"; import type { ToggleCardFeatureConfig } from "./types"; -import { showToast } from "../../../util/toast"; export const supportsToggleCardFeature = (stateObj: HassEntity) => { const domain = computeDomain(stateObj.entity_id); - return ["switch", "input_boolean"].includes(domain); + return ["switch", "input_boolean", "light", "fan", "siren", "valve"].includes( + domain + ); +}; + +const DOMAIN_ICONS: Record = { + siren: { + on: mdiVolumeHigh, + off: mdiVolumeOff, + }, + light: { + on: mdiLightbulbOn, + off: mdiLightbulbOff, + }, + fan: { + on: mdiFan, + off: mdiFanOff, + }, }; @customElement("hui-toggle-card-feature") @@ -41,67 +68,120 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature { this._config = config; } - protected render(): TemplateResult | null { + private _valueChanged(ev) { + const checked = ev.target.checked as boolean; + + if (checked) { + this._turnOn(); + } else { + this._turnOff(); + } + } + + private _turnOn() { + this._callService(true); + } + + private _turnOff() { + this._callService(false); + } + + private async _callService(turnOn): Promise { + if (!this.hass || !this.stateObj) { + return; + } + forwardHaptic("light"); + const stateDomain = computeDomain(this.stateObj.entity_id); + const serviceDomain = stateDomain; + const service = turnOn ? "turn_on" : "turn_off"; + + await this.hass.callService(serviceDomain, service, { + entity_id: this.stateObj.entity_id, + }); + } + + protected render() { if ( !this._config || !this.hass || !this.stateObj || !supportsToggleCardFeature(this.stateObj) ) { - return null; + return nothing; } - const color = stateColorCss(this.stateObj); + const onColor = "var(--feature-color)"; + const offColor = stateColorCss(this.stateObj, "off"); - const options = ["off", "on"].map((entityState) => ({ - value: entityState, - label: this.hass!.formatEntityState(this.stateObj!, entityState), - path: entityState === "on" ? mdiPower : mdiPowerOff, - })); + const isOn = this.stateObj.state === "on"; + const isOff = this.stateObj.state === "off"; + + const domain = computeDomain(this.stateObj.entity_id); + const onIcon = DOMAIN_ICONS[domain]?.on || mdiPower; + const offIcon = DOMAIN_ICONS[domain]?.off || mdiPowerOff; + + if ( + this.stateObj.attributes.assumed_state || + this.stateObj.state === UNKNOWN + ) { + return html` + + + + + + + + + `; + } return html` - - + `; } - private async _valueChanged(ev: CustomEvent) { - const newState = (ev.detail as any).value; - - if ( - newState === this.stateObj!.state && - !this.stateObj!.attributes.assumed_state - ) - return; - const service = newState === "on" ? "turn_on" : "turn_off"; - const domain = computeDomain(this.stateObj!.entity_id); - - try { - await this.hass!.callService(domain, service, { - entity_id: this.stateObj!.entity_id, - }); - } catch (_err) { - showToast(this, { - message: this.hass!.localize("ui.notification_toast.action_failed", { - service: domain + "." + service, - }), - duration: 5000, - dismissable: true, - }); - } + static get styles() { + return [ + cardFeatureStyles, + css` + ha-control-button.active { + --control-button-icon-color: white; + --control-button-background-color: var(--color); + --control-button-background-opacity: 1; + } + `, + ]; } - - static styles = cardFeatureStyles; } declare global { From 5caa47acc1f0be504d396a3d807fffa2ef627ee6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 27 Feb 2025 15:45:32 +0100 Subject: [PATCH 11/38] Bumped version to 20250227.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9c07e59516..bf88ad41f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250226.0" +version = "20250227.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 52b199c92b55cc4468c70e423e183b9a8e36c0cf Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 27 Feb 2025 16:15:42 +0100 Subject: [PATCH 12/38] Don't show no config flow message when source = system (#24425) dont show no config flow message when source = system --- src/panels/config/integrations/ha-integration-card.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index c3125dcc02..9ac636c354 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -186,7 +186,9 @@ export class HaIntegrationCard extends LitElement {
` : nothing} - ${this.manifest && !this.manifest?.config_flow + ${this.manifest && + !this.manifest?.config_flow && + !this.items.every((itm) => itm.source === "system") ? html`
Date: Thu, 27 Feb 2025 19:30:15 +0100 Subject: [PATCH 13/38] Allow the card features buttons to be smaller if needed (#24431) --- .../lovelace/card-features/common/card-feature-styles.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/panels/lovelace/card-features/common/card-feature-styles.ts b/src/panels/lovelace/card-features/common/card-feature-styles.ts index 5787f1d1d2..31fff2358a 100644 --- a/src/panels/lovelace/card-features/common/card-feature-styles.ts +++ b/src/panels/lovelace/card-features/common/card-feature-styles.ts @@ -20,6 +20,10 @@ export const cardFeatureStyles = css` --control-button-group-spacing: var(--feature-button-spacing); --control-button-group-thickness: var(--feature-height); } + ha-control-button-group > ha-control-button { + flex-basis: 20px; + --control-button-padding: 0px; + } ha-control-slider { --control-slider-color: var(--feature-color); --control-slider-background: var(--feature-color); From 99f86bb9cf1b98934786699d3721325a25e2fe4d Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Feb 2025 20:36:41 +0100 Subject: [PATCH 14/38] Use border color for focus state of button and select in dashboard (#24429) --- src/components/ha-control-button.ts | 11 ++++++----- src/components/ha-control-number-buttons.ts | 3 ++- src/components/ha-control-select-menu.ts | 7 +++++-- .../ha-more-info-control-select-container.ts | 4 ++-- .../card-features/common/card-feature-styles.ts | 4 ++++ src/state-control/ha-state-control-toggle.ts | 1 + .../lock/ha-state-control-lock-toggle.ts | 1 + .../valve/ha-state-control-valve-toggle.ts | 1 + 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/components/ha-control-button.ts b/src/components/ha-control-button.ts index e86026ea3f..f680c23a5b 100644 --- a/src/components/ha-control-button.ts +++ b/src/components/ha-control-button.ts @@ -28,6 +28,7 @@ export class HaControlButton extends LitElement { static styles = css` :host { display: block; + --control-button-focus-color: var(--secondary-text-color); --control-button-icon-color: var(--primary-text-color); --control-button-background-color: var(--disabled-color); --control-button-background-opacity: 0.2; @@ -66,9 +67,13 @@ export class HaControlButton extends LitElement { z-index: 0; font-size: inherit; color: inherit; + transition: + box-shadow 180ms ease-in-out, + color 180ms ease-in-out; + color: var(--control-button-icon-color); } .button:focus-visible { - --control-button-background-opacity: 0.4; + box-shadow: 0 0 0 2px var(--control-button-focus-color); } .button::before { content: ""; @@ -85,10 +90,6 @@ export class HaControlButton extends LitElement { pointer-events: none; white-space: normal; } - .button { - transition: color 180ms ease-in-out; - color: var(--control-button-icon-color); - } .button ::slotted(*) { pointer-events: none; opacity: 0.95; diff --git a/src/components/ha-control-number-buttons.ts b/src/components/ha-control-number-buttons.ts index 50400bf7f8..062632b792 100644 --- a/src/components/ha-control-number-buttons.ts +++ b/src/components/ha-control-number-buttons.ts @@ -184,7 +184,7 @@ export class HaControlNumberButton extends LitElement { static styles = css` :host { display: block; - --control-number-buttons-focus-color: var(--primary-color); + --control-number-buttons-focus-color: var(--secondary-text-color); --control-number-buttons-background-color: var(--disabled-color); --control-number-buttons-background-opacity: 0.2; --control-number-buttons-border-radius: 10px; @@ -228,6 +228,7 @@ export class HaControlNumberButton extends LitElement { color: inherit; user-select: none; -webkit-tap-highlight-color: transparent; + transition: box-shadow 180ms ease-in-out; outline: none; } .value::before { diff --git a/src/components/ha-control-select-menu.ts b/src/components/ha-control-select-menu.ts index dd987bcc78..7f48497f68 100644 --- a/src/components/ha-control-select-menu.ts +++ b/src/components/ha-control-select-menu.ts @@ -137,6 +137,7 @@ export class HaControlSelectMenu extends SelectBase { css` :host { display: inline-block; + --control-select-menu-focus-color: var(--secondary-text-color); --control-select-menu-text-color: var(--primary-text-color); --control-select-menu-background-color: var(--disabled-color); --control-select-menu-background-opacity: 0.2; @@ -167,7 +168,9 @@ export class HaControlSelectMenu extends SelectBase { background: none; /* For safari border-radius overflow */ z-index: 0; - transition: color 180ms ease-in-out; + transition: + box-shadow 180ms ease-in-out, + color 180ms ease-in-out; gap: 10px; width: 100%; user-select: none; @@ -205,7 +208,7 @@ export class HaControlSelectMenu extends SelectBase { } .select-anchor:focus-visible { - --control-select-menu-background-opacity: 0.4; + box-shadow: 0 0 0 2px var(--control-select-menu-focus-color); } .select-anchor::before { diff --git a/src/dialogs/more-info/components/ha-more-info-control-select-container.ts b/src/dialogs/more-info/components/ha-more-info-control-select-container.ts index 2de2b17a59..e25e3404a6 100644 --- a/src/dialogs/more-info/components/ha-more-info-control-select-container.ts +++ b/src/dialogs/more-info/components/ha-more-info-control-select-container.ts @@ -37,8 +37,8 @@ export class HaMoreInfoControlSelectContainer extends LitElement { overflow: auto; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ - margin: 0 -24px; - padding: 0 24px; + margin: -2px -24px; + padding: 2px 24px; } .controls-scroll::-webkit-scrollbar { display: none; diff --git a/src/panels/lovelace/card-features/common/card-feature-styles.ts b/src/panels/lovelace/card-features/common/card-feature-styles.ts index 31fff2358a..e8293034d6 100644 --- a/src/panels/lovelace/card-features/common/card-feature-styles.ts +++ b/src/panels/lovelace/card-features/common/card-feature-styles.ts @@ -5,6 +5,7 @@ export const cardFeatureStyles = css` box-sizing: border-box; --control-select-menu-height: var(--feature-height); --control-select-menu-border-radius: var(--feature-border-radius); + --control-select-menu-focus-color: var(--feature-color); line-height: 1.2; display: block; width: 100%; @@ -24,6 +25,9 @@ export const cardFeatureStyles = css` flex-basis: 20px; --control-button-padding: 0px; } + ha-control-button { + --control-button-focus-color: var(--feature-color); + } ha-control-slider { --control-slider-color: var(--feature-color); --control-slider-background: var(--feature-color); diff --git a/src/state-control/ha-state-control-toggle.ts b/src/state-control/ha-state-control-toggle.ts index bcf01ad8c2..0fcf4eef38 100644 --- a/src/state-control/ha-state-control-toggle.ts +++ b/src/state-control/ha-state-control-toggle.ts @@ -157,6 +157,7 @@ export class HaStateControlToggle extends LitElement { ha-control-button.active { --control-button-icon-color: white; --control-button-background-color: var(--color); + --control-button-focus-color: var(--color); --control-button-background-opacity: 1; } ha-control-button:not(:last-child) { diff --git a/src/state-control/lock/ha-state-control-lock-toggle.ts b/src/state-control/lock/ha-state-control-lock-toggle.ts index 6e9c4ba548..c7cb21216b 100644 --- a/src/state-control/lock/ha-state-control-lock-toggle.ts +++ b/src/state-control/lock/ha-state-control-lock-toggle.ts @@ -188,6 +188,7 @@ export class HaStateControlLockToggle extends LitElement { ha-control-button.active { --control-button-icon-color: white; --control-button-background-color: var(--color); + --control-button-focus-color: var(--color); --control-button-background-opacity: 1; } ha-control-button:not(:last-child) { diff --git a/src/state-control/valve/ha-state-control-valve-toggle.ts b/src/state-control/valve/ha-state-control-valve-toggle.ts index e06f3ae9d6..e476b3bd58 100644 --- a/src/state-control/valve/ha-state-control-valve-toggle.ts +++ b/src/state-control/valve/ha-state-control-valve-toggle.ts @@ -166,6 +166,7 @@ export class HaStateControlValveToggle extends LitElement { ha-control-button.active { --control-button-icon-color: white; --control-button-background-color: var(--color); + --control-button-focus-color: var(--color); --control-button-background-opacity: 1; } ha-control-button:not(:last-child) { From f020269447419006cab12d7774cdcf6afca69a1c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 28 Feb 2025 08:03:15 +0000 Subject: [PATCH 15/38] Small fixes for Bluetooth device info (#24436) * Add missing service uuids to Bluetooth device info Service UUIDs are different from Service data because they do not have any data attached to them. I only discovered that they were missing in the UI because of helping a user troubleshoot of device, and we could not find the Service UUID * fix mapping * fix mapping * fix mapping * tweaks --- .../bluetooth/dialog-bluetooth-device-info.ts | 23 ++++++++++++++----- src/translations/en.json | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/bluetooth/dialog-bluetooth-device-info.ts b/src/panels/config/integrations/integration-panels/bluetooth/dialog-bluetooth-device-info.ts index 635da34b03..25c09a9284 100644 --- a/src/panels/config/integrations/integration-panels/bluetooth/dialog-bluetooth-device-info.ts +++ b/src/panels/config/integrations/integration-panels/bluetooth/dialog-bluetooth-device-info.ts @@ -81,10 +81,8 @@ class DialogBluetoothDeviceInfo extends LitElement implements HassDialog { ${Object.entries(this._params.entry.manufacturer_data).map( ([key, value]) => html` - ${key} - - - ${this.showDataAsHex(value)} + ${key} + ${this.showDataAsHex(value)} ` )} @@ -97,10 +95,23 @@ class DialogBluetoothDeviceInfo extends LitElement implements HassDialog { ${Object.entries(this._params.entry.service_data).map( ([key, value]) => html` - ${key} + ${key} + ${this.showDataAsHex(value)} + ` + )} + + + +

+ ${this.hass.localize("ui.panel.config.bluetooth.service_uuids")} +

+ + + ${this._params.entry.service_uuids.map( + (uuid) => html` - ${this.showDataAsHex(value)} + ` )} diff --git a/src/translations/en.json b/src/translations/en.json index d04fa625a1..02e9a055a9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5378,6 +5378,7 @@ "advertisement_data": "Advertisement data", "manufacturer_data": "Manufacturer data", "service_data": "Service data", + "service_uuids": "Service UUIDs", "copy_to_clipboard": "[%key:ui::panel::config::automation::editor::copy_to_clipboard%]" }, "thread": { From 9d5d0e448f7e2df25ab36e5f4f28eb70d8db5808 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 28 Feb 2025 05:07:00 -0800 Subject: [PATCH 16/38] More height fixes in devtools/statistics (#24438) * More height fixes in devtools/statistics * fix selection bar --- .../statistics/developer-tools-statistics.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index 6113d08927..ac06d1fc9b 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -81,7 +81,7 @@ type DisplayedStatisticData = StatisticData & { class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ type: Boolean }) public narrow = false; + @property({ type: Boolean, reflect: true }) public narrow = false; @state() private _data: StatisticData[] = [] as StatisticsMetaData[]; @@ -307,7 +307,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) { `; return html` -
+
${this._selectMode ? html`
@@ -700,15 +700,16 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) { height: 100%; } + .table-with-toolbars { + height: 100%; + display: flex; + flex-direction: column; + } ha-data-table { width: 100%; - height: 100%; + flex-grow: 1; --data-table-border-width: 0; } - :host(:not([narrow])) ha-data-table { - height: calc(100vh - 1px - var(--header-height) - 48px); - display: block; - } :host([narrow]) { --expansion-panel-summary-padding: 0 16px; @@ -748,7 +749,6 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) { .selection-bar { background: rgba(var(--rgb-primary-color), 0.1); width: 100%; - height: 100%; display: flex; align-items: center; justify-content: space-between; From a384bc22739f847ed4446bb8963e255757c4fe32 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 27 Feb 2025 23:04:02 -0800 Subject: [PATCH 17/38] Change label on BT advertisement timestamp (#24439) --- .../bluetooth/bluetooth-advertisement-monitor.ts | 2 +- src/translations/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/bluetooth/bluetooth-advertisement-monitor.ts b/src/panels/config/integrations/integration-panels/bluetooth/bluetooth-advertisement-monitor.ts index 65c17dc42e..c9de84adbd 100644 --- a/src/panels/config/integrations/integration-panels/bluetooth/bluetooth-advertisement-monitor.ts +++ b/src/panels/config/integrations/integration-panels/bluetooth/bluetooth-advertisement-monitor.ts @@ -142,7 +142,7 @@ export class BluetoothAdvertisementMonitorPanel extends LitElement { defaultHidden: true, }, time: { - title: localize("ui.panel.config.bluetooth.last_seen"), + title: localize("ui.panel.config.bluetooth.updated"), filterable: false, sortable: true, defaultHidden: false, diff --git a/src/translations/en.json b/src/translations/en.json index 02e9a055a9..e583bb5d62 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5372,7 +5372,7 @@ "source": "Source", "rssi": "RSSI", "source_address": "Source address", - "last_seen": "Last seen", + "updated": "Updated", "device": "Device", "device_information": "Device information", "advertisement_data": "Advertisement data", From 56eef4bf311176202f6b8f221d7171b0771cedf8 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 28 Feb 2025 14:05:14 +0100 Subject: [PATCH 18/38] Fix control number buttons height (#24441) --- src/components/ha-control-number-buttons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-control-number-buttons.ts b/src/components/ha-control-number-buttons.ts index 062632b792..7977fb2190 100644 --- a/src/components/ha-control-number-buttons.ts +++ b/src/components/ha-control-number-buttons.ts @@ -254,7 +254,7 @@ export class HaControlNumberButton extends LitElement { bottom: 0; padding: 0; width: 35px; - height: 40px; + height: 100%; border: none; background: none; cursor: pointer; From 7f7575dcbc0c729799c87de6c9c2e66a4e5d1116 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Fri, 28 Feb 2025 16:06:03 +0100 Subject: [PATCH 19/38] More info panel: Replace "Dismiss dialog" tooltip with "Close info" (#24449) * More info panel: Replace "Dismiss dialog" tooltip with "Close info" Dismiss should only be used for closing dialogs like notifications that cannot be reopened. Therefore the "Dismiss dialog" tooltip for the More info dialog is misleading, especially in translations that might emphasize the destructive meaning of "dismiss". Matching the "Back to info" tooltip shown to return to the initial view from the entity settings this is changed to "Close info". * use common close --------- Co-authored-by: Bram Kragten --- src/dialogs/config-flow/dialog-data-entry-flow.ts | 4 +--- src/dialogs/more-info/ha-more-info-dialog.ts | 4 +--- .../zha/dialog-zha-manage-zigbee-device.ts | 2 +- src/translations/en.json | 6 ------ 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index 94a090ec90..bf9a66eba6 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -229,9 +229,7 @@ class DataEntryFlowDialog extends LitElement { ` : ""} diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index b078a1d6f4..cc383b86ed 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -307,9 +307,7 @@ export class MoreInfoDialog extends LitElement { ` diff --git a/src/panels/config/integrations/integration-panels/zha/dialog-zha-manage-zigbee-device.ts b/src/panels/config/integrations/integration-panels/zha/dialog-zha-manage-zigbee-device.ts index ce07e1e066..3ac4a62f02 100644 --- a/src/panels/config/integrations/integration-panels/zha/dialog-zha-manage-zigbee-device.ts +++ b/src/panels/config/integrations/integration-panels/zha/dialog-zha-manage-zigbee-device.ts @@ -95,7 +95,7 @@ class DialogZHAManageZigbeeDevice extends LitElement { Date: Fri, 28 Feb 2025 15:51:06 +0100 Subject: [PATCH 20/38] Align common dialog translations (#24450) --- src/components/ha-dialog.ts | 2 +- src/components/media-player/dialog-media-manage.ts | 2 +- src/components/media-player/dialog-media-player-browse.ts | 2 +- src/dialogs/generic/dialog-box.ts | 4 ++-- src/dialogs/restart/dialog-restart.ts | 2 +- .../voice-assistant-setup/voice-assistant-setup-dialog.ts | 3 +-- src/panels/calendar/confirm-event-dialog-box.ts | 2 +- .../automation-mode-dialog/dialog-automation-mode.ts | 2 +- .../automation-save-dialog/dialog-automation-save.ts | 2 +- .../config/backup/dialogs/dialog-backup-onboarding.ts | 4 +++- .../backup/dialogs/dialog-change-backup-encryption-key.ts | 2 +- .../backup/dialogs/dialog-download-decrypted-backup.ts | 2 +- src/panels/config/backup/dialogs/dialog-generate-backup.ts | 2 +- src/panels/config/backup/dialogs/dialog-restore-backup.ts | 2 +- .../backup/dialogs/dialog-set-backup-encryption-key.ts | 2 +- .../backup/dialogs/dialog-show-backup-encryption-key.ts | 4 ++-- src/panels/config/backup/dialogs/dialog-upload-backup.ts | 2 +- .../mqtt/dialog-mqtt-device-debug-info.ts | 2 +- src/panels/config/integrations/dialog-yaml-integration.ts | 4 ++-- .../integration-panels/zha/dialog-zha-change-channel.ts | 2 +- .../integration-panels/zha/dialog-zha-reconfigure-device.ts | 6 +++--- .../lovelace/resources/dialog-lovelace-resource-detail.ts | 2 +- src/panels/config/repairs/dialog-repairs-issue.ts | 2 +- src/panels/config/users/dialog-admin-change-password.ts | 2 +- src/panels/config/voice-assistants/dialog-expose-entity.ts | 2 +- .../editor/select-dashboard/hui-dialog-select-dashboard.ts | 2 +- src/translations/en.json | 5 +---- 27 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index ee96bd0f52..e326e6b19b 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -16,7 +16,7 @@ export const createCloseHeading = ( ) => html`
${this._params.dismissText ? this._params.dismissText - : this.hass.localize("ui.dialogs.generic.cancel")} + : this.hass.localize("ui.common.cancel")} `} ${this._params.confirmText ? this._params.confirmText - : this.hass.localize("ui.dialogs.generic.ok")} + : this.hass.localize("ui.common.ok")}
diff --git a/src/dialogs/restart/dialog-restart.ts b/src/dialogs/restart/dialog-restart.ts index 5bb9627199..62526c5b53 100644 --- a/src/dialogs/restart/dialog-restart.ts +++ b/src/dialogs/restart/dialog-restart.ts @@ -93,7 +93,7 @@ class DialogRestart extends LitElement { diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts index 81ed1c1113..b278af890a 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts @@ -134,8 +134,7 @@ export class HaVoiceAssistantSetupDialog extends LitElement { : this._step !== STEP.UPDATE ? html`` diff --git a/src/panels/calendar/confirm-event-dialog-box.ts b/src/panels/calendar/confirm-event-dialog-box.ts index b0c8ec2dda..bea8dc0b05 100644 --- a/src/panels/calendar/confirm-event-dialog-box.ts +++ b/src/panels/calendar/confirm-event-dialog-box.ts @@ -41,7 +41,7 @@ class ConfirmEventDialogBox extends LitElement {

${this._params.text}

- ${this.hass.localize("ui.dialogs.generic.cancel")} + ${this.hass.localize("ui.common.cancel")} - ${this.hass.localize("ui.dialogs.generic.cancel")} + ${this.hass.localize("ui.common.cancel")} ${this.hass.localize("ui.panel.config.automation.editor.change_mode")} diff --git a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts index 6c1dc80ce9..60eb15cf25 100644 --- a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts +++ b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts @@ -263,7 +263,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
- ${this.hass.localize("ui.dialogs.generic.cancel")} + ${this.hass.localize("ui.common.cancel")} ${this.hass.localize( diff --git a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts index c9bc98845a..6b4e51fe06 100644 --- a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts +++ b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts @@ -216,7 +216,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { ? html` @@ -277,6 +277,8 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { private _useRecommended() { if (!this._config?.create_backup.password) { + // this should not happen, if there is no password set, restart the wizard + this.showDialog(this._params!); return; } this._config = { diff --git a/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts b/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts index 9c55bbfe6e..f8e7ee2f70 100644 --- a/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts +++ b/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts @@ -108,7 +108,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { : html` diff --git a/src/panels/config/backup/dialogs/dialog-download-decrypted-backup.ts b/src/panels/config/backup/dialogs/dialog-download-decrypted-backup.ts index a9ac25a9d1..8c82390142 100644 --- a/src/panels/config/backup/dialogs/dialog-download-decrypted-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-download-decrypted-backup.ts @@ -113,7 +113,7 @@ class DialogDownloadDecryptedBackup extends LitElement implements HassDialog {
- ${this.hass.localize("ui.dialogs.generic.cancel")} + ${this.hass.localize("ui.common.cancel")} diff --git a/src/panels/config/backup/dialogs/dialog-generate-backup.ts b/src/panels/config/backup/dialogs/dialog-generate-backup.ts index 076ca101b4..65d70784c3 100644 --- a/src/panels/config/backup/dialogs/dialog-generate-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-generate-backup.ts @@ -185,7 +185,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog { ? html` diff --git a/src/panels/config/backup/dialogs/dialog-restore-backup.ts b/src/panels/config/backup/dialogs/dialog-restore-backup.ts index 5a7ad2cfd9..75371de173 100644 --- a/src/panels/config/backup/dialogs/dialog-restore-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-restore-backup.ts @@ -139,7 +139,7 @@ class DialogRestoreBackup extends LitElement implements HassDialog { diff --git a/src/panels/config/backup/dialogs/dialog-set-backup-encryption-key.ts b/src/panels/config/backup/dialogs/dialog-set-backup-encryption-key.ts index de0c3e7004..748e8cc325 100644 --- a/src/panels/config/backup/dialogs/dialog-set-backup-encryption-key.ts +++ b/src/panels/config/backup/dialogs/dialog-set-backup-encryption-key.ts @@ -92,7 +92,7 @@ class DialogSetBackupEncryptionKey extends LitElement implements HassDialog { diff --git a/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts b/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts index 9a5bda4bbf..15bb07096c 100644 --- a/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts +++ b/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts @@ -52,7 +52,7 @@ class DialogShowBackupEncryptionKey extends LitElement implements HassDialog { @@ -98,7 +98,7 @@ class DialogShowBackupEncryptionKey extends LitElement implements HassDialog {
- ${this.hass.localize("ui.dialogs.generic.close")} + ${this.hass.localize("ui.common.close")}
diff --git a/src/panels/config/backup/dialogs/dialog-upload-backup.ts b/src/panels/config/backup/dialogs/dialog-upload-backup.ts index ab396fd4d1..4a2621b6fe 100644 --- a/src/panels/config/backup/dialogs/dialog-upload-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-upload-backup.ts @@ -82,7 +82,7 @@ export class DialogUploadBackup - ${this.hass!.localize("ui.dialogs.generic.close")} + ${this.hass!.localize("ui.common.close")} `; diff --git a/src/panels/config/integrations/dialog-yaml-integration.ts b/src/panels/config/integrations/dialog-yaml-integration.ts index ca57b6dc07..953071c949 100644 --- a/src/panels/config/integrations/dialog-yaml-integration.ts +++ b/src/panels/config/integrations/dialog-yaml-integration.ts @@ -44,7 +44,7 @@ export class DialogYamlIntegration extends LitElement { )}

- ${this.hass.localize("ui.dialogs.generic.cancel")} + ${this.hass.localize("ui.common.cancel")} ${docLink ? html` ` : html` - ${this.hass.localize("ui.dialogs.generic.ok")} + ${this.hass.localize("ui.common.ok")} `} `; diff --git a/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts b/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts index fe1980e466..e0833ebcc6 100644 --- a/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts +++ b/src/panels/config/integrations/integration-panels/zha/dialog-zha-change-channel.ts @@ -130,7 +130,7 @@ class DialogZHAChangeChannel extends LitElement implements HassDialog { slot="secondaryAction" @click=${this.closeDialog} .disabled=${this._migrationInProgress} - >${this.hass.localize("ui.dialogs.generic.cancel")}
${this.hass.localize("ui.common.cancel")} `; diff --git a/src/panels/config/integrations/integration-panels/zha/dialog-zha-reconfigure-device.ts b/src/panels/config/integrations/integration-panels/zha/dialog-zha-reconfigure-device.ts index 1fd53ab466..635dc5a717 100644 --- a/src/panels/config/integrations/integration-panels/zha/dialog-zha-reconfigure-device.ts +++ b/src/panels/config/integrations/integration-panels/zha/dialog-zha-reconfigure-device.ts @@ -125,7 +125,7 @@ class DialogZHAReconfigureDevice extends LitElement {
- ${this.hass.localize("ui.dialogs.generic.close")} + ${this.hass.localize("ui.common.close")} ${this._showDetails @@ -154,7 +154,7 @@ class DialogZHAReconfigureDevice extends LitElement {
- ${this.hass.localize("ui.dialogs.generic.close")} + ${this.hass.localize("ui.common.close")} ${this._showDetails @@ -183,7 +183,7 @@ class DialogZHAReconfigureDevice extends LitElement { - ${this.hass.localize("ui.dialogs.generic.close")} + ${this.hass.localize("ui.common.close")} ${this._showDetails 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 8614e8c8e8..0dee14c356 100644 --- a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts +++ b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts @@ -99,7 +99,7 @@ export class DialogLovelaceResourceDetail extends LitElement { diff --git a/src/panels/config/repairs/dialog-repairs-issue.ts b/src/panels/config/repairs/dialog-repairs-issue.ts index 95810186ba..179d3ac62a 100644 --- a/src/panels/config/repairs/dialog-repairs-issue.ts +++ b/src/panels/config/repairs/dialog-repairs-issue.ts @@ -70,7 +70,7 @@ class DialogRepairsIssue extends LitElement { diff --git a/src/panels/config/users/dialog-admin-change-password.ts b/src/panels/config/users/dialog-admin-change-password.ts index b51ba5cde3..fe1c9cb29a 100644 --- a/src/panels/config/users/dialog-admin-change-password.ts +++ b/src/panels/config/users/dialog-admin-change-password.ts @@ -122,7 +122,7 @@ class DialogAdminChangePassword extends LitElement { )}

- ${this.hass.localize("ui.dialogs.generic.ok")} + ${this.hass.localize("ui.common.ok")} ` : html` diff --git a/src/panels/config/voice-assistants/dialog-expose-entity.ts b/src/panels/config/voice-assistants/dialog-expose-entity.ts index 6197fa0841..d8b523c7ef 100644 --- a/src/panels/config/voice-assistants/dialog-expose-entity.ts +++ b/src/panels/config/voice-assistants/dialog-expose-entity.ts @@ -74,7 +74,7 @@ class DialogExposeEntity extends LitElement { Date: Fri, 28 Feb 2025 15:52:40 +0100 Subject: [PATCH 21/38] Use card text align variable for header text alignment (#24451) Use card text align variable for header text aligment --- src/panels/lovelace/cards/hui-markdown-card.ts | 1 + src/panels/lovelace/views/hui-sections-view.ts | 1 - src/panels/lovelace/views/hui-view-header.ts | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/cards/hui-markdown-card.ts b/src/panels/lovelace/cards/hui-markdown-card.ts index b1f2d71ca9..6d2e98e906 100644 --- a/src/panels/lovelace/cards/hui-markdown-card.ts +++ b/src/panels/lovelace/cards/hui-markdown-card.ts @@ -238,6 +238,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { ha-markdown { padding: 16px; word-wrap: break-word; + text-align: var(--card-text-align, inherit); } .with-header ha-markdown { padding: 0 16px 16px; diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index 45df6f7979..58d45edba8 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -564,7 +564,6 @@ export class SectionsView extends LitElement implements LovelaceViewElement { hui-view-header { display: block; - text-align: center; padding: 0 var(--column-gap); padding-top: var(--row-gap); margin: auto; diff --git a/src/panels/lovelace/views/hui-view-header.ts b/src/panels/lovelace/views/hui-view-header.ts index 278e17f4bd..cf1e751a41 100644 --- a/src/panels/lovelace/views/hui-view-header.ts +++ b/src/panels/lovelace/views/hui-view-header.ts @@ -352,11 +352,11 @@ export class HuiViewHeader extends LitElement { } .layout .heading { - text-align: start; + --card-text-align: start; } .layout.center .heading { - text-align: center; + --card-text-align: center; display: flex; flex-direction: column; align-items: center; From 209d0ae5f493381d60df12a99de82e1ca7720835 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 28 Feb 2025 16:01:33 +0100 Subject: [PATCH 22/38] Add support for toggle card feature for automation domain (#24452) --- .../lovelace/card-features/hui-toggle-card-feature.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts index 96cfce4161..757766f622 100644 --- a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts @@ -27,9 +27,14 @@ import type { ToggleCardFeatureConfig } from "./types"; export const supportsToggleCardFeature = (stateObj: HassEntity) => { const domain = computeDomain(stateObj.entity_id); - return ["switch", "input_boolean", "light", "fan", "siren", "valve"].includes( - domain - ); + return [ + "switch", + "input_boolean", + "light", + "fan", + "siren", + "automation", + ].includes(domain); }; const DOMAIN_ICONS: Record = { From 7120200fd453f8a0b333c7ad7793f20e217b2179 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 28 Feb 2025 16:25:24 +0100 Subject: [PATCH 23/38] Bumped version to 20250228.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bf88ad41f7..1eee993f7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250227.0" +version = "20250228.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 76b03d3a405d766c742dbb34f94088f49d6d9585 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 28 Feb 2025 16:46:53 +0100 Subject: [PATCH 24/38] Correctly parse number state for numeric input card feature (#24453) --- .../hui-numeric-input-card-feature.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-numeric-input-card-feature.ts b/src/panels/lovelace/card-features/hui-numeric-input-card-feature.ts index b4d1f6cbd8..561ce890cf 100644 --- a/src/panels/lovelace/card-features/hui-numeric-input-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-numeric-input-card-feature.ts @@ -83,13 +83,16 @@ class HuiNumericInputCardFeature const stateObj = this.stateObj; + const parsedState = Number(stateObj.state); + const value = !isNaN(parsedState) ? parsedState : undefined; + if (this._config.style === "buttons") { return html` Date: Wed, 5 Mar 2025 01:38:13 -0800 Subject: [PATCH 25/38] Remember hidden energy devices from storage (#24470) * Remember hidden devices from storage * remove accidental * typing fix --- src/components/chart/ha-chart-base.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 8ebea677b3..f831b3791a 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -316,6 +316,16 @@ export class HaChartBase extends LitElement { } }); } + + const legend = ensureArray(this.options?.legend || [])[0] as + | LegendComponentOption + | undefined; + Object.entries(legend?.selected || {}).forEach(([stat, selected]) => { + if (selected === false) { + this._hiddenDatasets.add(stat); + } + }); + this.chart.setOption({ ...this._createOptions(), series: this._getSeries(), From e2ff8ce302513eb82b8a1094ac993908562e92ce Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 4 Mar 2025 15:51:54 +0200 Subject: [PATCH 26/38] Tweak legend expand/collapse button (#24484) * Tweak legend expand/collapse button * Revert "Tweak legend expand/collapse button" This reverts commit 5eafdad781c8d9fb9800ae433cc622001c46338d. * update UX --- src/components/chart/ha-chart-base.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index f831b3791a..2975610e9b 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -252,9 +252,11 @@ export class HaChartBase extends LitElement { Date: Wed, 5 Mar 2025 12:04:56 +0200 Subject: [PATCH 27/38] Set chart axis pointer line to --info-color (#24494) --- src/components/chart/ha-chart-base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 2975610e9b..b5656fad04 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -574,8 +574,8 @@ export class HaChartBase extends LitElement { fontSize: 12, }, axisPointer: { - lineStyle: { color: style.getPropertyValue("--divider-color") }, - crossStyle: { color: style.getPropertyValue("--divider-color") }, + lineStyle: { color: style.getPropertyValue("--info-color") }, + crossStyle: { color: style.getPropertyValue("--info-color") }, }, }, timeline: {}, From 4783720aaadca4f9ff548b1ce231eead36116728 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:20:18 -0800 Subject: [PATCH 28/38] No integer validation on ha-form-float (#24501) --- src/components/ha-form/ha-form-float.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-form/ha-form-float.ts b/src/components/ha-form/ha-form-float.ts index 7669365ba5..5518c48158 100644 --- a/src/components/ha-form/ha-form-float.ts +++ b/src/components/ha-form/ha-form-float.ts @@ -38,6 +38,7 @@ export class HaFormFloat extends LitElement implements HaFormElement { Date: Wed, 5 Mar 2025 10:25:00 +0100 Subject: [PATCH 29/38] Remove touch action none for toggle feature (#24514) --- src/panels/lovelace/card-features/hui-toggle-card-feature.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts index 757766f622..3cef688c9c 100644 --- a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts @@ -163,7 +163,6 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature { return html` Date: Wed, 5 Mar 2025 11:08:52 +0100 Subject: [PATCH 30/38] Bumped version to 20250305.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1eee993f7c..68228b8f2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250228.0" +version = "20250305.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 690cd47945e96ecdc116243f4132171742a53af1 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:33:33 +0100 Subject: [PATCH 31/38] Landingpage add core checks before show errors (#24493) * Check core only if supervisor or observer is unresponsive * Improve core check * Revert test code * Remove unused prop * Combine network info with core check * Combine ping and network info * Add 30 sec timeout before show errors * Update landing-page/src/ha-landing-page.ts * Assume supervisor update on failed ping * Fix typo --- .../src/components/landing-page-logs.ts | 7 +- .../src/components/landing-page-network.ts | 152 ++++++------------ landing-page/src/data/supervisor.ts | 20 ++- landing-page/src/ha-landing-page.ts | 96 ++++++++--- src/common/util/wait.ts | 6 + src/data/hassio/network.ts | 2 +- 6 files changed, 154 insertions(+), 129 deletions(-) create mode 100644 src/common/util/wait.ts diff --git a/landing-page/src/components/landing-page-logs.ts b/landing-page/src/components/landing-page-logs.ts index f4745fd2eb..0b0c3e7c81 100644 --- a/landing-page/src/components/landing-page-logs.ts +++ b/landing-page/src/components/landing-page-logs.ts @@ -22,6 +22,8 @@ import { import { fireEvent } from "../../../src/common/dom/fire_event"; import { fileDownload } from "../../../src/util/file_download"; import { getSupervisorLogs, getSupervisorLogsFollow } from "../data/supervisor"; +import { waitForSeconds } from "../../../src/common/util/wait"; +import { ASSUME_CORE_START_SECONDS } from "../ha-landing-page"; const ERROR_CHECK = /^[\d\s-:]+(ERROR|CRITICAL)(.*)/gm; declare global { @@ -216,7 +218,7 @@ class LandingPageLogs extends LitElement { // eslint-disable-next-line no-console console.error(err); - // fallback to observerlogs if there is a problem with supervisor + // fallback to observer logs if there is a problem with supervisor this._loadObserverLogs(); } } @@ -251,6 +253,9 @@ class LandingPageLogs extends LitElement { this._scheduleObserverLogs(); } catch (err) { + // wait because there is a moment where landingpage is down and core is not up yet + await waitForSeconds(ASSUME_CORE_START_SECONDS); + // eslint-disable-next-line no-console console.error(err); this._error = true; diff --git a/landing-page/src/components/landing-page-network.ts b/landing-page/src/components/landing-page-network.ts index bf5a7e9115..bea2336e48 100644 --- a/landing-page/src/components/landing-page-network.ts +++ b/landing-page/src/components/landing-page-network.ts @@ -1,13 +1,7 @@ import "@material/mwc-linear-progress/mwc-linear-progress"; -import { - type CSSResultGroup, - LitElement, - type PropertyValues, - css, - html, - nothing, -} from "lit"; -import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { type CSSResultGroup, LitElement, css, html, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; import type { LandingPageKeys, LocalizeFunc, @@ -16,34 +10,24 @@ import "../../../src/components/ha-button"; import "../../../src/components/ha-alert"; import { ALTERNATIVE_DNS_SERVERS, - getSupervisorNetworkInfo, - pingSupervisor, setSupervisorNetworkDns, + type NetworkInfo, } from "../data/supervisor"; -import { fireEvent } from "../../../src/common/dom/fire_event"; import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box"; - -const SCHEDULE_FETCH_NETWORK_INFO_SECONDS = 5; +import type { NetworkInterface } from "../../../src/data/hassio/network"; +import { fireEvent } from "../../../src/common/dom/fire_event"; @customElement("landing-page-network") class LandingPageNetwork extends LitElement { @property({ attribute: false }) public localize!: LocalizeFunc; - @state() private _networkIssue = false; + @property({ attribute: false }) public networkInfo?: NetworkInfo; - @state() private _getNetworkInfoError = false; - - @state() private _dnsPrimaryInterfaceNameservers?: string; - - @state() private _dnsPrimaryInterface?: string; + @property({ type: Boolean }) public error = false; protected render() { - if (!this._networkIssue && !this._getNetworkInfoError) { - return nothing; - } - - if (this._getNetworkInfoError) { + if (this.error) { return html`

${this.localize("network_issue.error_get_network_info")}

@@ -51,6 +35,16 @@ class LandingPageNetwork extends LitElement { `; } + let dnsPrimaryInterfaceNameservers: string | undefined; + + const primaryInterface = this._getPrimaryInterface( + this.networkInfo?.interfaces + ); + if (primaryInterface) { + dnsPrimaryInterfaceNameservers = + this._getPrimaryNameservers(primaryInterface); + } + return html`

${this.localize("network_issue.description", { - dns: this._dnsPrimaryInterfaceNameservers || "?", + dns: dnsPrimaryInterfaceNameservers || "?", })}

${this.localize("network_issue.resolve_different")}

- ${!this._dnsPrimaryInterfaceNameservers + ${!dnsPrimaryInterfaceNameservers ? html`

${this.localize("network_issue.no_primary_interface")} @@ -74,7 +68,7 @@ class LandingPageNetwork extends LitElement { ({ translationKey }, key) => html`${this.localize(translationKey)}` @@ -84,97 +78,40 @@ class LandingPageNetwork extends LitElement { `; } - protected firstUpdated(_changedProperties: PropertyValues): void { - super.firstUpdated(_changedProperties); - this._pingSupervisor(); - } + private _getPrimaryInterface = memoizeOne((interfaces?: NetworkInterface[]) => + interfaces?.find((intf) => intf.primary && intf.enabled) + ); - private _schedulePingSupervisor() { - setTimeout( - () => this._pingSupervisor(), - SCHEDULE_FETCH_NETWORK_INFO_SECONDS * 1000 - ); - } - - private async _pingSupervisor() { - try { - const response = await pingSupervisor(); - if (!response.ok) { - throw new Error("Failed to ping supervisor, assume update in progress"); - } - this._fetchSupervisorInfo(); - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - this._schedulePingSupervisor(); - } - } - - private _scheduleFetchSupervisorInfo() { - setTimeout( - () => this._fetchSupervisorInfo(), - SCHEDULE_FETCH_NETWORK_INFO_SECONDS * 1000 - ); - } - - private async _fetchSupervisorInfo() { - let data; - try { - const response = await getSupervisorNetworkInfo(); - if (!response.ok) { - throw new Error("Failed to fetch network info"); - } - - ({ data } = await response.json()); - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - this._getNetworkInfoError = true; - this._dnsPrimaryInterfaceNameservers = undefined; - this._dnsPrimaryInterface = undefined; - return; - } - - this._getNetworkInfoError = false; - - const primaryInterface = data.interfaces.find( - (intf) => intf.primary && intf.enabled - ); - if (primaryInterface) { - this._dnsPrimaryInterfaceNameservers = [ + private _getPrimaryNameservers = memoizeOne( + (primaryInterface: NetworkInterface) => + [ ...(primaryInterface.ipv4?.nameservers || []), ...(primaryInterface.ipv6?.nameservers || []), - ].join(", "); - - this._dnsPrimaryInterface = primaryInterface.interface; - } else { - this._dnsPrimaryInterfaceNameservers = undefined; - this._dnsPrimaryInterface = undefined; - } - - if (!data.host_internet) { - this._networkIssue = true; - } else { - this._networkIssue = false; - } - - fireEvent(this, "value-changed", { - value: this._networkIssue, - }); - this._scheduleFetchSupervisorInfo(); - } + ].join(", ") + ); private async _setDns(ev) { + const primaryInterface = this._getPrimaryInterface( + this.networkInfo?.interfaces + ); + const index = ev.target?.index; try { + const dnsPrimaryInterface = primaryInterface?.interface; + if (!dnsPrimaryInterface) { + throw new Error("No primary interface found"); + } + const response = await setSupervisorNetworkDns( index, - this._dnsPrimaryInterface! + dnsPrimaryInterface ); if (!response.ok) { throw new Error("Failed to set DNS"); } - this._networkIssue = false; + + // notify landing page to trigger a network info reload + fireEvent(this, "dns-set"); } catch (err: any) { // eslint-disable-next-line no-console console.error(err); @@ -205,4 +142,7 @@ declare global { interface HTMLElementTagNameMap { "landing-page-network": LandingPageNetwork; } + interface HASSDomEvents { + "dns-set": undefined; + } } diff --git a/landing-page/src/data/supervisor.ts b/landing-page/src/data/supervisor.ts index 41c28297e0..1f0fcff24d 100644 --- a/landing-page/src/data/supervisor.ts +++ b/landing-page/src/data/supervisor.ts @@ -1,4 +1,17 @@ import type { LandingPageKeys } from "../../../src/common/translations/localize"; +import type { HassioResponse } from "../../../src/data/hassio/common"; +import type { + DockerNetwork, + NetworkInterface, +} from "../../../src/data/hassio/network"; +import { handleFetchPromise } from "../../../src/util/hass-call-api"; + +export interface NetworkInfo { + interfaces: NetworkInterface[]; + docker: DockerNetwork; + host_internet: boolean; + supervisor_internet: boolean; +} export const ALTERNATIVE_DNS_SERVERS: { ipv4: string[]; @@ -37,8 +50,11 @@ export async function pingSupervisor() { return fetch("/supervisor-api/supervisor/ping"); } -export async function getSupervisorNetworkInfo() { - return fetch("/supervisor-api/network/info"); +export async function getSupervisorNetworkInfo(): Promise { + const responseData = await handleFetchPromise>( + fetch("/supervisor-api/network/info") + ); + return responseData?.data; } export const setSupervisorNetworkDns = async ( diff --git a/landing-page/src/ha-landing-page.ts b/landing-page/src/ha-landing-page.ts index 83faa88675..e4c55d6311 100644 --- a/landing-page/src/ha-landing-page.ts +++ b/landing-page/src/ha-landing-page.ts @@ -10,36 +10,56 @@ import { extractSearchParam } from "../../src/common/url/search-params"; import { onBoardingStyles } from "../../src/onboarding/styles"; import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; import { LandingPageBaseElement } from "./landing-page-base-element"; +import { + getSupervisorNetworkInfo, + pingSupervisor, + type NetworkInfo, +} from "./data/supervisor"; -const SCHEDULE_CORE_CHECK_SECONDS = 5; +export const ASSUME_CORE_START_SECONDS = 30; +const SCHEDULE_CORE_CHECK_SECONDS = 1; +const SCHEDULE_FETCH_NETWORK_INFO_SECONDS = 5; @customElement("ha-landing-page") class HaLandingPage extends LandingPageBaseElement { @property({ attribute: false }) public translationFragment = "landing-page"; - @state() private _networkIssue = false; - @state() private _supervisorError = false; + @state() private _networkInfo?: NetworkInfo; + + @state() private _coreStatusChecked = false; + + @state() private _networkInfoError = false; + + @state() private _coreCheckActive = false; + private _mobileApp = extractSearchParam("redirect_uri") === "homeassistant://auth-callback"; render() { + const networkIssue = this._networkInfo && !this._networkInfo.host_internet; + return html`

${this.localize("header")}

- ${!this._networkIssue && !this._supervisorError + ${!networkIssue && !this._supervisorError ? html`

${this.localize("subheader")}

` : nothing} - - + ${networkIssue || this._networkInfoError + ? html` + + ` + : nothing} ${this._supervisorError ? html` this._checkCoreAvailability(), - SCHEDULE_CORE_CHECK_SECONDS * 1000 + () => this._fetchSupervisorInfo(true), + // on assumed core start check every second, otherwise every 5 seconds + (this._coreCheckActive + ? SCHEDULE_CORE_CHECK_SECONDS + : SCHEDULE_FETCH_NETWORK_INFO_SECONDS) * 1000 ); } + private _scheduleTurnOffCoreCheck() { + setTimeout(() => { + this._coreCheckActive = false; + }, ASSUME_CORE_START_SECONDS * 1000); + } + + private async _fetchSupervisorInfo(schedule = false) { + try { + const response = await pingSupervisor(); + if (!response.ok) { + throw new Error("ping-failed"); + } + + this._networkInfo = await getSupervisorNetworkInfo(); + this._networkInfoError = false; + this._coreStatusChecked = false; + } catch (err: any) { + if (!this._coreStatusChecked) { + // wait before show errors, because we assume that core is starting + this._coreCheckActive = true; + this._scheduleTurnOffCoreCheck(); + } + await this._checkCoreAvailability(); + + // assume supervisor update if ping fails -> don't show an error + if (!this._coreCheckActive && err.message !== "ping-failed") { + // eslint-disable-next-line no-console + console.error(err); + this._networkInfoError = true; + } + } + + if (schedule) { + this._scheduleFetchSupervisorInfo(); + } + } + private async _checkCoreAvailability() { try { const response = await fetch("/manifest.json"); if (response.ok) { location.reload(); + } else { + throw new Error("Failed to fetch manifest"); } - } finally { - this._scheduleCoreCheck(); + } catch (_err) { + this._coreStatusChecked = true; } } @@ -113,10 +175,6 @@ class HaLandingPage extends LandingPageBaseElement { this._supervisorError = true; } - private _networkInfoChanged(ev: CustomEvent) { - this._networkIssue = ev.detail.value; - } - private _languageChanged(ev: CustomEvent) { const language = ev.detail.value; if (language !== this.language && language) { diff --git a/src/common/util/wait.ts b/src/common/util/wait.ts new file mode 100644 index 0000000000..7ed8544d66 --- /dev/null +++ b/src/common/util/wait.ts @@ -0,0 +1,6 @@ +export const waitForMs = (ms: number) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +export const waitForSeconds = (seconds: number) => waitForMs(seconds * 1000); diff --git a/src/data/hassio/network.ts b/src/data/hassio/network.ts index 0107e9b033..40500360af 100644 --- a/src/data/hassio/network.ts +++ b/src/data/hassio/network.ts @@ -21,7 +21,7 @@ export interface NetworkInterface { wifi?: Partial | null; } -interface DockerNetwork { +export interface DockerNetwork { address: string; dns: string; gateway: string; From 782df0473c7178218c2f8033f4cb2a0e9c9f19f0 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Wed, 5 Mar 2025 15:05:01 +0200 Subject: [PATCH 32/38] Fix height of chart legend (#24519) --- src/components/chart/ha-chart-base.ts | 2 +- src/panels/lovelace/cards/hui-history-graph-card.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index b5656fad04..8bda2bfba8 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -701,7 +701,7 @@ export class HaChartBase extends LitElement { .chart-legend { max-height: 60%; overflow-y: auto; - margin: 12px 0 0; + padding: 12px 0 0; font-size: 12px; color: var(--primary-text-color); } diff --git a/src/panels/lovelace/cards/hui-history-graph-card.ts b/src/panels/lovelace/cards/hui-history-graph-card.ts index 8da9b69191..65847c5ad0 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.ts +++ b/src/panels/lovelace/cards/hui-history-graph-card.ts @@ -261,6 +261,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { class="content ${classMap({ "has-header": !!this._config.title, "has-rows": !!this._config.grid_options?.rows, + "has-height": hasFixedHeight, })}" > ${this._error @@ -320,9 +321,11 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { padding-top: 0; } state-history-charts { - height: 100%; --timeline-top-margin: 16px; } + .has-height state-history-charts { + height: 100%; + } .has-rows { --chart-max-height: 100%; } From f132a32fd4dba89538f8a58ebac945427c8395f4 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Thu, 6 Mar 2025 12:30:17 +0200 Subject: [PATCH 33/38] Ignore excessive keydown events in charts (#24523) * Ignore excessive keydown events in charts * lint --- src/components/chart/ha-chart-base.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 8bda2bfba8..bbf2fcacd7 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -108,7 +108,10 @@ export class HaChartBase extends LitElement { // Add keyboard event listeners const handleKeyDown = (ev: KeyboardEvent) => { - if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) { + if ( + !this._modifierPressed && + ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) + ) { this._modifierPressed = true; if (!this.options?.dataZoom) { this._setChartOptions({ dataZoom: this._getDataZoomConfig() }); @@ -123,7 +126,10 @@ export class HaChartBase extends LitElement { }; const handleKeyUp = (ev: KeyboardEvent) => { - if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) { + if ( + this._modifierPressed && + ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) + ) { this._modifierPressed = false; if (!this.options?.dataZoom) { this._setChartOptions({ dataZoom: this._getDataZoomConfig() }); From 022ef982caa59195916f3daa94ac7deca57596bc Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 6 Mar 2025 15:55:20 +0100 Subject: [PATCH 34/38] Only recreate stack editor when the type or index change (#24530) --- .../config-elements/hui-stack-card-editor.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts index 595eae1866..c08c959c22 100644 --- a/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts @@ -85,7 +85,7 @@ export class HuiStackCardEditor @state() protected _guiModeAvailable? = true; - protected _keys = new WeakMap(); + protected _keys = new Map(); protected _schema: readonly HaFormSchema[] = SCHEMA; @@ -203,7 +203,7 @@ export class HuiStackCardEditor >
${keyed( - this._getKey(this._config.cards[selected]), + this._getKey(this._config.cards, selected), html` Date: Thu, 6 Mar 2025 15:57:06 +0100 Subject: [PATCH 35/38] Bumped version to 20250306.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 68228b8f2e..f88c997698 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250305.0" +version = "20250306.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From e871dc8151601109cbaa27af59f324b3b64f26ae Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 12 Mar 2025 16:19:42 +0100 Subject: [PATCH 36/38] Increase core start seconds (#24604) --- landing-page/src/ha-landing-page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing-page/src/ha-landing-page.ts b/landing-page/src/ha-landing-page.ts index e4c55d6311..06d1c43716 100644 --- a/landing-page/src/ha-landing-page.ts +++ b/landing-page/src/ha-landing-page.ts @@ -16,7 +16,7 @@ import { type NetworkInfo, } from "./data/supervisor"; -export const ASSUME_CORE_START_SECONDS = 30; +export const ASSUME_CORE_START_SECONDS = 60; const SCHEDULE_CORE_CHECK_SECONDS = 1; const SCHEDULE_FETCH_NETWORK_INFO_SECONDS = 5; From df934cfed957748633836b013fee9e1e9a1b60a3 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:06:31 -0700 Subject: [PATCH 37/38] Energy self sufficiency gauge needs grid consumption (#24606) --- src/panels/energy/strategies/energy-view-strategy.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/panels/energy/strategies/energy-view-strategy.ts b/src/panels/energy/strategies/energy-view-strategy.ts index 92d61c435e..808d4504b0 100644 --- a/src/panels/energy/strategies/energy-view-strategy.ts +++ b/src/panels/energy/strategies/energy-view-strategy.ts @@ -148,11 +148,13 @@ export class EnergyViewStrategy extends ReactiveElement { collection_key: "energy_dashboard", }); } - view.cards!.push({ - type: "energy-self-sufficiency-gauge", - view_layout: { position: "sidebar" }, - collection_key: "energy_dashboard", - }); + if (hasGrid) { + view.cards!.push({ + type: "energy-self-sufficiency-gauge", + view_layout: { position: "sidebar" }, + collection_key: "energy_dashboard", + }); + } } // Only include if we have a grid From 46c9af75fde7d8507fca28ac03c57def0417fc05 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 12 Mar 2025 18:53:49 +0100 Subject: [PATCH 38/38] Bumped version to 20250312.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f88c997698..a1049bd9ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250306.0" +version = "20250312.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md"
${uuid}