From e1b34eaa3390191e52913587281539425342b081 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:27:03 -0800 Subject: [PATCH 01/49] Dont floor duration for milliseconds (#23028) * Dont floor duration for milliseconds * remove ms --- src/common/datetime/format_duration.ts | 17 +---------------- test/common/datetime/format_duration.test.ts | 8 -------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/common/datetime/format_duration.ts b/src/common/datetime/format_duration.ts index a67d5f82ca..8f8c9be41f 100644 --- a/src/common/datetime/format_duration.ts +++ b/src/common/datetime/format_duration.ts @@ -72,7 +72,7 @@ export const formatDurationDigital = ( duration: HaDurationData ) => formatDigitalDurationMem(locale).format(duration); -export const DURATION_UNITS = ["ms", "s", "min", "h", "d"] as const; +export const DURATION_UNITS = ["s", "min", "h", "d"] as const; type DurationUnit = (typeof DURATION_UNITS)[number]; @@ -108,14 +108,6 @@ const formatDurationSecondMem = memoizeOne( }) ); -const formatDurationMillisecondMem = memoizeOne( - (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { - style: "narrow", - millisecondsDisplay: "always", - }) -); - export const formatDuration = ( locale: FrontendLocaleData, duration: string, @@ -164,13 +156,6 @@ export const formatDuration = ( }; return formatDurationSecondMem(locale).format(input); } - case "ms": { - const milliseconds = Math.floor(value); - const input: DurationInput = { - milliseconds, - }; - return formatDurationMillisecondMem(locale).format(input); - } default: throw new Error("Invalid duration unit"); } diff --git a/test/common/datetime/format_duration.test.ts b/test/common/datetime/format_duration.test.ts index a713dee22c..adac5ce1c3 100644 --- a/test/common/datetime/format_duration.test.ts +++ b/test/common/datetime/format_duration.test.ts @@ -21,14 +21,6 @@ const LOCALE: FrontendLocaleData = { describe("formatDuration", () => { it("works", () => { - assert.strictEqual(formatDuration(LOCALE, "0", "ms"), "0ms"); - assert.strictEqual(formatDuration(LOCALE, "1", "ms"), "1ms"); - assert.strictEqual(formatDuration(LOCALE, "10", "ms"), "10ms"); - assert.strictEqual(formatDuration(LOCALE, "100", "ms"), "100ms"); - assert.strictEqual(formatDuration(LOCALE, "1000", "ms"), "1,000ms"); - assert.strictEqual(formatDuration(LOCALE, "1001", "ms"), "1,001ms"); - assert.strictEqual(formatDuration(LOCALE, "65000", "ms"), "65,000ms"); - assert.strictEqual(formatDuration(LOCALE, "0.5", "s"), "0s 500ms"); assert.strictEqual(formatDuration(LOCALE, "1", "s"), "1s"); assert.strictEqual(formatDuration(LOCALE, "1.1", "s"), "1s 100ms"); From 795bbefba67e26d3291e2b9975008d1a26845dc3 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:27:53 +0100 Subject: [PATCH 02/49] Fix platinum color and spacing of integration logo (#23029) --- src/panels/config/integrations/ha-config-integration-page.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 14a3282e92..6794544c3a 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -1496,6 +1496,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { .logo-container { display: flex; justify-content: center; + margin-bottom: 8px; } .version { padding-top: 8px; @@ -1548,7 +1549,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { color: gold; } ha-svg-icon.platinum-medal { - color: #d9d9d9; + color: #727272; } ha-md-list-item { position: relative; From 8d7c175d70c3ec7df78018058960c868d3c1cd61 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 27 Nov 2024 17:13:29 +0100 Subject: [PATCH 03/49] Only use duration poly-fill when necessary (#23030) --- build-scripts/gulp/locale-data.js | 1 - src/common/datetime/format_duration.ts | 13 ++++++------- src/types.ts | 11 +++++++++-- test/common/datetime/format_duration.test.ts | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build-scripts/gulp/locale-data.js b/build-scripts/gulp/locale-data.js index 14ce9bc06a..4cf91711d4 100755 --- a/build-scripts/gulp/locale-data.js +++ b/build-scripts/gulp/locale-data.js @@ -9,7 +9,6 @@ const outDir = join(paths.build_dir, "locale-data"); const INTL_POLYFILLS = { "intl-datetimeformat": "DateTimeFormat", - "intl-durationFormat": "DurationFormat", "intl-displaynames": "DisplayNames", "intl-listformat": "ListFormat", "intl-numberformat": "NumberFormat", diff --git a/src/common/datetime/format_duration.ts b/src/common/datetime/format_duration.ts index 8f8c9be41f..e20f7e18af 100644 --- a/src/common/datetime/format_duration.ts +++ b/src/common/datetime/format_duration.ts @@ -1,4 +1,3 @@ -import { DurationFormat } from "@formatjs/intl-durationformat"; import type { DurationInput } from "@formatjs/intl-durationformat/src/types"; import memoizeOne from "memoize-one"; import type { HaDurationData } from "../../components/ha-duration-input"; @@ -49,7 +48,7 @@ export const formatNumericDuration = ( const formatDurationLongMem = memoizeOne( (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { + new Intl.DurationFormat(locale.language, { style: "long", }) ); @@ -61,7 +60,7 @@ export const formatDurationLong = ( const formatDigitalDurationMem = memoizeOne( (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { + new Intl.DurationFormat(locale.language, { style: "digital", hoursDisplay: "auto", }) @@ -78,7 +77,7 @@ type DurationUnit = (typeof DURATION_UNITS)[number]; const formatDurationDayMem = memoizeOne( (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { + new Intl.DurationFormat(locale.language, { style: "narrow", daysDisplay: "always", }) @@ -86,7 +85,7 @@ const formatDurationDayMem = memoizeOne( const formatDurationHourMem = memoizeOne( (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { + new Intl.DurationFormat(locale.language, { style: "narrow", hoursDisplay: "always", }) @@ -94,7 +93,7 @@ const formatDurationHourMem = memoizeOne( const formatDurationMinuteMem = memoizeOne( (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { + new Intl.DurationFormat(locale.language, { style: "narrow", minutesDisplay: "always", }) @@ -102,7 +101,7 @@ const formatDurationMinuteMem = memoizeOne( const formatDurationSecondMem = memoizeOne( (locale: FrontendLocaleData) => - new DurationFormat(locale.language, { + new Intl.DurationFormat(locale.language, { style: "narrow", secondsDisplay: "always", }) diff --git a/src/types.ts b/src/types.ts index 67d5f75838..3d8a78ac50 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ +import type { DurationFormatConstructor } from "@formatjs/intl-durationformat/src/types"; import type { Auth, Connection, @@ -22,7 +23,7 @@ import type { Themes } from "./data/ws-themes"; import type { ExternalMessaging } from "./external_app/external_messaging"; declare global { - /* eslint-disable no-var, no-redeclare */ + /* eslint-disable no-var */ var __DEV__: boolean; var __DEMO__: boolean; var __BUILD__: "modern" | "legacy"; @@ -30,7 +31,7 @@ declare global { var __STATIC_PATH__: string; var __BACKWARDS_COMPAT__: boolean; var __SUPERVISOR__: boolean; - /* eslint-enable no-var, no-redeclare */ + /* eslint-enable no-var */ interface Window { // Custom panel entry point url @@ -64,6 +65,12 @@ declare global { interface ImportMeta { url: string; } + + // Intl.DurationFormat is not yet part of the TypeScript standard + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Intl { + const DurationFormat: DurationFormatConstructor; + } } export interface ValueChangedEvent extends CustomEvent { diff --git a/test/common/datetime/format_duration.test.ts b/test/common/datetime/format_duration.test.ts index adac5ce1c3..cd62785320 100644 --- a/test/common/datetime/format_duration.test.ts +++ b/test/common/datetime/format_duration.test.ts @@ -1,5 +1,5 @@ +import "@formatjs/intl-durationformat/polyfill-force"; import { assert, describe, it } from "vitest"; - import { formatDuration } from "../../../src/common/datetime/format_duration"; import type { FrontendLocaleData } from "../../../src/data/translation"; import { From 239cad9b47846a7deac2e7832e2bfdd280a11db0 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:58:34 +0100 Subject: [PATCH 04/49] Fix iqs naming and docs link anchor (#23036) --- .../ha-config-integration-page.ts | 31 +++++++------------ src/translations/en.json | 9 +++--- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 6794544c3a..5251a7a94d 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -354,25 +354,18 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { : mdiMedal} > - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.${this._manifest.quality_scale as MedalColor}_quality`, - { - quality_scale: html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.quality_scale" - )} - - `, - } - )} + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.${this._manifest.quality_scale as MedalColor}_quality` + )} + ` diff --git a/src/translations/en.json b/src/translations/en.json index 221ad621bc..99877d80af 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4521,11 +4521,10 @@ "setup_in_progress": "Initializing" }, "open_configuration_url": "Visit device", - "bronze_quality": "Bronze on our {quality_scale}", - "silver_quality": "Silver on our {quality_scale}", - "gold_quality": "Gold on our {quality_scale}", - "platinum_quality": "Platinum on our {quality_scale}", - "quality_scale": "quality scale" + "bronze_quality": "Bronze quality", + "silver_quality": "Silver quality", + "gold_quality": "Gold quality", + "platinum_quality": "Platinum quality" }, "config_flow": { "success": "Success", From 1edfec08e16decb5d728f4004a9eec6ce0bd480c Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:23:53 +0100 Subject: [PATCH 05/49] Add internal, legacy to IQS (#23040) --- src/data/integration.ts | 11 +-- src/data/integration_quality_scale.ts | 39 ++++++++++ .../ha-config-integration-page.ts | 75 +++++++++++-------- src/translations/en.json | 2 + 4 files changed, 91 insertions(+), 36 deletions(-) create mode 100644 src/data/integration_quality_scale.ts diff --git a/src/data/integration.ts b/src/data/integration.ts index 846751e07c..c293dfc540 100644 --- a/src/data/integration.ts +++ b/src/data/integration.ts @@ -40,12 +40,13 @@ export interface IntegrationManifest { loggers?: string[]; quality_scale?: | "bronze" - | "gold" - | "internal" - | "platinum" | "silver" - | "custom" - | "no_score"; + | "gold" + | "platinum" + | "no_score" + | "internal" + | "legacy" + | "custom"; iot_class: | "assumed_state" | "cloud_polling" diff --git a/src/data/integration_quality_scale.ts b/src/data/integration_quality_scale.ts new file mode 100644 index 0000000000..e49bbc835d --- /dev/null +++ b/src/data/integration_quality_scale.ts @@ -0,0 +1,39 @@ +import { mdiContentSave, mdiMedal, mdiTrophy } from "@mdi/js"; +import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg"; +import type { LocalizeKeys } from "../common/translations/localize"; + +/** + * Map integration quality scale to icon and translation key. + */ +export const QUALITY_SCALE_MAP: Record< + string, + { icon: string; translationKey: LocalizeKeys } +> = { + bronze: { + icon: mdiMedal, + translationKey: "ui.panel.config.integrations.config_entry.bronze_quality", + }, + silver: { + icon: mdiMedal, + translationKey: "ui.panel.config.integrations.config_entry.silver_quality", + }, + gold: { + icon: mdiMedal, + translationKey: "ui.panel.config.integrations.config_entry.gold_quality", + }, + platinum: { + icon: mdiTrophy, + translationKey: + "ui.panel.config.integrations.config_entry.platinum_quality", + }, + internal: { + icon: mdiHomeAssistant, + translationKey: + "ui.panel.config.integrations.config_entry.internal_integration", + }, + legacy: { + icon: mdiContentSave, + translationKey: + "ui.panel.config.integrations.config_entry.legacy_integration", + }, +}; diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 5251a7a94d..f44a03b00f 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -13,7 +13,6 @@ import { mdiDownload, mdiFileCodeOutline, mdiHandExtendedOutline, - mdiMedal, mdiOpenInNew, mdiPackageVariant, mdiPlayCircleOutline, @@ -23,7 +22,6 @@ import { mdiRenameBox, mdiShapeOutline, mdiStopCircleOutline, - mdiTrophy, mdiWeb, mdiWrench, } from "@mdi/js"; @@ -107,9 +105,7 @@ import { documentationUrl } from "../../../util/documentation-url"; import { fileDownload } from "../../../util/file_download"; import type { DataEntryFlowProgressExtended } from "./ha-config-integrations"; import { showAddIntegrationDialog } from "./show-add-integration-dialog"; - -type MedalColor = "gold" | "silver" | "bronze" | "platinum"; -const MEDAL_COLORS = ["bronze", "silver", "gold", "platinum"]; +import { QUALITY_SCALE_MAP } from "../../../data/integration_quality_scale"; export const renderConfigEntryError = ( hass: HomeAssistant, @@ -344,29 +340,30 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ? html`
${this._manifest.version}
` : nothing} ${this._manifest?.quality_scale && - MEDAL_COLORS.includes(this._manifest.quality_scale) + Object.keys(QUALITY_SCALE_MAP).includes( + this._manifest.quality_scale + ) ? html` ` : nothing} @@ -376,9 +373,18 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { class="warning" path=${mdiPackageVariant} > - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.custom_integration" - )} + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.custom_integration" + )} + ` : nothing} ${this._manifest?.iot_class?.startsWith("cloud_") @@ -1532,18 +1538,25 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { 100%; animation: shimmer 2.5s infinite; } - ha-svg-icon.bronze-medal { + ha-svg-icon.bronze-quality { color: #cd7f32; } - ha-svg-icon.silver-medal { + ha-svg-icon.silver-quality { color: silver; } - ha-svg-icon.gold-medal { + ha-svg-icon.gold-quality { color: gold; } - ha-svg-icon.platinum-medal { + ha-svg-icon.platinum-quality { color: #727272; } + ha-svg-icon.internal-quality { + color: var(--primary-color); + } + ha-svg-icon.legacy-quality { + color: var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38)); + animation: unset; + } ha-md-list-item { position: relative; } diff --git a/src/translations/en.json b/src/translations/en.json index 99877d80af..774479f15f 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4505,6 +4505,8 @@ } }, "custom_integration": "Custom integration", + "internal_integration": "Internal integration", + "legacy_integration": "Legacy integration", "custom_overwrites_core": "Custom integration that replaces a core component", "depends_on_cloud": "Depends on Internet connection", "yaml_only": "This integration cannot be setup from the UI", From 1061769144742ab6b55dda90c6e94a21c2f6986a Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:24:44 +0100 Subject: [PATCH 06/49] Simplify depends_on_cloud translation (#23042) --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 774479f15f..8069cf58ad 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4508,7 +4508,7 @@ "internal_integration": "Internal integration", "legacy_integration": "Legacy integration", "custom_overwrites_core": "Custom integration that replaces a core component", - "depends_on_cloud": "Depends on Internet connection", + "depends_on_cloud": "Requires Internet", "yaml_only": "This integration cannot be setup from the UI", "no_config_flow": "This integration was not set up from the UI", "disabled_polling": "Automatic polling for updated data disabled", From f6c15dc990979333287186dded77917daa58c70d Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 28 Nov 2024 16:53:58 +0100 Subject: [PATCH 07/49] Bumped version to 20241127.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5a79bd0d75..d373f50e27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.0" +version = "20241127.1" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 2550bff4e9954510e11f74b734cf9996d30228e4 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Mon, 2 Dec 2024 02:18:03 -0800 Subject: [PATCH 08/49] Init new scenes in live edit mode (#23051) * Init new scenes in live edit mode * Update src/panels/config/scene/ha-scene-editor.ts Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com> * imports --------- Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com> --- src/panels/config/scene/ha-scene-editor.ts | 23 +++++++++++++++++++--- src/translations/en.json | 8 ++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index 80b6a97918..5dce2d4577 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -7,7 +7,9 @@ import { mdiContentSave, mdiDelete, mdiDotsVertical, + mdiEye, mdiInformationOutline, + mdiMotionPlayOutline, mdiPlay, mdiTag, } from "@mdi/js"; @@ -204,6 +206,14 @@ export class HaSceneEditor extends SubscribeMixin( } ); + public connectedCallback() { + super.connectedCallback(); + if (!this.sceneId) { + this._mode = "live"; + this._subscribeEvents(); + } + } + public disconnectedCallback() { super.disconnectedCallback(); if (this._unsubscribeEvents) { @@ -387,15 +397,22 @@ export class HaSceneEditor extends SubscribeMixin( alert-type="info" .narrow=${this.narrow} .title=${this.hass.localize( - `ui.panel.config.scene.editor.${this._mode === "live" ? "live_preview" : "review_mode"}` + `ui.panel.config.scene.editor.${this._mode === "live" ? "live_edit" : "review_mode"}` )} > ${this.hass.localize( - `ui.panel.config.scene.editor.${this._mode === "live" ? "live_preview_detail" : "review_mode_detail"}` + `ui.panel.config.scene.editor.${this._mode === "live" ? "live_edit_detail" : "review_mode_detail"}` )} + + + ${this.hass.localize( - `ui.panel.config.scene.editor.${this._mode === "live" ? "back_to_review_mode" : "live_preview"}` + `ui.panel.config.scene.editor.${this._mode === "live" ? "switch_to_review_mode" : "live_edit"}` )} diff --git a/src/translations/en.json b/src/translations/en.json index 8069cf58ad..20018575bd 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3878,11 +3878,11 @@ }, "editor": { "review_mode": "Review Mode", - "review_mode_detail": "You can adjust the scene's details and remove devices or entities. To fully edit, switch to Live Preview, which will apply the scene.", - "live_preview": "Live Preview", - "live_preview_detail": "In Live Preview, all changes to this scene are applied in real-time to your devices and entities.", + "review_mode_detail": "You can adjust the scene's details and remove devices or entities. To fully edit, switch to Live Edit, which will apply the scene.", + "live_edit": "Live Edit", + "live_edit_detail": "In Live Edit, all changes to this scene are applied in real-time to your devices and entities.", "enter_live_mode_unsaved": "You have unsaved changes to this scene. Continuing to live preview will apply the saved scene, which may overwrite your unsaved changes. Consider if you would like to save the scene first before activating it.", - "back_to_review_mode": "Back to review mode", + "switch_to_review_mode": "Switch to review mode", "default_name": "New scene", "load_error_not_editable": "Only scenes in scenes.yaml are editable.", "load_error_unknown": "Error loading scene ({err_no}).", From 6d01728d54268bf6833a802b825ca850b74ad6f0 Mon Sep 17 00:00:00 2001 From: Alex Jurkiewicz Date: Mon, 2 Dec 2024 18:48:12 +0800 Subject: [PATCH 09/49] `history-graph-card-editor`: Correct `hours_to_show` validation (#23090) history-graph-card-editor: Correct hours_to_show validation Allow all floating point numbers from 0 up. Fixes #15933. --- .../editor/config-elements/hui-history-graph-card-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts index 2b76aa30a9..81ce443d72 100644 --- a/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts @@ -68,7 +68,7 @@ export class HuiHistoryGraphCardEditor { name: "hours_to_show", default: DEFAULT_HOURS_TO_SHOW, - selector: { number: { min: 1, mode: "box" } }, + selector: { number: { min: 0, step: "any", mode: "box" } }, }, ], }, From 05eb6e15a546ba96ec2215d1d80780b77f08c383 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:24:03 +0100 Subject: [PATCH 10/49] Save scene before switching to live edit (#23094) Save scene changes before live edit, align delete icons for entities. --- src/panels/config/scene/ha-scene-editor.ts | 10 ++++++++-- src/translations/en.json | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index 5dce2d4577..410aff18f9 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -559,6 +559,7 @@ export class HaSceneEditor extends SubscribeMixin( } return html` this._storeState(entity)); @@ -1326,6 +1329,9 @@ export class HaSceneEditor extends SubscribeMixin( li[role="separator"] { border-bottom-color: var(--divider-color); } + ha-list-item.entity { + padding-right: 28px; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index 20018575bd..193d38ce1a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3881,7 +3881,8 @@ "review_mode_detail": "You can adjust the scene's details and remove devices or entities. To fully edit, switch to Live Edit, which will apply the scene.", "live_edit": "Live Edit", "live_edit_detail": "In Live Edit, all changes to this scene are applied in real-time to your devices and entities.", - "enter_live_mode_unsaved": "You have unsaved changes to this scene. Continuing to live preview will apply the saved scene, which may overwrite your unsaved changes. Consider if you would like to save the scene first before activating it.", + "enter_live_mode_unsaved": "Before proceeding to Live Edit, please save your current changes.", + "save_before_live": "Save and Live Edit", "switch_to_review_mode": "Switch to review mode", "default_name": "New scene", "load_error_not_editable": "Only scenes in scenes.yaml are editable.", From 6ff1a6feccaf7d309ec2194f06ee72b3ebc25d8f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 2 Dec 2024 14:35:41 +0100 Subject: [PATCH 11/49] Don't show alert in voice assistant dialog (#23097) --- .../voice-command-dialog/ha-voice-command-dialog.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts index 4b7d020134..42c311bef2 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -134,12 +134,13 @@ export class HaVoiceCommandDialog extends LitElement { const controlHA = !this._pipeline ? false - : this.hass.states[this._pipeline?.conversation_engine] - ? supportsFeature( - this.hass.states[this._pipeline?.conversation_engine], - ConversationEntityFeature.CONTROL - ) - : true; + : this._pipeline.prefer_local_intents || + (this.hass.states[this._pipeline.conversation_engine] + ? supportsFeature( + this.hass.states[this._pipeline.conversation_engine], + ConversationEntityFeature.CONTROL + ) + : true); const supportsMicrophone = AudioRecorder.isSupported; const supportsSTT = this._pipeline?.stt_engine; From 41924d8ec67ddbfa177f02e542fe7dec9df25863 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:30:51 +0100 Subject: [PATCH 12/49] Add automatic retry to stream logs (#23098) --- src/data/hassio/supervisor.ts | 19 +++++ src/panels/config/logs/error-log-card.ts | 90 ++++++++++++++++-------- 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/data/hassio/supervisor.ts b/src/data/hassio/supervisor.ts index b14ffd8e56..a013aab07d 100644 --- a/src/data/hassio/supervisor.ts +++ b/src/data/hassio/supervisor.ts @@ -226,6 +226,25 @@ export const fetchHassioLogsFollow = async ( signal ); +export const fetchHassioLogsFollowSkip = async ( + hass: HomeAssistant, + provider: string, + signal: AbortSignal, + cursor: string, + skipLines: number, + lines = 100, + boot = 0 +) => + hass.callApiRaw( + "GET", + `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs${boot !== 0 ? `/boots/${boot}` : ""}/follow`, + undefined, + { + Range: `entries=${cursor}:${skipLines}:${lines}`, + }, + signal + ); + export const getHassioLogDownloadUrl = (provider: string) => `/api/hassio/${ provider.includes("_") ? `addons/${provider}` : provider diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index a5bd9e0b5f..0f551df687 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -50,6 +50,7 @@ import { fetchHassioBoots, fetchHassioLogs, fetchHassioLogsFollow, + fetchHassioLogsFollowSkip, fetchHassioLogsLegacy, getHassioLogDownloadLinesUrl, getHassioLogDownloadUrl, @@ -428,13 +429,21 @@ class ErrorLogCard extends LitElement { } } - private async _loadLogs(): Promise { + private async _loadLogs(retry = false): Promise { this._error = undefined; this._loadingState = "loading"; - this._loadingPrevState = undefined; - this._firstCursor = undefined; - this._numberOfLines = 0; - this._ansiToHtmlElement?.clear(); + this._numberOfLines = retry ? (this._numberOfLines ?? 0) : 0; + + if (!retry) { + this._loadingPrevState = undefined; + this._firstCursor = undefined; + this._ansiToHtmlElement?.clear(); + } + + const streamLogs = + this._streamSupported && + isComponentLoaded(this.hass, "hassio") && + this.provider; try { if (this._logStreamAborter) { @@ -442,32 +451,44 @@ class ErrorLogCard extends LitElement { this._logStreamAborter = undefined; } - if ( - this._streamSupported && - isComponentLoaded(this.hass, "hassio") && - this.provider - ) { + if (streamLogs) { this._logStreamAborter = new AbortController(); - // check if there are any logs at all - const testResponse = await fetchHassioLogs( - this.hass, - this.provider, - `entries=:-1:`, - this._boot - ); - const testLogs = await testResponse.text(); - if (!testLogs.trim()) { - this._loadingState = "empty"; + if (!retry) { + // check if there are any logs at all + const testResponse = await fetchHassioLogs( + this.hass, + this.provider!, + `entries=:-1:`, + this._boot + ); + const testLogs = await testResponse.text(); + if (!testLogs.trim()) { + this._loadingState = "empty"; + } } - const response = await fetchHassioLogsFollow( - this.hass, - this.provider, - this._logStreamAborter.signal, - NUMBER_OF_LINES, - this._boot - ); + let response: Response; + + if (retry && this._firstCursor) { + response = await fetchHassioLogsFollowSkip( + this.hass, + this.provider!, + this._logStreamAborter.signal, + this._firstCursor, + this._numberOfLines, + NUMBER_OF_LINES, + this._boot + ); + } else { + response = await fetchHassioLogsFollow( + this.hass, + this.provider!, + this._logStreamAborter.signal, + NUMBER_OF_LINES, + this._boot + ); + } if (response.headers.has("X-First-Cursor")) { this._firstCursor = response.headers.get("X-First-Cursor")!; @@ -524,7 +545,7 @@ class ErrorLogCard extends LitElement { if (!this._downloadSupported) { const downloadUrl = getHassioLogDownloadLinesUrl( - this.provider, + this.provider!, this._numberOfLines, this._boot ); @@ -532,6 +553,9 @@ class ErrorLogCard extends LitElement { this._logsFileLink = signedUrl.path; }); } + + // first chunk loads successfully, reset retry param + retry = false; } } } else { @@ -554,6 +578,13 @@ class ErrorLogCard extends LitElement { if (err.name === "AbortError") { return; } + + // The stream can fail if the connection is lost or firefox service worker intercept the connection + if (!retry && streamLogs) { + this._loadLogs(true); + return; + } + this._error = (this.localizeFunc || this.hass.localize)( "ui.panel.config.logs.failed_get_logs", { @@ -590,9 +621,10 @@ class ErrorLogCard extends LitElement { private _handleConnectionStatus = (ev: HASSDomEvent) => { if (ev.detail === "disconnected" && this._logStreamAborter) { this._logStreamAborter.abort(); + this._loadingState = "loading"; } if (ev.detail === "connected") { - this._loadLogs(); + this._loadLogs(true); } }; From 0a578c5847f53ceb832a120e1c0824e349cb8fa2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Dec 2024 20:48:24 +0100 Subject: [PATCH 13/49] Voice wizard local: Add error message, fix hostname (#23103) Add error message, fix hostname --- .../voice-assistant-setup-step-local.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts index 1fb690601f..69c50ef571 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts @@ -37,6 +37,8 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { @state() private _detailState?: string; + @state() private _error?: string; + @state() private _localTts?: EntityRegistryDisplayEntry[]; @state() private _localStt?: EntityRegistryDisplayEntry[]; @@ -62,6 +64,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { alt="Casita Home Assistant error logo" />

Failed to install add-ons

+

${this._error}

We could not automatically install a local TTS and STT provider for you. Read the documentation to learn how to install them. @@ -179,8 +182,9 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { } this._detailState = "Creating assistant"; await this._findEntitiesAndCreatePipeline(); - } catch (e) { + } catch (e: any) { this._state = "ERROR"; + this._error = e.message; } } @@ -199,11 +203,13 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { private async _setupConfigEntry(addon: string) { const configFlow = await createConfigFlow(this.hass, "wyoming"); const step = await handleConfigFlowStep(this.hass, configFlow.flow_id, { - host: `core_${addon}`, + host: `core-${addon}`, port: addon === "piper" ? 10200 : 10300, }); if (step.type !== "create_entry") { - throw new Error("Failed to create entry"); + throw new Error( + `Failed to create entry for ${addon}${"errors" in step ? `: ${step.errors.base}` : ""}` + ); } } @@ -321,7 +327,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { this._findLocalEntities(); if (!this._localTts?.length || !this._localStt?.length) { if (tryNo > 3) { - throw new Error("Timeout searching for local TTS and STT entities"); + throw new Error("Could not find local TTS and STT entities"); } await new Promise((resolve) => { setTimeout(resolve, 2000); From 04beef5e3677fa778b73d021f2210a74202157ad Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Dec 2024 21:31:47 +0100 Subject: [PATCH 14/49] Bumped version to 20241127.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d373f50e27..1803d94af1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.1" +version = "20241127.2" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 4f76e66cc01bd097a47fc339f7583eff31e77572 Mon Sep 17 00:00:00 2001 From: Marcin Date: Tue, 3 Dec 2024 10:25:56 +0100 Subject: [PATCH 15/49] Updated English translations for scene editor. (#23110) --- src/translations/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 193d38ce1a..bdb721c507 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3896,14 +3896,14 @@ "devices": { "header": "Devices", "introduction": "Add the devices that you want to be included in your scene. Set all entities in each device to the state you want for this scene.", - "introduction_review": "To add the devices that you want to be included in your scene turn on the Live Preview mode.", + "introduction_review": "To add the devices that you want to be included in your scene switch to the Live Edit mode.", "add": "Add a device", "delete": "Delete device" }, "entities": { "header": "Entities", "introduction": "Individual entities can be added here.", - "introduction_review": "To add individual entities to your scene turn on the Live Preview mode.", + "introduction_review": "To add individual entities to your scene switch to the Live Edit mode.", "without_device": "Entities", "add": "Add an entity", "delete": "Delete entity" From 8beb93b695e463e3fa698bc1152f6f3ac424caaf Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Dec 2024 12:01:54 +0100 Subject: [PATCH 16/49] Voice local: Small refactor and return when local already exists (#23113) --- .../voice-assistant-setup-step-local.ts | 40 +++++++++---------- .../voice-assistant-setup-step-pipeline.ts | 4 ++ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts index 69c50ef571..cd00187d5a 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts @@ -24,6 +24,7 @@ import type { HomeAssistant } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; import { AssistantSetupStyles } from "./styles"; import { STEP } from "./voice-assistant-setup-dialog"; +import { nextRender } from "../../common/util/render-status"; @customElement("ha-voice-assistant-setup-step-local") export class HaVoiceAssistantSetupStepLocal extends LitElement { @@ -138,6 +139,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { } if (this._localTts.length && this._localStt.length) { this._pickOrCreatePipelineExists(); + return; } if (!isComponentLoaded(this.hass, "hassio")) { this._state = "NOT_SUPPORTED"; @@ -148,38 +150,30 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { const { addons } = await fetchHassioAddonsInfo(this.hass); const whisper = addons.find((addon) => addon.slug === "core_whisper"); const piper = addons.find((addon) => addon.slug === "core_piper"); - if (piper && !this._localTts.length) { - if (piper.state !== "started") { + if (!this._localTts.length) { + if (!piper) { + this._detailState = "Installing Piper add-on"; + await installHassioAddon(this.hass, "core_piper"); + } + if (!piper || piper.state !== "started") { this._detailState = "Starting Piper add-on"; await startHassioAddon(this.hass, "core_piper"); } this._detailState = "Setting up Piper"; await this._setupConfigEntry("piper"); } - if (whisper && !this._localStt.length) { - if (whisper.state !== "started") { + if (!this._localStt.length) { + if (!whisper) { + this._detailState = "Installing Whisper add-on"; + await installHassioAddon(this.hass, "core_whisper"); + } + if (!whisper || whisper.state !== "started") { this._detailState = "Starting Whisper add-on"; await startHassioAddon(this.hass, "core_whisper"); } this._detailState = "Setting up Whisper"; await this._setupConfigEntry("whisper"); } - if (!piper) { - this._detailState = "Installing Piper add-on"; - await installHassioAddon(this.hass, "core_piper"); - this._detailState = "Starting Piper add-on"; - await startHassioAddon(this.hass, "core_piper"); - this._detailState = "Setting up Piper"; - await this._setupConfigEntry("piper"); - } - if (!whisper) { - this._detailState = "Installing Whisper add-on"; - await installHassioAddon(this.hass, "core_whisper"); - this._detailState = "Starting Whisper add-on"; - await startHassioAddon(this.hass, "core_whisper"); - this._detailState = "Setting up Whisper"; - await this._setupConfigEntry("whisper"); - } this._detailState = "Creating assistant"; await this._findEntitiesAndCreatePipeline(); } catch (e: any) { @@ -259,6 +253,9 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { this._localTts[0].entity_id, this._localStt[0].entity_id ); + + // wait a render so the `hui-select-entity-row` is also updated and doesn't undo the select action + await nextRender(); } await this.hass.callService( @@ -340,6 +337,9 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { this._localStt[0].entity_id ); + // wait a render so the `hui-select-entity-row` is also updated and doesn't undo the select action + await nextRender(); + await this.hass.callService( "select", "select_option", diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts index d008d312f0..781802d348 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts @@ -17,6 +17,7 @@ import type { HomeAssistant } from "../../types"; import { AssistantSetupStyles } from "./styles"; import { STEP } from "./voice-assistant-setup-dialog"; import { documentationUrl } from "../../util/documentation-url"; +import { nextRender } from "../../common/util/render-status"; @customElement("ha-voice-assistant-setup-step-pipeline") export class HaVoiceAssistantSetupStepPipeline extends LitElement { @@ -240,6 +241,9 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { wake_word_entity: null, wake_word_id: null, }); + + // wait a render so the `hui-select-entity-row` is also updated and doesn't undo the select action + await nextRender(); } await this.hass.callService( From ac9085c7a4a328e0410675d5d07a9b028903ccb3 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:05:05 +0100 Subject: [PATCH 17/49] Fix ha-target-picker hideTitle (#23116) --- src/components/ha-target-picker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts index 460e8740da..f7ba2fd5ca 100644 --- a/src/components/ha-target-picker.ts +++ b/src/components/ha-target-picker.ts @@ -345,7 +345,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { "ui.components.target-picker.expand" )} .path=${mdiUnfoldMoreVertical} - hideTooltip + hideTitle .id=${id} .type=${type} @click=${this._handleExpand} @@ -361,7 +361,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { class="mdc-chip__icon mdc-chip__icon--trailing" .label=${this.hass.localize("ui.components.target-picker.remove")} .path=${mdiClose} - hideTooltip + hideTitle .id=${id} .type=${type} @click=${this._handleRemove} From 13c17632774001ade1b6375ee9c9a1f4b467e032 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Dec 2024 14:43:51 +0100 Subject: [PATCH 18/49] Bumped version to 20241127.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1803d94af1..74cb9fd55e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.2" +version = "20241127.3" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 5b7eeb6ac16a25c964ba228730c64c305910e32e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:58:59 +0100 Subject: [PATCH 19/49] Update dependency marked to v15.0.3 (#23088) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 920479bacc..7a3360c785 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "lit": "2.8.0", "lit-html": "2.8.0", "luxon": "3.5.0", - "marked": "15.0.2", + "marked": "15.0.3", "memoize-one": "6.0.0", "node-vibrant": "3.2.1-alpha.1", "proxy-polyfill": "0.3.2", diff --git a/yarn.lock b/yarn.lock index a973b9522c..b361b2778c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9646,7 +9646,7 @@ __metadata: luxon: "npm:3.5.0" magic-string: "npm:0.30.13" map-stream: "npm:0.0.7" - marked: "npm:15.0.2" + marked: "npm:15.0.3" memoize-one: "npm:6.0.0" node-vibrant: "npm:3.2.1-alpha.1" object-hash: "npm:3.0.0" @@ -11371,12 +11371,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:15.0.2": - version: 15.0.2 - resolution: "marked@npm:15.0.2" +"marked@npm:15.0.3": + version: 15.0.3 + resolution: "marked@npm:15.0.3" bin: marked: bin/marked.js - checksum: 10/dde3d918bb3938906d7a874b990cbfb193514a5fef4b41bca102db3b63d50235afb517dd70429525b6e0a5cff43abd827e7bd2b18617f8bd4776db745e75e973 + checksum: 10/821c1af0e527b3f6f4684a68802a0fa5e91cd2b0887d0600ce9035896f8b0fbad180a3f8c5be91de25bdc20323abbd674351832c40d48820b6f0d888fd77cd59 languageName: node linkType: hard From f8d6f0fae4c3b5ff2b005f6b10097949735a4513 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Wed, 4 Dec 2024 09:52:48 +0100 Subject: [PATCH 20/49] Show in assist pipeline debug when intent is preferred and processed locally (#23115) --- src/data/assist_pipeline.ts | 2 ++ .../debug/assist-render-pipeline-run.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/data/assist_pipeline.ts b/src/data/assist_pipeline.ts index c0c8b4b788..0623049a0f 100644 --- a/src/data/assist_pipeline.ts +++ b/src/data/assist_pipeline.ts @@ -104,12 +104,14 @@ interface PipelineIntentStartEvent extends PipelineEventBase { data: { engine: string; language: string; + prefer_local_intents: boolean; intent_input: string; }; } interface PipelineIntentEndEvent extends PipelineEventBase { type: "intent-end"; data: { + processed_locally: boolean; intent_output: ConversationResult; }; } diff --git a/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts b/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts index b3c2a86296..9d3bbf81ea 100644 --- a/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts +++ b/src/panels/config/voice-assistants/debug/assist-render-pipeline-run.ts @@ -307,6 +307,18 @@ export class AssistPipelineDebug extends LitElement { ` : ""}` : ""} +

+
Prefer handling locally
+
+ ${this.pipelineRun.intent.prefer_local_intents} +
+
+
+
Processed locally
+
+ ${this.pipelineRun.intent.processed_locally} +
+
${dataMinusKeysRender( this.pipelineRun.intent, INTENT_DATA From 5f6577a24ce8cd04770ca1ad4d56ad3c6d20f6ea Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:15:59 +0100 Subject: [PATCH 21/49] Remove static font from ha-badge (#23120) --- src/components/ha-badge.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ha-badge.ts b/src/components/ha-badge.ts index 8b2160f24e..46ab2a0dd1 100644 --- a/src/components/ha-badge.ts +++ b/src/components/ha-badge.ts @@ -98,7 +98,6 @@ export class HaBadge extends LitElement { align-items: flex-start; padding-inline-start: initial; text-align: center; - font-family: Roboto; } .label { font-size: 10px; From 257cab1061e0bb206090cee64af0ca03c689ddc3 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 3 Dec 2024 16:16:52 +0100 Subject: [PATCH 22/49] Fix create section on iOS (#23123) --- src/panels/lovelace/views/hui-sections-view.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index 944e3a1632..a5eea8f38c 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -246,7 +246,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
From 6c9cfed49fae5a0414c860ffbb4ca0e20c79d170 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Wed, 4 Dec 2024 07:52:53 +0100 Subject: [PATCH 23/49] Explicitly set file name for camera snapshot (#23124) * Explicitly set file name for camera snapshot * Process code review --- src/dialogs/more-info/controls/more-info-camera.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index 7b339db519..0a9a6e6339 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -8,6 +8,7 @@ import "../../../components/buttons/ha-progress-button"; import { UNAVAILABLE } from "../../../data/entity"; import { fileDownload } from "../../../util/file_download"; import { showToast } from "../../../util/toast"; +import { slugify } from "../../../common/string/slugify"; class MoreInfoCamera extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -69,9 +70,14 @@ class MoreInfoCamera extends LitElement { throw new Error("No response from API"); } + const contentType = result.headers.get("content-type"); + const ext = contentType === "image/png" ? "png" : "jpg"; + const date = new Date().toLocaleString(); + const filename = `snapshot_${slugify(this.stateObj!.entity_id)}_${date}.${ext}`; + const blob = await result.blob(); const url = window.URL.createObjectURL(blob); - fileDownload(url); + fileDownload(url, filename); } catch (err) { this._waiting = false; button.actionError(); From 3a2cb51f8d24d1348785b7bf471643783d5a2094 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 4 Dec 2024 09:40:41 +0100 Subject: [PATCH 24/49] Reduce button target zone in media player more info (#23130) --- src/dialogs/more-info/controls/more-info-media_player.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts index 694b190d04..93828334d4 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.ts +++ b/src/dialogs/more-info/controls/more-info-media_player.ts @@ -207,12 +207,17 @@ class MoreInfoMediaPlayer extends LitElement { static get styles(): CSSResultGroup { return css` - ha-icon-button[action="turn_off"], - ha-icon-button[action="turn_on"], ha-slider { flex-grow: 1; } + ha-icon-button[action="turn_off"], + ha-icon-button[action="turn_on"] { + margin-inline-end: auto; + margin-right: auto; + margin-left: inherit; + } + .controls { display: flex; flex-wrap: wrap; From 8f4e65d392a80a922eea79b6a395e14bbe7ec1dc Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 4 Dec 2024 13:17:47 +0100 Subject: [PATCH 25/49] Don't use duration formatting for second unit (#23132) Don't use duration formatting for sec unit --- src/common/datetime/format_duration.ts | 19 +------------------ test/common/datetime/format_duration.test.ts | 5 ----- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/common/datetime/format_duration.ts b/src/common/datetime/format_duration.ts index e20f7e18af..c2c0c11d55 100644 --- a/src/common/datetime/format_duration.ts +++ b/src/common/datetime/format_duration.ts @@ -71,7 +71,7 @@ export const formatDurationDigital = ( duration: HaDurationData ) => formatDigitalDurationMem(locale).format(duration); -export const DURATION_UNITS = ["s", "min", "h", "d"] as const; +export const DURATION_UNITS = ["min", "h", "d"] as const; type DurationUnit = (typeof DURATION_UNITS)[number]; @@ -99,14 +99,6 @@ const formatDurationMinuteMem = memoizeOne( }) ); -const formatDurationSecondMem = memoizeOne( - (locale: FrontendLocaleData) => - new Intl.DurationFormat(locale.language, { - style: "narrow", - secondsDisplay: "always", - }) -); - export const formatDuration = ( locale: FrontendLocaleData, duration: string, @@ -146,15 +138,6 @@ export const formatDuration = ( }; return formatDurationMinuteMem(locale).format(input); } - case "s": { - const seconds = Math.floor(value); - const milliseconds = Math.floor((value - seconds) * 1000); - const input: DurationInput = { - seconds, - milliseconds, - }; - return formatDurationSecondMem(locale).format(input); - } default: throw new Error("Invalid duration unit"); } diff --git a/test/common/datetime/format_duration.test.ts b/test/common/datetime/format_duration.test.ts index cd62785320..c0fc46af4b 100644 --- a/test/common/datetime/format_duration.test.ts +++ b/test/common/datetime/format_duration.test.ts @@ -21,11 +21,6 @@ const LOCALE: FrontendLocaleData = { describe("formatDuration", () => { it("works", () => { - assert.strictEqual(formatDuration(LOCALE, "0.5", "s"), "0s 500ms"); - assert.strictEqual(formatDuration(LOCALE, "1", "s"), "1s"); - assert.strictEqual(formatDuration(LOCALE, "1.1", "s"), "1s 100ms"); - assert.strictEqual(formatDuration(LOCALE, "65", "s"), "65s"); - assert.strictEqual(formatDuration(LOCALE, "0.25", "min"), "0m 15s"); assert.strictEqual(formatDuration(LOCALE, "0.5", "min"), "0m 30s"); assert.strictEqual(formatDuration(LOCALE, "1", "min"), "1m"); From fbff95345c2caace85f85387ca02cac47e3130f9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 4 Dec 2024 13:57:55 +0100 Subject: [PATCH 26/49] Use action instead of selected for select entity row (#23135) --- .../voice-assistant-setup-step-local.ts | 7 ------- .../voice-assistant-setup-step-pipeline.ts | 4 ---- src/panels/lovelace/entity-rows/hui-select-entity-row.ts | 6 ++++-- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts index cd00187d5a..1926f83e1e 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts @@ -24,7 +24,6 @@ import type { HomeAssistant } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; import { AssistantSetupStyles } from "./styles"; import { STEP } from "./voice-assistant-setup-dialog"; -import { nextRender } from "../../common/util/render-status"; @customElement("ha-voice-assistant-setup-step-local") export class HaVoiceAssistantSetupStepLocal extends LitElement { @@ -253,9 +252,6 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { this._localTts[0].entity_id, this._localStt[0].entity_id ); - - // wait a render so the `hui-select-entity-row` is also updated and doesn't undo the select action - await nextRender(); } await this.hass.callService( @@ -337,9 +333,6 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { this._localStt[0].entity_id ); - // wait a render so the `hui-select-entity-row` is also updated and doesn't undo the select action - await nextRender(); - await this.hass.callService( "select", "select_option", diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts index 781802d348..d008d312f0 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts @@ -17,7 +17,6 @@ import type { HomeAssistant } from "../../types"; import { AssistantSetupStyles } from "./styles"; import { STEP } from "./voice-assistant-setup-dialog"; import { documentationUrl } from "../../util/documentation-url"; -import { nextRender } from "../../common/util/render-status"; @customElement("ha-voice-assistant-setup-step-pipeline") export class HaVoiceAssistantSetupStepPipeline extends LitElement { @@ -241,9 +240,6 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { wake_word_entity: null, wake_word_id: null, }); - - // wait a render so the `hui-select-entity-row` is also updated and doesn't undo the select action - await nextRender(); } await this.hass.callService( diff --git a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts index 55290d7210..cf6cfb73e8 100644 --- a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts @@ -63,7 +63,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { .value=${stateObj.state} .disabled=${stateObj.state === UNAVAILABLE} naturalMenuWidth - @selected=${this._selectedChanged} + @action=${this._handleAction} @click=${stopPropagation} @closed=${stopPropagation} > @@ -94,11 +94,13 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { `; } - private _selectedChanged(ev): void { + private _handleAction(ev): void { const stateObj = this.hass!.states[ this._config!.entity ] as InputSelectEntity; + const option = ev.target.value; + if ( option === stateObj.state || !stateObj.attributes.options.includes(option) From a9c25b49b90ec1f279552e65c477aa578e9456e9 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:55:12 +0100 Subject: [PATCH 27/49] Add red delete button to delete zone confirmation dialog (#23136) --- src/panels/config/zone/ha-config-zone.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/zone/ha-config-zone.ts b/src/panels/config/zone/ha-config-zone.ts index 25306721af..d0a8a36580 100644 --- a/src/panels/config/zone/ha-config-zone.ts +++ b/src/panels/config/zone/ha-config-zone.ts @@ -509,6 +509,7 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { title: this.hass!.localize("ui.panel.config.zone.confirm_delete"), dismissText: this.hass!.localize("ui.common.cancel"), confirmText: this.hass!.localize("ui.common.delete"), + destructive: true, })) ) { return false; From dbda1d75f9aa08902c6a0a389e8c85d93ec4cb8c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 4 Dec 2024 14:12:49 +0100 Subject: [PATCH 28/49] Bumped version to 20241127.4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 74cb9fd55e..e0d234d749 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.3" +version = "20241127.4" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 6d2e7f9fbd926c3c9387987b0893803eb404d61d Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 5 Dec 2024 17:01:04 +0100 Subject: [PATCH 29/49] Improve trigger and action description for conversation (#23141) --- src/data/automation_i18n.ts | 20 ++++++++++++++------ src/data/script_i18n.ts | 5 +++++ src/translations/en.json | 6 ++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index 94c2946346..616659e682 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -682,19 +682,27 @@ const tryDescribeTrigger = ( // Conversation Trigger if (trigger.trigger === "conversation") { - if (!trigger.command) { + if (!trigger.command || !trigger.command.length) { return hass.localize( `${triggerTranslationBaseKey}.conversation.description.empty` ); } + const commands = ensureArray(trigger.command); + + if (commands.length === 1) { + return hass.localize( + `${triggerTranslationBaseKey}.conversation.description.single`, + { + sentence: commands[0], + } + ); + } return hass.localize( - `${triggerTranslationBaseKey}.conversation.description.full`, + `${triggerTranslationBaseKey}.conversation.description.multiple`, { - sentence: formatListWithOrs( - hass.locale, - ensureArray(trigger.command).map((cmd) => `'${cmd}'`) - ), + sentence: commands[0], + count: commands.length - 1, } ); } diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index 81cd488236..a459d23ff4 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -486,6 +486,11 @@ const tryDescribeAction = ( if (actionType === "set_conversation_response") { const config = action as SetConversationResponseAction; + if (isTemplate(config.set_conversation_response)) { + return hass.localize( + `${actionTranslationBaseKey}.set_conversation_response.description.template` + ); + } return hass.localize( `${actionTranslationBaseKey}.set_conversation_response.description.full`, { response: config.set_conversation_response } diff --git a/src/translations/en.json b/src/translations/en.json index bdb721c507..ec56a5416a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3124,7 +3124,8 @@ "description": { "picker": "When Assist matches a sentence from a voice assistant.", "empty": "When a sentence is said", - "full": "When the sentence {sentence} is said" + "single": "When the sentence ''{sentence}'' is said", + "multiple": "When the sentence ''{sentence}'' or {count, plural,\n one {another}\n other {{count} others}\n} are said" } }, "tag": { @@ -3598,7 +3599,8 @@ "label": "Set conversation response", "description": { "picker": "Set response of conversation if automation was triggered by conversation trigger.", - "full": "Set response of conversation to {response}" + "template": "Set response of conversation to a template", + "full": "Set response of conversation to ''{response}''" } }, "unknown": { From cd8900dd26439f4b34a986feb66260223e0d115c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 5 Dec 2024 10:14:06 +0100 Subject: [PATCH 30/49] Voice addon install: try to find discovered flow (#23146) --- .../voice-assistant-setup-step-local.ts | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts index 1926f83e1e..b0279b6098 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts @@ -11,7 +11,11 @@ import { listAssistPipelines, } from "../../data/assist_pipeline"; import type { AssistSatelliteConfiguration } from "../../data/assist_satellite"; -import { createConfigFlow, handleConfigFlowStep } from "../../data/config_flow"; +import { + createConfigFlow, + fetchConfigFlowInProgress, + handleConfigFlowStep, +} from "../../data/config_flow"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry"; import { fetchHassioAddonsInfo, @@ -194,6 +198,35 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { } private async _setupConfigEntry(addon: string) { + const configFlow = await this._findConfigFlowInProgress(addon); + + if (configFlow) { + const step = await handleConfigFlowStep( + this.hass, + configFlow.flow_id, + {} + ); + if (step.type === "create_entry") { + return undefined; + } + } + + return this._createConfigEntry(addon); + } + + private async _findConfigFlowInProgress(addon: string) { + const configFlows = await fetchConfigFlowInProgress(this.hass.connection); + + return configFlows.find( + (flow) => + flow.handler === "wyoming" && + flow.context.source === "hassio" && + (flow.context.configuration_url.includes(`core_${addon}`) || + flow.context.title_placeholders.title.toLowerCase().includes(addon)) + ); + } + + private async _createConfigEntry(addon: string) { const configFlow = await createConfigFlow(this.hass, "wyoming"); const step = await handleConfigFlowStep(this.hass, configFlow.flow_id, { host: `core-${addon}`, From 8ca52820b1444bd4bc9909baf2d3a964e955c9d7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:02:55 -0800 Subject: [PATCH 31/49] Missing horiz swing mode import (#23168) --- .../lovelace/create-element/create-card-feature-element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/lovelace/create-element/create-card-feature-element.ts b/src/panels/lovelace/create-element/create-card-feature-element.ts index 04f0efa876..872ceb0aba 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -1,6 +1,7 @@ import "../card-features/hui-alarm-modes-card-feature"; import "../card-features/hui-climate-fan-modes-card-feature"; import "../card-features/hui-climate-swing-modes-card-feature"; +import "../card-features/hui-climate-swing-horizontal-modes-card-feature"; import "../card-features/hui-climate-hvac-modes-card-feature"; import "../card-features/hui-climate-preset-modes-card-feature"; import "../card-features/hui-cover-open-close-card-feature"; From 287a068ada8080dbb4553978efd8897172ca4928 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 6 Dec 2024 08:11:17 +0100 Subject: [PATCH 32/49] Add localisation to voice wizard (#23169) * Add localization to voice wizard * more --- .../cloud/cloud-step-intro.ts | 19 +++- .../cloud/cloud-step-signin.ts | 6 +- .../cloud/cloud-step-signup.ts | 24 +++-- .../voice-assistant-setup-dialog.ts | 8 +- .../voice-assistant-setup-step-area.ts | 21 ++++- ...e-assistant-setup-step-change-wake-word.ts | 19 ++-- .../voice-assistant-setup-step-check.ts | 35 +++++-- .../voice-assistant-setup-step-local.ts | 91 ++++++++++++++----- .../voice-assistant-setup-step-pipeline.ts | 70 +++++++++++--- .../voice-assistant-setup-step-success.ts | 11 ++- .../voice-assistant-setup-step-update.ts | 13 ++- .../voice-assistant-setup-step-wake-word.ts | 39 ++++++-- src/translations/en.json | 83 +++++++++++++++++ 13 files changed, 346 insertions(+), 93 deletions(-) diff --git a/src/dialogs/voice-assistant-setup/cloud/cloud-step-intro.ts b/src/dialogs/voice-assistant-setup/cloud/cloud-step-intro.ts index cc00cf7c81..c2edcf4949 100644 --- a/src/dialogs/voice-assistant-setup/cloud/cloud-step-intro.ts +++ b/src/dialogs/voice-assistant-setup/cloud/cloud-step-intro.ts @@ -18,7 +18,11 @@ export class CloudStepIntro extends LitElement { src=${`/static/images/logo_nabu_casa${this.hass.themes?.darkMode ? "_dark" : ""}.png`} alt="Nabu Casa logo" /> -

The power of Home Assistant Cloud

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.cloud.title" + )} +

@@ -45,12 +49,15 @@ export class CloudStepIntro extends LitElement {

- Remote access + ${this.hass.localize( + "ui.panel.config.voice_assistants.assistants.cloud.features.remote_access.title" + )}

- Secure remote access to your system while supporting the - development of Home Assistant. + ${this.hass.localize( + "ui.panel.config.voice_assistants.assistants.cloud.features.remote_access.text" + )}

@@ -101,7 +108,9 @@ export class CloudStepIntro extends LitElement { Try 1 month for free${this.hass.localize( + "ui.panel.config.cloud.register.headline" + )}
`; } diff --git a/src/dialogs/voice-assistant-setup/cloud/cloud-step-signin.ts b/src/dialogs/voice-assistant-setup/cloud/cloud-step-signin.ts index 4d17b25693..d4c3663682 100644 --- a/src/dialogs/voice-assistant-setup/cloud/cloud-step-signin.ts +++ b/src/dialogs/voice-assistant-setup/cloud/cloud-step-signin.ts @@ -32,7 +32,7 @@ export class CloudStepSignin extends LitElement { src=${`/static/images/logo_nabu_casa${this.hass.themes?.darkMode ? "_dark" : ""}.png`} alt="Nabu Casa logo" /> -

Sign in

+

${this.hass.localize("ui.panel.config.cloud.login.sign_in")}

${this._error ? html`${this._error}` : ""} @@ -73,7 +73,9 @@ export class CloudStepSignin extends LitElement { unelevated @click=${this._handleLogin} .disabled=${this._requestInProgress} - >Sign in${this.hass.localize( + "ui.panel.config.cloud.login.sign_in" + )}
`; } diff --git a/src/dialogs/voice-assistant-setup/cloud/cloud-step-signup.ts b/src/dialogs/voice-assistant-setup/cloud/cloud-step-signup.ts index 0ec6ef0a69..3e8437fe2f 100644 --- a/src/dialogs/voice-assistant-setup/cloud/cloud-step-signup.ts +++ b/src/dialogs/voice-assistant-setup/cloud/cloud-step-signup.ts @@ -40,14 +40,18 @@ export class CloudStepSignup extends LitElement { src=${`/static/images/logo_nabu_casa${this.hass.themes?.darkMode ? "_dark" : ""}.png`} alt="Nabu Casa logo" /> -

Sign up

+

+ ${this.hass.localize("ui.panel.config.cloud.register.create_account")} +

${this._error ? html`${this._error}` : ""} ${this._state === "VERIFY" ? html`

- Check the email we just sent to ${this._email} and click the - confirmation link to continue. + ${this.hass.localize( + "ui.panel.config.cloud.register.confirm_email", + { email: this._email } + )}

` : html`Send the email again${this.hass.localize( + "ui.panel.config.cloud.register.resend_confirm_email" + )}I clicked the link${this.hass.localize( + "ui.panel.config.cloud.register.clicked_confirm" + )}` : html`Sign in${this.hass.localize( + "ui.panel.config.cloud.login.sign_in" + )} Next${this.hass.localize("ui.common.next")}`} `; } 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 66c0f4ad1f..eadf4a901a 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts @@ -145,7 +145,9 @@ export class HaVoiceAssistantSetupDialog extends LitElement { @click=${this._goToNextStep} class="skip-btn" slot="actionItems" - >Skip${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.skip" + )}` : nothing} @@ -164,7 +166,9 @@ export class HaVoiceAssistantSetupDialog extends LitElement { )} >` : assistEntityState?.state === UNAVAILABLE - ? html`Your voice assistant is not available.` + ? this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.not_available" + ) : this._step === STEP.CHECK ? html` -

Select area

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.area.title" + )} +

- When you voice assistant knows where it is, it can better control the - devices around it. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.area.secondary" + )}

`; } private async _setArea() { const area = this.shadowRoot!.querySelector("ha-area-picker")!.value; if (!area) { - showAlertDialog(this, { text: "Please select an area" }); + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.area.no_selection" + ), + }); return; } await updateDeviceRegistryEntry(this.hass, this.deviceId, { diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-change-wake-word.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-change-wake-word.ts index c5894c7651..645d2ce8de 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-change-wake-word.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-change-wake-word.ts @@ -1,14 +1,13 @@ import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-md-list"; +import "../../components/ha-md-list-item"; import type { AssistSatelliteConfiguration } from "../../data/assist_satellite"; import { setWakeWords } from "../../data/assist_satellite"; import type { HomeAssistant } from "../../types"; -import { STEP } from "./voice-assistant-setup-dialog"; import { AssistantSetupStyles } from "./styles"; -import "../../components/ha-md-list"; -import "../../components/ha-md-list-item"; -import { formatLanguageCode } from "../../common/language/format_language"; +import { STEP } from "./voice-assistant-setup-dialog"; @customElement("ha-voice-assistant-setup-step-change-wake-word") export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement { @@ -25,11 +24,15 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement { src="/static/images/voice-assistant/change-wake-word.png" alt="Casita Home Assistant logo" /> -

Change wake word

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.change_wake_word.title" + )} +

- Some wake words are better for - ${formatLanguageCode(this.hass.locale.language, this.hass.locale)} and - voice than others. Please try them out. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.change_wake_word.secondary" + )}

diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts index eba258fa6a..31d8032dc2 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-check.ts @@ -40,11 +40,15 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement { src="/static/images/voice-assistant/error.png" alt="Casita Home Assistant error logo" /> -

The voice assistant is unable to connect to Home Assistant

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.check.failed_title" + )} +

- To play audio, the voice assistant device has to connect to Home - Assistant to fetch the files. Our test shows that the device is - unable to reach the Home Assistant server. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.check.failed_secondary" + )}

` : html`Casita Home Assistant hi logo -

Hi

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.check.title" + )} +

- Over the next couple steps we're going to personalize your voice - assistant. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.check.secondary" + )}

${this._showLoader diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts index b0279b6098..e4ff17d33f 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts @@ -54,9 +54,15 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { src="/static/images/voice-assistant/update.png" alt="Casita Home Assistant loading logo" /> -

Installing add-ons

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.title" + )} +

- The Whisper and Piper add-ons are being installed and configured. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.secondary" + )}

@@ -67,13 +73,20 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { src="/static/images/voice-assistant/error.png" alt="Casita Home Assistant error logo" /> -

Failed to install add-ons

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.failed_title" + )} +

${this._error}

- We could not automatically install a local TTS and STT provider - for you. Read the documentation to learn how to install them. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.failed_secondary" + )}

- Go back + ${this.hass.localize("ui.common.back")} - Learn more ` : this._state === "NOT_SUPPORTED" @@ -92,13 +107,19 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { src="/static/images/voice-assistant/error.png" alt="Casita Home Assistant error logo" /> -

Installation of add-ons is not supported on your system

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.not_supported_title" + )} +

- Your system is not supported to automatically install a local - TTS and STT provider. Learn how to set up local TTS and STT - providers in the documentation. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.not_supported_secondary" + )}

- Go back + ${this.hass.localize("ui.common.back")} - Learn more ` : nothing} @@ -155,29 +178,43 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { const piper = addons.find((addon) => addon.slug === "core_piper"); if (!this._localTts.length) { if (!piper) { - this._detailState = "Installing Piper add-on"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.installing_piper" + ); await installHassioAddon(this.hass, "core_piper"); } if (!piper || piper.state !== "started") { - this._detailState = "Starting Piper add-on"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.starting_piper" + ); await startHassioAddon(this.hass, "core_piper"); } - this._detailState = "Setting up Piper"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.setup_piper" + ); await this._setupConfigEntry("piper"); } if (!this._localStt.length) { if (!whisper) { - this._detailState = "Installing Whisper add-on"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.installing_whisper" + ); await installHassioAddon(this.hass, "core_whisper"); } if (!whisper || whisper.state !== "started") { - this._detailState = "Starting Whisper add-on"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.starting_whisper" + ); await startHassioAddon(this.hass, "core_whisper"); } - this._detailState = "Setting up Whisper"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.setup_whisper" + ); await this._setupConfigEntry("whisper"); } - this._detailState = "Creating assistant"; + this._detailState = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.state.creating_pipeline" + ); await this._findEntitiesAndCreatePipeline(); } catch (e: any) { this._state = "ERROR"; @@ -234,7 +271,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { }); if (step.type !== "create_entry") { throw new Error( - `Failed to create entry for ${addon}${"errors" in step ? `: ${step.errors.base}` : ""}` + `${this.hass.localize("ui.panel.config.voice_assistants.satellite_wizard.local.errors.failed_create_entry", { addon })}${"errors" in step ? `: ${step.errors.base}` : ""}` ); } } @@ -322,7 +359,9 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { ) ).providers.find((provider) => provider.engine_id === sttEntityId); - let pipelineName = "Local Assistant"; + let pipelineName = this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.local_pipeline" + ); let i = 1; while ( pipelines.pipelines.find( @@ -330,7 +369,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { (pipeline) => pipeline.name === pipelineName ) ) { - pipelineName = `Local Assistant ${i}`; + pipelineName = `${this.hass.localize("ui.panel.config.voice_assistants.satellite_wizard.local.local_pipeline")} ${i}`; i++; } @@ -353,7 +392,11 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { this._findLocalEntities(); if (!this._localTts?.length || !this._localStt?.length) { if (tryNo > 3) { - throw new Error("Could not find local TTS and STT entities"); + throw new Error( + this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.local.errors.could_not_find_entities" + ) + ); } await new Promise((resolve) => { setTimeout(resolve, 2000); diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts index d008d312f0..e75e4260cf 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts @@ -14,9 +14,9 @@ import { fetchCloudStatus } from "../../data/cloud"; import { listSTTEngines } from "../../data/stt"; import { listTTSEngines, listTTSVoices } from "../../data/tts"; import type { HomeAssistant } from "../../types"; +import { documentationUrl } from "../../util/documentation-url"; import { AssistantSetupStyles } from "./styles"; import { STEP } from "./voice-assistant-setup-dialog"; -import { documentationUrl } from "../../util/documentation-url"; @customElement("ha-voice-assistant-setup-step-pipeline") export class HaVoiceAssistantSetupStepPipeline extends LitElement { @@ -69,10 +69,15 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { } return html`
-

What hardware do you want to use?

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.title" + )} +

- How quickly your assistant responds depends on the power of the - hardware. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.secondary" + )}

@@ -80,7 +85,12 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { ${!this._showFirst ? "…" : "Turn on the lights in the bedroom"}
${this._showFirst - ? html`
0.2 seconds
` + ? html`
+ 0.2 + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.seconds" + )} +
` : nothing} ${this._showFirst ? html`
@@ -88,12 +98,23 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
` : nothing} ${this._showSecond - ? html`
0.4 seconds
` + ? html`
+ 0.4 + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.seconds" + )} +
` : nothing}

Home Assistant Cloud

-

Ideal if you don't have a powerful system at home.

- Learn more +

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.cloud.description" + )} +

+ ${this.hass.localize("ui.panel.config.common.learn_more")}
@@ -101,7 +122,12 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { ${!this._showThird ? "…" : "Turn on the lights in the bedroom"}
${this._showThird - ? html`
3 seconds
` + ? html`
+ 3 + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.seconds" + )} +
` : nothing} ${this._showThird ? html`
@@ -109,13 +135,23 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
` : nothing} ${this._showFourth - ? html`
5 seconds
` + ? html`
+ 5 + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.seconds" + )} +
` : nothing}
-

Do-it-yourself

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.local.title" + )} +

- Install add-ons or containers to run it on your own system. Powerful - hardware is needed for fast responses. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.local.description" + )}

- Learn more Setup with add-ons${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.pipeline.local.setup" + )}
diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts index 4b8e88fcb4..27e0a188d8 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-success.ts @@ -72,10 +72,15 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement { src="/static/images/voice-assistant/heart.png" alt="Casita Home Assistant logo" /> -

Ready to Assist!

+

+ ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.success.title" + )} +

- Make any final customizations here. You can always change these in the - Voice Assistants section of the settings page. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.success.secondary" + )}

${this.assistConfiguration && diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-update.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-update.ts index cba6ebec50..4c61291f81 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-update.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-update.ts @@ -72,12 +72,17 @@ export class HaVoiceAssistantSetupStepUpdate extends LitElement {

${stateObj && (stateObj.state === "unavailable" || updateIsInstalling(stateObj)) - ? "Updating your voice assistant" - : "Checking for updates"} + ? this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.update.title" + ) + : this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.update.checking" + )}

- We are making sure you have the latest and greatest version of your - voice assistant. This may take a few minutes. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.update.secondary" + )}

- Say “${this._activeWakeWord(this.assistConfiguration)}” to wake the - device up + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.wake_word.title", + { wakeword: this._activeWakeWord(this.assistConfiguration) } + )}

-

Setup will continue once the device is awake.

+

${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.wake_word.secondary" + )}

` : html`Casita Home Assistant logo

- Say “${this._activeWakeWord(this.assistConfiguration)}” again + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.wake_word.title_2", + { wakeword: this._activeWakeWord(this.assistConfiguration) } + )}

- To make sure the wake word works for you. + ${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.wake_word.secondary_2" + )}

`} ${this._timedout ? html`We have not heard the wake word, is your device muted?${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.wake_word.time_out" + )}` : this._muteSwitchEntity && this.hass.states[this._muteSwitchEntity].state === "on" - ? html`Please unmute your device to continue.${this.hass.localize( + "ui.panel.config.voice_assistants.satellite_wizard.wake_word.muted_description" + )}` : nothing} `; } diff --git a/src/translations/en.json b/src/translations/en.json index ec56a5416a..5676049328 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2771,6 +2771,10 @@ "speech": { "title": "Amazing speech options for Assist", "text": "Bring personality to your home by having it speak to you using our neural-network powered speech-to-text and text-to-speech services." + }, + "remote_access": { + "title": "Remote access", + "text": " Secure remote access to your system while supporting the development of Home Assistant." } }, "and_more": "And more", @@ -2824,6 +2828,83 @@ "expose_to": "to {assistants}", "expose_entities": "Expose {count} {count, plural,\n one {entity}\n other {entities}\n}" } + }, + "satellite_wizard": { + "skip": "Skip", + "not_available": "Your voice assistant is not available.", + "update": { + "title": "Updating", + "checking": "Checking for updates", + "secondary": "We are making sure you have the latest updates. This may take a few minutes." + }, + "check": { + "title": "Hello!", + "secondary": "Over the next couple steps we're going to personalize your voice assistant.", + "failed_title": "The voice assistant is unable to connect to Home Assistant", + "failed_secondary": "To play audio, the voice assistant device has to connect to Home Assistant to fetch the files. Our test shows that the device is unable to reach the Home Assistant server.", + "help": "Help me", + "retry": "Retry" + }, + "wake_word": { + "title": "Say ''{wakeword}'' to wake the device up", + "secondary": "Setup will continue once the device is awake.", + "title_2": "Say ''{wakeword}'' again", + "secondary_2": "To make sure the wake word works for you.", + "change_wake_word": "Change wake word", + "time_out": "We have not heard the wake word, is your device muted?", + "muted": "Your device is muted", + "muted_description": "Please unmute your device to continue." + }, + "change_wake_word": { + "title": "Change wake word", + "secondary": "These are the phrases you can use to wake your device and have it start listening for commands." + }, + "area": { + "title": "Select area", + "secondary": "When your voice assistant knows where it is, it can better control the devices around it. This allows you to control devices in the room with short commands, like 'turn on the lights'", + "no_selection": "Please select an area" + }, + "pipeline": { + "title": "What hardware do you want to use?", + "secondary": "How quickly your assistant responds depends on the power of the hardware.", + "seconds": "seconds", + "cloud": { + "description": "Ideal if you don't have a powerful system at home." + }, + "local": { "title": "", "description": "", "setup": "" } + }, + "cloud": { + "title": "The power of Home Assistant Cloud", + "register": { + "confirm_email": "Confirm email" + } + }, + "local": { + "title": "Installing add-ons", + "secondary": "The Whisper and Piper add-ons are being installed and configured based on your hardware.", + "failed_title": "Failed to install add-ons", + "failed_secondary": "We were unable to install the Whisper and Piper add-ons automatically for you. Read the documentation to learn how to install them.", + "not_supported_title": "Installation of add-ons is not supported on your system", + "not_supported_secondary": "Your system is not supported to automatically install a local TTS and STT provider. Learn how to set up local TTS and STT providers in the documentation.", + "local_pipeline": "Local Assistant", + "state": { + "installing_piper": "Installing Piper add-on", + "starting_piper": "Starting Piper add-on", + "setup_piper": "Setting up Piper", + "installing_whisper": "Installing Whisper add-on", + "starting_whisper": "Starting Whisper add-on", + "setup_whisper": "Setting up Whisper", + "creating_pipeline": "Creating assistant" + }, + "errors": { + "failed_create_entry": "Failed to create entry for {addon}", + "could_not_find_entities": "Could not find local TTS and STT entities" + } + }, + "success": { + "title": "Ready to Assist!", + "secondary": "Make any final customizations here. You can always change these in the Voice Assistants section of the settings page." + } } }, "automation": { @@ -3967,6 +4048,8 @@ "password_error_msg": "Passwords are at least 8 characters", "start_trial": "Start trial", "resend_confirm_email": "Resend confirmation email", + "clicked_confirm": "I clicked the confirmation link", + "confirm_email": "Check the email we just sent to {email} and click the confirmation link to continue.", "account_created": "Account created! Check your email for instructions on how to activate your account." }, "account": { From c6c5ea34d385b51e88ecabf8f712937f91a0893d Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 6 Dec 2024 09:19:56 +0100 Subject: [PATCH 33/49] Fix text color in ha-md-select in dark mode (#23174) --- src/components/ha-md-select.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-md-select.ts b/src/components/ha-md-select.ts index 924fc21635..7dc7ef03f6 100644 --- a/src/components/ha-md-select.ts +++ b/src/components/ha-md-select.ts @@ -18,6 +18,7 @@ export class HaMdSelect extends MdFilledSelect { --md-sys-color-on-surface: var(--input-ink-color); --md-sys-color-surface-container: var(--input-fill-color); + --md-sys-color-on-secondary-container: var(--primary-text-color); --md-sys-color-secondary-container: var(--input-fill-color); --md-menu-container-color: var(--card-background-color); } From f3e0df93b5e6bb6893d42edad8134c32185c5f75 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 6 Dec 2024 10:37:14 +0100 Subject: [PATCH 34/49] Bumped version to 20241127.5 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e0d234d749..0a0ba72545 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.4" +version = "20241127.5" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 7f31acf7642cc316569f9283b34d5d9304cbc50a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 6 Dec 2024 16:14:30 +0100 Subject: [PATCH 35/49] Fix label selector when required (#23186) --- src/components/ha-form/compute-initial-ha-form-data.ts | 2 ++ src/components/ha-selector/ha-selector-label.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/components/ha-form/compute-initial-ha-form-data.ts b/src/components/ha-form/compute-initial-ha-form-data.ts index b74ad95fe9..53e3488328 100644 --- a/src/components/ha-form/compute-initial-ha-form-data.ts +++ b/src/components/ha-form/compute-initial-ha-form-data.ts @@ -47,6 +47,8 @@ export const computeInitialHaFormData = ( data[field.name] = selector.entity?.multiple ? [] : ""; } else if ("area" in selector) { data[field.name] = selector.area?.multiple ? [] : ""; + } else if ("label" in selector) { + data[field.name] = selector.label?.multiple ? [] : ""; } else if ("boolean" in selector) { data[field.name] = false; } else if ( diff --git a/src/components/ha-selector/ha-selector-label.ts b/src/components/ha-selector/ha-selector-label.ts index 72a5d8ee57..d2b4098683 100644 --- a/src/components/ha-selector/ha-selector-label.ts +++ b/src/components/ha-selector/ha-selector-label.ts @@ -34,6 +34,7 @@ export class HaLabelSelector extends LitElement { no-add .hass=${this.hass} .value=${ensureArray(this.value ?? [])} + .required=${this.required} .disabled=${this.disabled} .label=${this.label} @value-changed=${this._handleChange} @@ -46,6 +47,7 @@ export class HaLabelSelector extends LitElement { no-add .hass=${this.hass} .value=${this.value} + .required=${this.required} .disabled=${this.disabled} .label=${this.label} @value-changed=${this._handleChange} From 72f8f020fc183141ab654af9bf5b8093ebb50fed Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 6 Dec 2024 16:18:37 +0100 Subject: [PATCH 36/49] Bumped version to 20241127.6 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0a0ba72545..69577d089e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.5" +version = "20241127.6" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 443921a97cf03a7f58492cf3f919ca148d25407f Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:41:21 +0100 Subject: [PATCH 37/49] Automate supervisor & landing-page release (#22959) * Automate supervisor & landing-page release * Add no prerelease condition to supervisor/landing-page release * Prepare release workflow for testing * Add release permissions to create PR * Add supervisor, landingpage release to assets * Create test draft release to test * Fix hassio release path * Fix workflow permission for test reasons * Revert test settings --- .github/workflows/release.yaml | 55 +++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 641ea5e7cf..59d085a2ae 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,7 +4,6 @@ on: release: types: - published - workflow_dispatch: env: PYTHON_VERSION: "3.12" @@ -82,3 +81,57 @@ jobs: arch: amd64 wheels-key: ${{ secrets.WHEELS_KEY }} requirements: "requirements.txt" + + release-landing-page: + name: Release landing-page frontend + if: github.event.release.prerelease == false + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4.2.2 + - name: Setup Node + uses: actions/setup-node@v4.1.0 + with: + node-version-file: ".nvmrc" + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download Translations + run: ./script/translations_download + env: + LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} + - name: Build landing-page + run: landing-page/script/build_landing_page + - name: Tar folder + run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist . + - name: Upload release asset + uses: softprops/action-gh-release@v2.1.0 + with: + files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz + + release-supervisor: + name: Release supervisor frontend + if: github.event.release.prerelease == false + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4.2.2 + - name: Setup Node + uses: actions/setup-node@v4.1.0 + with: + node-version-file: ".nvmrc" + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download Translations + run: ./script/translations_download + env: + LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} + - name: Build supervisor + run: hassio/script/build_hassio + - name: Tar folder + run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build . + - name: Upload release asset + uses: softprops/action-gh-release@v2.1.0 + with: + files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz From 0df8b96133de92316fe320d48b6ee41b8f9feb76 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:45:25 -0800 Subject: [PATCH 38/49] No script entities in scenes (#23192) --- src/data/scene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/scene.ts b/src/data/scene.ts index ee56c30c2d..750fdf9135 100644 --- a/src/data/scene.ts +++ b/src/data/scene.ts @@ -17,6 +17,7 @@ export const SCENE_IGNORED_DOMAINS = [ "person", "scene", "schedule", + "script", "sensor", "sun", "update", From 829de4a0739235bc0e5a2b3fb23d714ff3dabceb Mon Sep 17 00:00:00 2001 From: ildar170975 <71872483+ildar170975@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:06:18 +0300 Subject: [PATCH 39/49] Fix "Integration entries" page for yaml-based integrations (#23201) * Update ha-config-integration-page.ts * prettier --- .../ha-config-integration-page.ts | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index f44a03b00f..33f5284085 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -627,19 +627,18 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { "ui.panel.config.integrations.integration_page.no_entries" )} ` - : nothing} - - ${normalEntries.map( - (item, index) => - html`${this._renderConfigEntry(item)} - ${index < normalEntries.length - 1 - ? html` ` - : nothing}` - )} - + : html` + ${normalEntries.map( + (item, index) => + html`${this._renderConfigEntry(item)} + ${index < normalEntries.length - 1 + ? html` ` + : nothing}` + )} + `}
${this._manifest?.integration_type @@ -1490,7 +1489,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { padding-bottom: 0; } .no-entries { - padding-top: 12px; + padding: 12px 16px; } .logo-container { display: flex; From ddd2c177b5adcdc205ef733a44aed12761c5bbd7 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <9592452+CFenner@users.noreply.github.com> Date: Sun, 8 Dec 2024 18:21:34 +0100 Subject: [PATCH 40/49] Correct overwriting integration labelling on integrations page (#23206) * Update ha-config-integration-page.ts fixes #22776 * update icon color --------- Co-authored-by: Petar Petrov --- .../integrations/ha-config-integration-page.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 33f5284085..daad09dece 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -368,11 +368,12 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ` : nothing} ${this._manifest?.is_built_in === false - ? html`
- + ? html`` @@ -1529,6 +1532,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { .integration-info.warn ha-svg-icon { color: var(--warning-color); } + .integration-info.error ha-svg-icon { + color: var(--error-color); + } .integration-info.info ha-svg-icon { color: var(--info-color); } From 629ae3fbf3e7b715608824e22812d102974c971a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 8 Dec 2024 16:32:43 -0500 Subject: [PATCH 41/49] Fix voice debug link (#23214) --- .../config/voice-assistants/assist-pref.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/panels/config/voice-assistants/assist-pref.ts b/src/panels/config/voice-assistants/assist-pref.ts index 8692d95680..86c1f0a60b 100644 --- a/src/panels/config/voice-assistants/assist-pref.ts +++ b/src/panels/config/voice-assistants/assist-pref.ts @@ -42,6 +42,7 @@ import { showVoiceAssistantPipelineDetailDialog } from "./show-dialog-voice-assi import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeDomain } from "../../../common/entity/compute_domain"; +import { navigate } from "../../../common/navigate"; @customElement("assist-pref") export class AssistPref extends LitElement { @@ -159,14 +160,16 @@ export class AssistPref extends LitElement { )} - - - ${this.hass.localize( - "ui.panel.config.voice_assistants.assistants.pipeline.detail.debug" - )} - - - + + ${this.hass.localize( + "ui.panel.config.voice_assistants.assistants.pipeline.detail.debug" + )} + + Date: Mon, 9 Dec 2024 15:59:07 +0100 Subject: [PATCH 42/49] Bumped version to 20241127.7 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 69577d089e..bcfcbba5ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.6" +version = "20241127.7" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 3768be55ff607bc4118bdd18f9fbc370ec0921fe Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:51:25 +0100 Subject: [PATCH 43/49] Fix landingpage, supervisor release permission (#23223) --- .github/workflows/release.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 59d085a2ae..edbd85c50f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -86,6 +86,8 @@ jobs: name: Release landing-page frontend if: github.event.release.prerelease == false runs-on: ubuntu-latest + permissions: + contents: write # Required to upload release assets steps: - name: Checkout the repository uses: actions/checkout@v4.2.2 @@ -113,6 +115,8 @@ jobs: name: Release supervisor frontend if: github.event.release.prerelease == false runs-on: ubuntu-latest + permissions: + contents: write # Required to upload release assets steps: - name: Checkout the repository uses: actions/checkout@v4.2.2 From 8b25fe88a46ff6d34a24bb6cad6465cbefa00884 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 10 Dec 2024 12:33:21 +0100 Subject: [PATCH 44/49] Use list item for integration quality scale (#23236) --- .../ha-config-integration-page.ts | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index daad09dece..ab75fc915a 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -339,34 +339,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ${this._manifest?.version != null ? html`
${this._manifest.version}
` : nothing} - ${this._manifest?.quality_scale && - Object.keys(QUALITY_SCALE_MAP).includes( - this._manifest.quality_scale - ) - ? html` - - ` - : nothing} ${this._manifest?.is_built_in === false ? html`
+ ${this._manifest?.quality_scale && + Object.keys(QUALITY_SCALE_MAP).includes( + this._manifest.quality_scale + ) + ? html` + + + + ${this.hass.localize( + QUALITY_SCALE_MAP[this._manifest.quality_scale] + .translationKey + )} + + + + ` + : nothing} ${devices.length > 0 ? html` Date: Thu, 12 Dec 2024 17:38:53 +0100 Subject: [PATCH 45/49] Add missing translations to voice wizard (#23278) --- src/translations/en.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 5676049328..5602399e3a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2871,7 +2871,11 @@ "cloud": { "description": "Ideal if you don't have a powerful system at home." }, - "local": { "title": "", "description": "", "setup": "" } + "local": { + "title": "Do-it-yourself", + "description": "Install add-ons or containers to run it on your own system. Powerful hardware is needed for fast responses.", + "setup": "Set up" + } }, "cloud": { "title": "The power of Home Assistant Cloud", From 3d78a7821a33d47b5e9e720cb305bef302c4916d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Dec 2024 11:41:11 -0500 Subject: [PATCH 46/49] Improve piper audio generation (#23281) * Improve piper audio generation * update logic --------- Co-authored-by: Bram Kragten --- .../voice-assistant-setup-step-pipeline.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts index e75e4260cf..c8cb672388 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts @@ -57,10 +57,10 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { }, 600); setTimeout(() => { this._showThird = true; - }, 3000); + }, 2000); setTimeout(() => { this._showFourth = true; - }, 8000); + }, 3000); } protected override render() { @@ -123,7 +123,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
${this._showThird ? html`
- 3 + 2 ${this.hass.localize( "ui.panel.config.voice_assistants.satellite_wizard.pipeline.seconds" )} @@ -136,7 +136,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { : nothing} ${this._showFourth ? html`
- 5 + 1 ${this.hass.localize( "ui.panel.config.voice_assistants.satellite_wizard.pipeline.seconds" )} From f8dfdcb090ed3982df3adaa17c4a9fef7467f490 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 12 Dec 2024 17:43:43 +0100 Subject: [PATCH 47/49] Bumped version to 20241127.8 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bcfcbba5ee..27949672ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.7" +version = "20241127.8" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From d21f249aacaec4fc439d3da8e2db4a0956004ff1 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 17 Dec 2024 16:50:29 +0100 Subject: [PATCH 48/49] Only use pipelines that have the default coversation agent (#23320) only use pipelines that have the default coversation agent --- .../voice-assistant-setup/voice-assistant-setup-step-local.ts | 3 +++ .../voice-assistant-setup-step-pipeline.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts index e4ff17d33f..9791fc00b5 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-local.ts @@ -293,6 +293,8 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { if (preferredPipeline) { if ( + preferredPipeline.conversation_engine === + "conversation.home_assistant" && preferredPipeline.tts_engine && ttsEntityIds.includes(preferredPipeline.tts_engine) && preferredPipeline.stt_engine && @@ -311,6 +313,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement { let localPipeline = pipelines.pipelines.find( (pipeline) => + pipeline.conversation_engine === "conversation.home_assistant" && pipeline.tts_engine && ttsEntityIds.includes(pipeline.tts_engine) && pipeline.stt_engine && diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts index c8cb672388..01f27f0666 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-pipeline.ts @@ -213,6 +213,8 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { if (preferredPipeline) { if ( + preferredPipeline.conversation_engine === + "conversation.home_assistant" && preferredPipeline.tts_engine === cloudTtsEntityId && preferredPipeline.stt_engine === cloudSttEntityId ) { @@ -229,6 +231,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement { let cloudPipeline = pipelines.pipelines.find( (pipeline) => + pipeline.conversation_engine === "conversation.home_assistant" && pipeline.tts_engine === cloudTtsEntityId && pipeline.stt_engine === cloudSttEntityId ); From 5470c8f250c0c2447358083ba16abb4da226b645 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 20 Dec 2024 13:54:18 +0100 Subject: [PATCH 49/49] Bumped version to 20241127.9 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 27949672ba..c5d9ceb1d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241127.8" +version = "20241127.9" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md"