diff --git a/.github/workflows/relative-ci.yaml b/.github/workflows/relative-ci.yaml index c9db3b3a3a..58d9eb796d 100644 --- a/.github/workflows/relative-ci.yaml +++ b/.github/workflows/relative-ci.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send bundle stats and build information to RelativeCI - uses: relative-ci/agent-action@v2.1.12 + uses: relative-ci/agent-action@v2.1.13 with: key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} token: ${{ github.token }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2eaf2b6a26..b919371340 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -55,7 +55,7 @@ jobs: script/release - name: Upload release assets - uses: softprops/action-gh-release@v2.0.8 + uses: softprops/action-gh-release@v2.0.9 with: files: | dist/*.whl diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 8f85440499..2737aea12c 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -223,7 +223,10 @@ class HassioAddonInfo extends LitElement {
${this.addon.version ? html` - Current version: ${this.addon.version} + ${this.supervisor.localize( + "addon.dashboard.current_version", + { version: this.addon.version } + )}
(${this.supervisor.localize( diff --git a/package.json b/package.json index 97b0732e12..956b2bf945 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,11 @@ "dependencies": { "@babel/runtime": "7.26.0", "@braintree/sanitize-url": "7.1.0", - "@codemirror/autocomplete": "6.18.1", + "@codemirror/autocomplete": "6.18.2", "@codemirror/commands": "6.7.1", "@codemirror/language": "6.10.3", "@codemirror/legacy-modes": "6.4.1", - "@codemirror/search": "6.5.6", + "@codemirror/search": "6.5.7", "@codemirror/state": "6.4.1", "@codemirror/view": "6.34.1", "@egjs/hammerjs": "2.0.17", @@ -101,7 +101,7 @@ "chart.js": "4.4.6", "color-name": "2.0.0", "comlink": "4.4.1", - "core-js": "3.38.1", + "core-js": "3.39.0", "cropperjs": "1.6.2", "date-fns": "4.1.0", "date-fns-tz": "3.2.0", @@ -142,12 +142,12 @@ "vue": "2.7.16", "vue2-daterange-picker": "0.6.8", "weekstart": "2.0.0", - "workbox-cacheable-response": "7.1.0", - "workbox-core": "7.1.0", - "workbox-expiration": "7.1.0", - "workbox-precaching": "7.1.0", - "workbox-routing": "7.1.0", - "workbox-strategies": "7.1.0", + "workbox-cacheable-response": "7.3.0", + "workbox-core": "7.3.0", + "workbox-expiration": "7.3.0", + "workbox-precaching": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0", "xss": "1.0.15" }, "devDependencies": { @@ -224,7 +224,7 @@ "lodash.template": "4.5.0", "magic-string": "0.30.12", "map-stream": "0.0.7", - "mocha": "10.7.3", + "mocha": "10.8.2", "object-hash": "3.0.0", "open": "10.1.0", "pinst": "3.0.0", @@ -241,7 +241,7 @@ "transform-async-modules-webpack-plugin": "1.1.1", "ts-lit-plugin": "2.0.2", "typescript": "5.6.3", - "webpack": "5.95.0", + "webpack": "5.96.1", "webpack-cli": "5.1.4", "webpack-dev-server": "5.1.0", "webpack-manifest-plugin": "5.0.0", diff --git a/public/static/images/voice-assistant/area.gif b/public/static/images/voice-assistant/area.gif deleted file mode 100644 index e56aebd62d..0000000000 Binary files a/public/static/images/voice-assistant/area.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/area.png b/public/static/images/voice-assistant/area.png new file mode 100644 index 0000000000..d325705c0e Binary files /dev/null and b/public/static/images/voice-assistant/area.png differ diff --git a/public/static/images/voice-assistant/change-wake-word.gif b/public/static/images/voice-assistant/change-wake-word.gif deleted file mode 100644 index d8fec78be5..0000000000 Binary files a/public/static/images/voice-assistant/change-wake-word.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/change-wake-word.png b/public/static/images/voice-assistant/change-wake-word.png new file mode 100644 index 0000000000..7be0377c0d Binary files /dev/null and b/public/static/images/voice-assistant/change-wake-word.png differ diff --git a/public/static/images/voice-assistant/error.gif b/public/static/images/voice-assistant/error.gif deleted file mode 100644 index 0e19c5a20d..0000000000 Binary files a/public/static/images/voice-assistant/error.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/error.png b/public/static/images/voice-assistant/error.png new file mode 100644 index 0000000000..84008f9da9 Binary files /dev/null and b/public/static/images/voice-assistant/error.png differ diff --git a/public/static/images/voice-assistant/great-job.png b/public/static/images/voice-assistant/great-job.png new file mode 100644 index 0000000000..5e2fa242ac Binary files /dev/null and b/public/static/images/voice-assistant/great-job.png differ diff --git a/public/static/images/voice-assistant/heart.gif b/public/static/images/voice-assistant/heart.gif deleted file mode 100644 index 8a7c956829..0000000000 Binary files a/public/static/images/voice-assistant/heart.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/heart.png b/public/static/images/voice-assistant/heart.png new file mode 100644 index 0000000000..3eac74cfb7 Binary files /dev/null and b/public/static/images/voice-assistant/heart.png differ diff --git a/public/static/images/voice-assistant/hi.gif b/public/static/images/voice-assistant/hi.gif deleted file mode 100644 index 9769ad0d29..0000000000 Binary files a/public/static/images/voice-assistant/hi.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/hi.png b/public/static/images/voice-assistant/hi.png new file mode 100644 index 0000000000..9c37a9a2fd Binary files /dev/null and b/public/static/images/voice-assistant/hi.png differ diff --git a/public/static/images/voice-assistant/ok-nabu.gif b/public/static/images/voice-assistant/ok-nabu.gif deleted file mode 100644 index 866269f23a..0000000000 Binary files a/public/static/images/voice-assistant/ok-nabu.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/ok-nabu.png b/public/static/images/voice-assistant/ok-nabu.png new file mode 100644 index 0000000000..f909705537 Binary files /dev/null and b/public/static/images/voice-assistant/ok-nabu.png differ diff --git a/public/static/images/voice-assistant/sleep.gif b/public/static/images/voice-assistant/sleep.gif deleted file mode 100644 index 23ddb402b6..0000000000 Binary files a/public/static/images/voice-assistant/sleep.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/sleep.png b/public/static/images/voice-assistant/sleep.png new file mode 100644 index 0000000000..1639006a34 Binary files /dev/null and b/public/static/images/voice-assistant/sleep.png differ diff --git a/public/static/images/voice-assistant/update.gif b/public/static/images/voice-assistant/update.gif deleted file mode 100644 index a8b569ef99..0000000000 Binary files a/public/static/images/voice-assistant/update.gif and /dev/null differ diff --git a/public/static/images/voice-assistant/update.png b/public/static/images/voice-assistant/update.png new file mode 100644 index 0000000000..685b63e3f8 Binary files /dev/null and b/public/static/images/voice-assistant/update.png differ diff --git a/pyproject.toml b/pyproject.toml index 9cc215662c..86a0d55242 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241031.0" +version = "20241104.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index 52a182d7a2..29bf3a7860 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -102,7 +102,9 @@ export class HaCameraStream extends LitElement { .entityid=${this.stateObj.entity_id} .posterUrl=${this._posterUrl} @streams=${this._handleHlsStreams} - class=${!this._streamType && this._webRtcStreams ? "hidden" : ""} + class=${!this._streamType && this._webRtcStreams?.hasVideo + ? "hidden" + : ""} >` : nothing} ${this._streamType === STREAM_TYPE_WEB_RTC || diff --git a/src/components/ha-duration-input.ts b/src/components/ha-duration-input.ts index 3c5077708e..2b35d622ad 100644 --- a/src/components/ha-duration-input.ts +++ b/src/components/ha-duration-input.ts @@ -43,6 +43,7 @@ class HaDurationInput extends LitElement { .label=${this.label} .helper=${this.helper} .required=${this.required} + .clearable=${!this.required && this.data !== undefined} .autoValidate=${this.required} .disabled=${this.disabled} errorMessage="Required" @@ -67,50 +68,79 @@ class HaDurationInput extends LitElement { } private get _days() { - return this.data?.days ? Number(this.data.days) : 0; + return this.data?.days + ? Number(this.data.days) + : this.required || this.data + ? 0 + : NaN; } private get _hours() { - return this.data?.hours ? Number(this.data.hours) : 0; + return this.data?.hours + ? Number(this.data.hours) + : this.required || this.data + ? 0 + : NaN; } private get _minutes() { - return this.data?.minutes ? Number(this.data.minutes) : 0; + return this.data?.minutes + ? Number(this.data.minutes) + : this.required || this.data + ? 0 + : NaN; } private get _seconds() { - return this.data?.seconds ? Number(this.data.seconds) : 0; + return this.data?.seconds + ? Number(this.data.seconds) + : this.required || this.data + ? 0 + : NaN; } private get _milliseconds() { - return this.data?.milliseconds ? Number(this.data.milliseconds) : 0; + return this.data?.milliseconds + ? Number(this.data.milliseconds) + : this.required || this.data + ? 0 + : NaN; } - private _durationChanged(ev: CustomEvent<{ value: TimeChangedEvent }>) { + private _durationChanged(ev: CustomEvent<{ value?: TimeChangedEvent }>) { ev.stopPropagation(); - const value = { ...ev.detail.value }; + const value = ev.detail.value ? { ...ev.detail.value } : undefined; - if (!this.enableMillisecond && !value.milliseconds) { - // @ts-ignore - delete value.milliseconds; - } else if (value.milliseconds > 999) { - value.seconds += Math.floor(value.milliseconds / 1000); - value.milliseconds %= 1000; - } + if (value) { + value.hours ||= 0; + value.minutes ||= 0; + value.seconds ||= 0; - if (value.seconds > 59) { - value.minutes += Math.floor(value.seconds / 60); - value.seconds %= 60; - } + if ("days" in value) value.days ||= 0; + if ("milliseconds" in value) value.milliseconds ||= 0; - if (value.minutes > 59) { - value.hours += Math.floor(value.minutes / 60); - value.minutes %= 60; - } + if (!this.enableMillisecond && !value.milliseconds) { + // @ts-ignore + delete value.milliseconds; + } else if (value.milliseconds > 999) { + value.seconds += Math.floor(value.milliseconds / 1000); + value.milliseconds %= 1000; + } - if (this.enableDay && value.hours > 24) { - value.days = (value.days ?? 0) + Math.floor(value.hours / 24); - value.hours %= 24; + if (value.seconds > 59) { + value.minutes += Math.floor(value.seconds / 60); + value.seconds %= 60; + } + + if (value.minutes > 59) { + value.hours += Math.floor(value.minutes / 60); + value.minutes %= 60; + } + + if (this.enableDay && value.hours > 24) { + value.days = (value.days ?? 0) + Math.floor(value.hours / 24); + value.hours %= 24; + } } fireEvent(this, "value-changed", { diff --git a/src/components/ha-icon.ts b/src/components/ha-icon.ts index 41bbe25433..3428deaada 100644 --- a/src/components/ha-icon.ts +++ b/src/components/ha-icon.ts @@ -8,7 +8,6 @@ import { customIcons } from "../data/custom_icons"; import type { Chunks, Icons } from "../data/iconsets"; import { MDI_PREFIXES, - checkCacheVersion, findIconChunk, getIcon, writeCache, @@ -26,11 +25,6 @@ const mdiDeprecatedIcons: DeprecatedIcon = {}; const chunks: Chunks = {}; -// Supervisor doesn't use icons, and should not update/downgrade the icon DB. -if (!__SUPERVISOR__) { - checkCacheVersion(); -} - const debouncedWriteCache = debounce(() => writeCache(chunks), 2000); const cachedIcons: Record = {}; diff --git a/src/components/ha-toast.ts b/src/components/ha-toast.ts index f742ff0ebe..f0f94cbea4 100644 --- a/src/components/ha-toast.ts +++ b/src/components/ha-toast.ts @@ -24,7 +24,7 @@ export class HaToast extends Snackbar { max-width: 650px; } - // Revert the default styles set by mwc-snackbar + /* Revert the default styles set by mwc-snackbar */ @media (max-width: 480px), (max-width: 344px) { .mdc-snackbar__surface { min-width: inherit; diff --git a/src/components/map/ha-map.ts b/src/components/map/ha-map.ts index e4777b970e..0cce2ded0b 100644 --- a/src/components/map/ha-map.ts +++ b/src/components/map/ha-map.ts @@ -86,6 +86,8 @@ export class HaMap extends ReactiveElement { private _mapZones: Array = []; + private _mapFocusZones: Array = []; + private _mapPaths: Array = []; public connectedCallback(): void { @@ -201,7 +203,11 @@ export class HaMap extends ReactiveElement { return; } - if (!this._mapFocusItems.length && !this.layers?.length) { + if ( + !this._mapFocusItems.length && + !this._mapFocusZones.length && + !this.layers?.length + ) { this.leafletMap.setView( new this.Leaflet.LatLng( this.hass.config.latitude, @@ -218,13 +224,9 @@ export class HaMap extends ReactiveElement { : [] ); - if (this.fitZones) { - this._mapZones?.forEach((zone) => { - bounds.extend( - "getBounds" in zone ? zone.getBounds() : zone.getLatLng() - ); - }); - } + this._mapFocusZones?.forEach((zone) => { + bounds.extend("getBounds" in zone ? zone.getBounds() : zone.getLatLng()); + }); this.layers?.forEach((layer: any) => { bounds.extend( @@ -395,6 +397,7 @@ export class HaMap extends ReactiveElement { if (this._mapZones.length) { this._mapZones.forEach((marker) => marker.remove()); this._mapZones = []; + this._mapFocusZones = []; } if (!this.entities) { @@ -466,13 +469,18 @@ export class HaMap extends ReactiveElement { ); // create circle around it - this._mapZones.push( - Leaflet.circle([latitude, longitude], { - interactive: false, - color: passive ? passiveZoneColor : zoneColor, - radius, - }) - ); + const circle = Leaflet.circle([latitude, longitude], { + interactive: false, + color: passive ? passiveZoneColor : zoneColor, + radius, + }); + this._mapZones.push(circle); + if ( + this.fitZones && + (typeof entity === "string" || entity.focus !== false) + ) { + this._mapFocusZones.push(circle); + } continue; } diff --git a/src/data/hassio/supervisor.ts b/src/data/hassio/supervisor.ts index cc74a7fd70..a0966d1bae 100644 --- a/src/data/hassio/supervisor.ts +++ b/src/data/hassio/supervisor.ts @@ -193,7 +193,7 @@ export const fetchHassioLogs = async ( ) => hass.callApiRaw( "GET", - `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}`, + `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs${boot !== 0 ? `/boots/${boot}` : ""}`, undefined, range ? { @@ -203,20 +203,6 @@ export const fetchHassioLogs = async ( ); export const fetchHassioLogsFollow = async ( - hass: HomeAssistant, - provider: string, - signal: AbortSignal, - lines = 100 -) => - hass.callApiRaw( - "GET", - `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/follow?lines=${lines}`, - undefined, - undefined, - signal - ); - -export const fetchHassioLogsBootFollow = async ( hass: HomeAssistant, provider: string, signal: AbortSignal, @@ -225,7 +211,7 @@ export const fetchHassioLogsBootFollow = async ( ) => hass.callApiRaw( "GET", - `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}/follow?lines=${lines}`, + `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs${boot !== 0 ? `/boots/${boot}` : ""}/follow?lines=${lines}`, undefined, undefined, signal @@ -236,19 +222,14 @@ export const getHassioLogDownloadUrl = (provider: string) => provider.includes("_") ? `addons/${provider}` : provider }/logs`; -export const getHassioLogDownloadLinesUrl = (provider: string, lines: number) => - `/api/hassio/${ - provider.includes("_") ? `addons/${provider}` : provider - }/logs?lines=${lines}`; - -export const getHassioLogBootDownloadLinesUrl = ( +export const getHassioLogDownloadLinesUrl = ( provider: string, lines: number, boot = 0 ) => `/api/hassio/${ provider.includes("_") ? `addons/${provider}` : provider - }/logs/boots/${boot}?lines=${lines}`; + }/logs${boot !== 0 ? `/boots/${boot}` : ""}?lines=${lines}`; export const setSupervisorOption = async ( hass: HomeAssistant, diff --git a/src/data/iconsets.ts b/src/data/iconsets.ts index 9cee9041c5..5bf8f1671e 100644 --- a/src/data/iconsets.ts +++ b/src/data/iconsets.ts @@ -1,4 +1,5 @@ import { clear, get, set, createStore, promisifyRequest } from "idb-keyval"; +import memoizeOne from "memoize-one"; import { promiseTimeout } from "../common/util/promise-timeout"; import { iconMetadata } from "../resources/icon-metadata"; import type { IconMeta } from "../types"; @@ -11,7 +12,23 @@ export interface Chunks { [key: string]: Promise; } -export const iconStore = createStore("hass-icon-db", "mdi-icon-store"); +const getStore = memoizeOne(async () => { + const iconStore = createStore("hass-icon-db", "mdi-icon-store"); + + // Supervisor doesn't use icons, and should not update/downgrade the icon DB. + if (!__SUPERVISOR__) { + const version = await get("_version", iconStore); + + if (!version) { + set("_version", iconMetadata.version, iconStore); + } else if (version !== iconMetadata.version) { + await clear(iconStore); + set("_version", iconMetadata.version, iconStore); + } + } + + return iconStore; +}); export const MDI_PREFIXES = ["mdi", "hass", "hassio", "hademo"]; @@ -28,7 +45,10 @@ export const getIcon = (iconName: string) => return; } - const readIcons = () => + // Start initializing the store, so it's ready when we need it + const iconStoreProm = getStore(); + const readIcons = async () => { + const iconStore = await iconStoreProm; iconStore("readonly", (store) => { for (const [iconName_, resolve_, reject_] of toRead) { promisifyRequest(store.get(iconName_)) @@ -37,6 +57,7 @@ export const getIcon = (iconName: string) => } toRead = []; }); + }; promiseTimeout(1000, readIcons()).catch((e) => { // Firefox in private mode doesn't support IDB @@ -62,6 +83,7 @@ export const findIconChunk = (icon: string): string => { export const writeCache = async (chunks: Chunks) => { const keys = Object.keys(chunks); const iconsSets: Icons[] = await Promise.all(Object.values(chunks)); + const iconStore = await getStore(); // We do a batch opening the store just once, for (considerable) performance iconStore("readwrite", (store) => { iconsSets.forEach((icons, idx) => { @@ -72,14 +94,3 @@ export const writeCache = async (chunks: Chunks) => { }); }); }; - -export const checkCacheVersion = async () => { - const version = await get("_version", iconStore); - - if (!version) { - set("_version", iconMetadata.version, iconStore); - } else if (version !== iconMetadata.version) { - await clear(iconStore); - set("_version", iconMetadata.version, iconStore); - } -}; diff --git a/src/dialogs/config-flow/previews/entity-preview-row.ts b/src/dialogs/config-flow/previews/entity-preview-row.ts index deb0031150..a52606b510 100644 --- a/src/dialogs/config-flow/previews/entity-preview-row.ts +++ b/src/dialogs/config-flow/previews/entity-preview-row.ts @@ -1,10 +1,24 @@ import type { HassEntity } from "home-assistant-js-websocket"; -import type { CSSResultGroup } from "lit"; +import type { CSSResultGroup, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { ifDefined } from "lit/directives/if-defined"; +import { format } from "date-fns"; import { computeStateName } from "../../../common/entity/compute_state_name"; +import "../../../components/ha-climate-state"; +import "../../../components/ha-cover-controls"; +import "../../../components/ha-cover-tilt-controls"; +import "../../../components/ha-date-input"; +import "../../../components/ha-humidifier-state"; +import "../../../components/ha-select"; +import "../../../components/ha-slider"; +import "../../../components/ha-time-input"; +import "../../../components/entity/ha-entity-toggle"; import "../../../components/entity/state-badge"; +import { isTiltOnly } from "../../../data/cover"; import { isUnavailableState } from "../../../data/entity"; +import type { ImageEntity } from "../../../data/image"; +import { computeImageUrl } from "../../../data/image"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import "../../../panels/lovelace/components/hui-timestamp-display"; import type { HomeAssistant } from "../../../types"; @@ -28,18 +42,7 @@ class EntityPreviewRow extends LitElement {
${computeStateName(stateObj)}
-
- ${stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - !isUnavailableState(stateObj.state) - ? html` - - ` - : this.hass.formatEntityState(stateObj)} -
`; +
${this.renderEntityState(stateObj)}
`; } static get styles(): CSSResultGroup { @@ -59,8 +62,308 @@ class EntityPreviewRow extends LitElement { .value { direction: ltr; } + .numberflex { + display: flex; + align-items: center; + justify-content: flex-end; + flex-grow: 2; + } + .numberstate { + min-width: 45px; + text-align: end; + } + ha-textfield { + text-align: end; + direction: ltr !important; + } + ha-slider { + width: 100%; + max-width: 200px; + } + ha-time-input { + margin-left: 4px; + margin-inline-start: 4px; + margin-inline-end: initial; + direction: var(--direction); + } + .datetimeflex { + display: flex; + justify-content: flex-end; + width: 100%; + } + mwc-button { + margin-right: -0.57em; + margin-inline-end: -0.57em; + margin-inline-start: initial; + } + img { + display: block; + width: 100%; + } `; } + + private renderEntityState(stateObj: HassEntity): TemplateResult | string { + const domain = stateObj.entity_id.split(".", 1)[0]; + + if (domain === "button") { + return html` + + ${this.hass.localize("ui.card.button.press")} + + `; + } + + const climateDomains = ["climate", "water_heater"]; + if (climateDomains.includes(domain)) { + return html` + + + `; + } + + if (domain === "cover") { + return html` + ${isTiltOnly(stateObj) + ? html` + + ` + : html` + + `} + `; + } + + if (domain === "date") { + return html` + + + `; + } + + if (domain === "datetime") { + const dateObj = isUnavailableState(stateObj.state) + ? undefined + : new Date(stateObj.state); + const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined; + const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined; + return html` +
+ + + +
+ `; + } + + if (domain === "event") { + return html` +
+ ${isUnavailableState(stateObj.state) + ? this.hass.formatEntityState(stateObj) + : html``} +
+
+ ${isUnavailableState(stateObj.state) + ? nothing + : this.hass.formatEntityAttributeValue(stateObj, "event_type")} +
+ `; + } + + const toggleDomains = ["fan", "light", "remote", "siren", "switch"]; + if (toggleDomains.includes(domain)) { + const showToggle = + stateObj.state === "on" || + stateObj.state === "off" || + isUnavailableState(stateObj.state); + return html` + ${showToggle + ? html` + + ` + : this.hass.formatEntityState(stateObj)} + `; + } + + if (domain === "humidifier") { + return html` + + + `; + } + + if (domain === "image") { + const image: string = computeImageUrl(stateObj as ImageEntity); + return html` + ${ifDefined(stateObj?.attributes.friendly_name)} + `; + } + + if (domain === "lock") { + return html` + + ${stateObj.state === "locked" + ? this.hass!.localize("ui.card.lock.unlock") + : this.hass!.localize("ui.card.lock.lock")} + + `; + } + + if (domain === "number") { + const showNumberSlider = + stateObj.attributes.mode === "slider" || + (stateObj.attributes.mode === "auto" && + (Number(stateObj.attributes.max) - Number(stateObj.attributes.min)) / + Number(stateObj.attributes.step) <= + 256); + return html` + ${showNumberSlider + ? html` +
+ + + ${this.hass.formatEntityState(stateObj)} + +
+ ` + : html`
+ +
`} + `; + } + + if (domain === "select") { + return html` + + ${stateObj.attributes.options + ? stateObj.attributes.options.map( + (option) => html` + + ${this.hass!.formatEntityState(stateObj, option)} + + ` + ) + : ""} + + `; + } + + if (domain === "sensor") { + const showSensor = + stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && + !isUnavailableState(stateObj.state); + return html` + ${showSensor + ? html` + + ` + : this.hass.formatEntityState(stateObj)} + `; + } + + if (domain === "text") { + return html` + + `; + } + + if (domain === "time") { + return html` + + `; + } + + if (domain === "weather") { + return html` +
+ ${isUnavailableState(stateObj.state) || + stateObj.attributes.temperature === undefined || + stateObj.attributes.temperature === null + ? this.hass.formatEntityState(stateObj) + : this.hass.formatEntityAttributeValue(stateObj, "temperature")} +
+ `; + } + + return this.hass.formatEntityState(stateObj); + } } declare global { diff --git a/src/dialogs/more-info/controls/more-info-update.ts b/src/dialogs/more-info/controls/more-info-update.ts index 38c332cece..7014a505e9 100644 --- a/src/dialogs/more-info/controls/more-info-update.ts +++ b/src/dialogs/more-info/controls/more-info-update.ts @@ -143,7 +143,7 @@ class MoreInfoUpdate extends LitElement { )}
diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts index 2fabe74db1..4d7a5d8535 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-dialog.ts @@ -66,6 +66,8 @@ export class HaVoiceAssistantSetupDialog extends LitElement { private _dialogClosed() { this._params = undefined; this._assistConfiguration = undefined; + this._previousSteps = []; + this._nextStep = undefined; this._step = STEP.INIT; fireEvent(this, "dialog-closed", { dialog: this.localName }); } diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-area.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-area.ts index 31c25e178c..d6c918c957 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-area.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-area.ts @@ -17,7 +17,7 @@ export class HaVoiceAssistantSetupStepArea extends LitElement { const device = this.hass.devices[this.deviceId]; return html`
- +

Select area

When you voice assistant knows where it is, it can better control the 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 1ea24948f1..8f51521c9b 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 @@ -21,7 +21,7 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement { protected override render() { return html`

- +

Change wake word

Some wake words are better for 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 a12a243c2e..415d6e4d13 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 @@ -6,6 +6,7 @@ import "../../components/ha-circular-progress"; import { testAssistSatelliteConnection } from "../../data/assist_satellite"; import type { HomeAssistant } from "../../types"; import { AssistantSetupStyles } from "./styles"; +import { documentationUrl } from "../../util/documentation-url"; @customElement("ha-voice-assistant-setup-step-check") export class HaVoiceAssistantSetupStepCheck extends LitElement { @@ -35,7 +36,7 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement { protected override render() { return html`

${this._status === "timeout" - ? html` + ? html`

The voice assistant is unable to connect to Home Assistant

To play audio, the voice assistant device has to connect to Home @@ -44,12 +45,15 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {

` - : html` + : html`

Hi

Over the next couple steps we're going to personalize your voice 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 f4b171bda7..939d76be5e 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 @@ -67,7 +67,7 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement { : undefined; return html`

- +

Ready to Assist!

Make any final customizations here. You can always change these in the 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 9171ff15f0..e81afa480b 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 @@ -65,7 +65,7 @@ export class HaVoiceAssistantSetupStepUpdate extends LitElement { const progressIsNumeric = stateObj && updateUsesProgress(stateObj); return html`

- +

${stateObj && (stateObj.state === "unavailable" || updateIsInstalling(stateObj)) diff --git a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-wake-word.ts b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-wake-word.ts index 2515f7ba9c..442ca54192 100644 --- a/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-wake-word.ts +++ b/src/dialogs/voice-assistant-setup/voice-assistant-setup-step-wake-word.ts @@ -64,14 +64,14 @@ export class HaVoiceAssistantSetupStepWakeWord extends LitElement { return html`
${!this._detected ? html` - +

Say “${this._activeWakeWord(this.assistConfiguration)}” to wake the device up

Setup will continue once the device is awake.

` - : html` + : html`

Say “${this._activeWakeWord(this.assistConfiguration)}” again

diff --git a/src/onboarding/onboarding-create-user.ts b/src/onboarding/onboarding-create-user.ts index 2d843b3b26..5f9d333e94 100644 --- a/src/onboarding/onboarding-create-user.ts +++ b/src/onboarding/onboarding-create-user.ts @@ -16,6 +16,8 @@ import type { ValueChangedEvent } from "../types"; import { onBoardingStyles } from "./styles"; import { debounce } from "../common/util/debounce"; +const CHECK_USERNAME_REGEX = /\s|[A-Z]/; + const CREATE_USER_SCHEMA: HaFormSchema[] = [ { name: "name", @@ -121,6 +123,7 @@ class OnboardingCreateUser extends LitElement { ev: ValueChangedEvent ): void { const nameChanged = ev.detail.value.name !== this._newUser.name; + const usernameChanged = ev.detail.value.username !== this._newUser.username; const passwordChanged = ev.detail.value.password !== this._newUser.password || ev.detail.value.password_confirm !== this._newUser.password_confirm; @@ -135,6 +138,9 @@ class OnboardingCreateUser extends LitElement { this._debouncedCheckPasswordMatch(); } } + if (usernameChanged) { + this._checkUsername(); + } } private _debouncedCheckPasswordMatch = debounce( @@ -164,6 +170,21 @@ class OnboardingCreateUser extends LitElement { const parts = String(this._newUser.name).split(" "); if (parts.length) { this._newUser.username = parts[0].toLowerCase(); + this._checkUsername(); + } + } + + private _checkUsername(): void { + const old = this._formError.username; + if (CHECK_USERNAME_REGEX.test(this._newUser.username as string)) { + this._formError.username = this.localize( + "ui.panel.page-onboarding.user.error.username_not_normalized" + ); + } else { + this._formError.username = ""; + } + if (old !== this._formError.username) { + this.requestUpdate("_formError"); } } diff --git a/src/panels/config/automation/action/types/ha-automation-action-delay.ts b/src/panels/config/automation/action/types/ha-automation-action-delay.ts index 996824c7c5..3efb1c618a 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-delay.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-delay.ts @@ -49,6 +49,7 @@ export class HaDelayAction extends LitElement implements ActionElement { .disabled=${this.disabled} .data=${this._timeData} enableMillisecond + required @value-changed=${this._valueChanged} >`; } diff --git a/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts b/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts index 3c27aa6dfb..7d6e74fd79 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts @@ -67,9 +67,6 @@ export class HaWaitForTriggerAction private _timeoutChanged(ev: CustomEvent<{ value: TimeChangedEvent }>): void { ev.stopPropagation(); const value = ev.detail.value; - if (!value) { - return; - } fireEvent(this, "value-changed", { value: { ...this.action, timeout: value }, }); diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 4c32f3aa3d..37c9f8b77b 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -712,8 +712,12 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { private async _duplicate() { const result = this._readOnly ? await showConfirmationDialog(this, { - title: "Migrate automation?", - text: "You can migrate this automation, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old automation from your configuration. Do you want to migrate this automation?", + title: this.hass.localize( + "ui.panel.config.automation.picker.migrate_automation" + ), + text: this.hass.localize( + "ui.panel.config.automation.picker.migrate_automation_description" + ), }) : await this.confirmUnsavedChanged(); if (result) { diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-calendar.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-calendar.ts index 4ece6655ae..c2e11c7890 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-calendar.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-calendar.ts @@ -46,7 +46,7 @@ export class HaCalendarTrigger extends LitElement implements TriggerElement { ], ], }, - { name: "offset", selector: { duration: {} } }, + { name: "offset", required: true, selector: { duration: {} } }, { name: "offset_type", type: "select", diff --git a/src/panels/config/integrations/dialog-add-integration.ts b/src/panels/config/integrations/dialog-add-integration.ts index 3c0d4f156c..5447065e0c 100644 --- a/src/panels/config/integrations/dialog-add-integration.ts +++ b/src/panels/config/integrations/dialog-add-integration.ts @@ -584,6 +584,10 @@ class AddIntegrationDialog extends LitElement { }); if (configEntries.length > 0) { this.closeDialog(); + const localize = await this.hass.loadBackendTranslation( + "title", + integration.name + ); showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.single_config_entry_title" @@ -591,7 +595,7 @@ class AddIntegrationDialog extends LitElement { text: this.hass.localize( "ui.panel.config.integrations.config_flow.single_config_entry", { - integration_name: integration.name, + integration_name: domainToName(localize, integration.name), } ), }); diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 7031bc991d..876c3e59d8 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -1387,6 +1387,10 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { this._extraConfigEntries || this.configEntries ); if (entries.length > 0) { + const localize = await this.hass.loadBackendTranslation( + "title", + this._manifest.name + ); await showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.single_config_entry_title" @@ -1394,7 +1398,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { text: this.hass.localize( "ui.panel.config.integrations.config_flow.single_config_entry", { - integration_name: this._manifest.name, + integration_name: domainToName(localize, this._manifest.name), } ), }); diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index af99da864d..064bdfcbe2 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -744,6 +744,10 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { if (integration.single_config_entry) { const configEntries = await getConfigEntries(this.hass, { domain }); if (configEntries.length > 0) { + const localize = await this.hass.loadBackendTranslation( + "title", + integration.name + ); showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.integrations.config_flow.single_config_entry_title" @@ -751,7 +755,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { text: this.hass.localize( "ui.panel.config.integrations.config_flow.single_config_entry", { - integration_name: integration.name, + integration_name: domainToName(localize, integration.name!), } ), }); diff --git a/src/panels/config/integrations/integration-panels/matter/matter-add-device/matter-add-device-google-home.ts b/src/panels/config/integrations/integration-panels/matter/matter-add-device/matter-add-device-google-home.ts index 3f1834ea1c..85193fd24a 100644 --- a/src/panels/config/integrations/integration-panels/matter/matter-add-device/matter-add-device-google-home.ts +++ b/src/panels/config/integrations/integration-panels/matter/matter-add-device/matter-add-device-google-home.ts @@ -44,7 +44,6 @@ class MatterAddDeviceGoogleHome extends LitElement { home_assistant: html`Home Assistant`, } )} -
+
  • + ${this.hass.localize( + `ui.dialogs.matter-add-device.google_home.redirect` + )} +

  • -

    - ${this.hass.localize( - `ui.dialogs.matter-add-device.google_home.redirect` - )} -

    `; } diff --git a/src/panels/config/logs/dialog-download-logs.ts b/src/panels/config/logs/dialog-download-logs.ts index e4726e0528..fdd67e5a31 100644 --- a/src/panels/config/logs/dialog-download-logs.ts +++ b/src/panels/config/logs/dialog-download-logs.ts @@ -14,10 +14,7 @@ import type { DownloadLogsDialogParams } from "./show-dialog-download-logs"; import "../../../components/ha-select"; import "../../../components/ha-list-item"; import { stopPropagation } from "../../../common/dom/stop_propagation"; -import { - getHassioLogDownloadLinesUrl, - getHassioLogBootDownloadLinesUrl, -} from "../../../data/hassio/supervisor"; +import { getHassioLogDownloadLinesUrl } from "../../../data/hassio/supervisor"; import { getSignedPath } from "../../../data/auth"; import { fileDownload } from "../../../util/file_download"; @@ -115,7 +112,7 @@ class DownloadLogsDialog extends LitElement { const boot = this._dialogParams!.boot; const timeString = new Date().toISOString().replace(/:/g, "-"); - const downloadUrl = this._getDownloadUrlFunction()( + const downloadUrl = getHassioLogDownloadLinesUrl( provider, this._lineCount, boot @@ -129,13 +126,6 @@ class DownloadLogsDialog extends LitElement { this.closeDialog(); } - private _getDownloadUrlFunction() { - if (this._dialogParams!.boot === 0) { - return getHassioLogDownloadLinesUrl; - } - return getHassioLogBootDownloadLinesUrl; - } - private _setNumberOfLogs(ev) { this._lineCount = Number(ev.target.value); } diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index 12994fe1e2..259a87ca7d 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -39,7 +39,6 @@ import { extractApiErrorMessage } from "../../../data/hassio/common"; import { fetchHassioBoots, fetchHassioLogs, - fetchHassioLogsBootFollow, fetchHassioLogsFollow, getHassioLogDownloadUrl, } from "../../../data/hassio/supervisor"; @@ -379,7 +378,7 @@ class ErrorLogCard extends LitElement { isComponentLoaded(this.hass, "hassio") && this.provider ) { - const response = await this._fetchLogsFunction()( + const response = await fetchHassioLogsFollow( this.hass, this.provider, this._logStreamAborter.signal, @@ -469,13 +468,6 @@ class ErrorLogCard extends LitElement { } } - private _fetchLogsFunction = () => { - if (this._boot === 0) { - return fetchHassioLogsFollow; - } - return fetchHassioLogsBootFollow; - }; - private _debounceSearch = debounce(() => { this._noSearchResults = !this._ansiToHtmlElement?.filterLines(this.filter); diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index d387f2a0db..a3eaaea6a4 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -681,8 +681,12 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { private async _duplicate() { const result = this._readOnly ? await showConfirmationDialog(this, { - title: "Migrate script?", - text: "You can migrate this script, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old script from your configuration. Do you want to migrate this script?", + title: this.hass.localize( + "ui.panel.config.script.picker.migrate_script" + ), + text: this.hass.localize( + "ui.panel.config.script.picker.migrate_script_description" + ), }) : await this.confirmUnsavedChanged(); if (result) { diff --git a/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts b/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts index 28a446d62a..8db4e6b44a 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts @@ -59,18 +59,12 @@ export class HuiEnergyDateSelectionCard static get styles(): CSSResultGroup { return css` - :host { ha-card { height: 100%; display: flex; flex-direction: column; justify-content: center; } - .padded { - padding-left: 16px !important; - padding-inline-start: 16px !important; - padding-inline-end: initial !important; - } `; } } diff --git a/src/panels/lovelace/cards/hui-horizontal-stack-card.ts b/src/panels/lovelace/cards/hui-horizontal-stack-card.ts index 4e41f9a5c0..aec415dd26 100644 --- a/src/panels/lovelace/cards/hui-horizontal-stack-card.ts +++ b/src/panels/lovelace/cards/hui-horizontal-stack-card.ts @@ -28,6 +28,7 @@ export class HuiHorizontalStackCard extends HuiStackCard { css` #root { display: flex; + height: 100%; gap: var(--horizontal-stack-card-gap, var(--stack-card-gap, 8px)); } #root > hui-card { diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts index c3aa69cc8c..e013a4fbbf 100644 --- a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts @@ -64,6 +64,7 @@ const cardConfigStruct = assign( hours_to_show: optional(number()), geo_location_sources: optional(array(geoSourcesConfigStruct)), auto_fit: optional(boolean()), + fit_zones: optional(boolean()), theme_mode: optional(string()), }) ); diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts index fe4236bea0..d311a4d073 100644 --- a/src/panels/lovelace/sections/hui-grid-section.ts +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -242,8 +242,9 @@ export class GridSection extends LitElement implements LovelaceSectionElement { min-height: var(--row-height); } - .container.edit-mode:not(.import-only) { - border-start-end-radius: 0px; + .container.import-only { + border: none; + padding: 0 !important; } .card { diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index 492fe8da29..8dff28e127 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -1,5 +1,11 @@ import { ResizeController } from "@lit-labs/observers/resize-controller"; -import { mdiDelete, mdiDrag, mdiPencil, mdiViewGridPlus } from "@mdi/js"; +import { + mdiDelete, + mdiDrag, + mdiEyeOff, + mdiPencil, + mdiViewGridPlus, +} from "@mdi/js"; import type { CSSResultGroup, PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -245,6 +251,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {

    + ${this.hass.localize( "ui.panel.lovelace.editor.section.imported_cards_title" )} @@ -480,9 +487,9 @@ export class SectionsView extends LitElement implements LovelaceViewElement { } .imported-card-header { - margin-top: 24px; - padding: 16px 8px; - border-top: 2px dashed var(--divider-color); + margin-top: 36px; + padding: 32px 0 16px 0; + border-top: 4px dotted var(--divider-color); } .imported-card-header .title { @@ -491,6 +498,11 @@ export class SectionsView extends LitElement implements LovelaceViewElement { font-size: 16px; font-weight: 400; line-height: 24px; + --mdc-icon-size: 18px; + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 8px; } .imported-card-header .subtitle { margin: 0; diff --git a/src/translations/en.json b/src/translations/en.json index 0fc56d82f1..80c7faebff 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1745,8 +1745,8 @@ "answer_generic": "Other controllers" }, "google_home": { - "header": "Link Matter app", - "step_1": "Find your device in Google Home. Tap the gear icon to open the device settings.", + "header": "Share from Google Home", + "step_1": "Find your device in the Google Home app. Tap the gear icon to open the device settings.", "step_2": "Tap {linked_matter_apps_services}.", "step_3": "Tap {link_apps_services} and choose {home_assistant} from the list.", "linked_matter_apps_services": "Linked Matter apps and services", @@ -1776,8 +1776,8 @@ "code_instructions": "Paste the code you just received from the other controller." }, "generic": { - "header": "Copy setup code", - "code_instructions": "Search for the sharing mode in the app of your controller, and activate it. You will get a sharing code, enter that below.", + "header": "Enter setup code", + "code_instructions": "Search for the sharing mode in the app of your controller, and activate it. You will get a setup code, enter that below.", "setup_code": "Setup code" } } @@ -2792,7 +2792,9 @@ }, "empty_header": "Start automating", "empty_text_1": "Automations make Home Assistant automatically respond to things happening in and around your home.", - "empty_text_2": "Automations connect triggers to actions in a ''when trigger then action'' fashion with optional conditions. For example: ''When the sun sets and if {user} is home, then turn on the lights''." + "empty_text_2": "Automations connect triggers to actions in a ''when trigger then action'' fashion with optional conditions. For example: ''When the sun sets and if {user} is home, then turn on the lights''.", + "migrate_automation": "Migrate automation?", + "migrate_automation_description": "You can migrate this automation, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old automation from your configuration. Do you want to migrate this automation?" }, "dialog_new": { "header": "Create automation", @@ -3215,7 +3217,7 @@ "description": { "picker": "If an entity (or attribute) is in a specific state.", "no_entity": "Confirm state", - "full": "If{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n zero {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n zero {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }" + "full": "If{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n =0 {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n =0 {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }" } }, "sun": { @@ -3676,7 +3678,9 @@ "duplicate": "[%key:ui::common::duplicate%]", "empty_header": "Create your first script", "empty_text": "A script is a sequence of actions that can be run from a dashboard, an automation, or be triggered by voice. For example, a ''Wake-up routine''' script that gradually turns on the light in the bedroom and opens the blinds after a delay.", - "search": "Search {number} scripts" + "search": "Search {number} scripts", + "migrate_script": "Migrate script?", + "migrate_script_description": "You can migrate this script, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old script from your configuration. Do you want to migrate this script?" }, "dialog_new": { "header": "Create script", @@ -4115,7 +4119,7 @@ "hidden": "Hidden" }, "confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?", - "confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to update them yourself to use the new entity IDs!", + "confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to manually edit them yourself to use the new entity IDs!", "confirm_rename_entity_will_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will be renamed", "confirm_rename_new": "New", "confirm_rename_old": "Old", @@ -5306,7 +5310,7 @@ "share": "Share" }, "mount_type": { - "nfs": "Network file share (NFS)", + "nfs": "Network File System (NFS)", "cifs": "Samba/Windows (CIFS)" }, "cifs_versions": { @@ -5884,7 +5888,7 @@ }, "entities": { "name": "Entities", - "show_header_toggle": "Show header toggle?", + "show_header_toggle": "Show header toggle", "toggle": "Toggle entities.", "description": "The Entities card is the most common type of card. It groups items together into lists.", "special_row": "special row", @@ -5931,9 +5935,9 @@ }, "gauge": { "name": "Gauge", - "needle_gauge": "Display as needle gauge?", + "needle_gauge": "Display as needle gauge", "severity": { - "define": "Define severity?", + "define": "Define severity", "green": "Green", "red": "Red", "yellow": "Yellow" @@ -6067,7 +6071,7 @@ "state": "State", "secondary_info_attribute": "Secondary info attribute", "search": "Search", - "state_color": "Color icons based on state?", + "state_color": "Show state color", "suggested_cards": "Suggested cards", "other_cards": "Other cards", "custom_cards": "Custom cards", @@ -6104,7 +6108,6 @@ "name": "Map", "geo_location_sources": "Geolocation sources", "no_geo_location_sources": "No geolocation sources available", - "dark_mode": "Dark mode?", "appearance": "Appearance", "theme_mode": "Theme Mode", "theme_modes": { @@ -7203,6 +7206,7 @@ }, "create_account": "Create account", "error": { + "username_not_normalized": "Username can only contain lowercase letters, and can not contain whitespace.", "password_not_match": "Passwords don't match" } }, @@ -7372,6 +7376,7 @@ }, "dashboard": { "changelog": "Changelog", + "current_version": "Current version: {version}", "cpu_usage": "Add-on CPU usage", "ram_usage": "Add-on RAM usage", "hostname": "Hostname", @@ -7472,7 +7477,7 @@ }, "watchdog": { "title": "Watchdog", - "description": "This will start the add-on if it crashes" + "description": "This will restart the add-on if it crashes" }, "auto_update": { "title": "Auto update", diff --git a/yarn.lock b/yarn.lock index b6d4ed513a..190ad621bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1265,9 +1265,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/autocomplete@npm:6.18.1": - version: 6.18.1 - resolution: "@codemirror/autocomplete@npm:6.18.1" +"@codemirror/autocomplete@npm:6.18.2": + version: 6.18.2 + resolution: "@codemirror/autocomplete@npm:6.18.2" dependencies: "@codemirror/language": "npm:^6.0.0" "@codemirror/state": "npm:^6.0.0" @@ -1278,7 +1278,7 @@ __metadata: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 "@lezer/common": ^1.0.0 - checksum: 10/3b56ac6c57214e3e50c6ed79c12ac1822e3774afb033e0e4fb98dffd252f5ae64e5bed67dc2ad9cbd5d784373031be90995ddb1b36a10c16a2eef6af832041e2 + checksum: 10/35bd17afb53e8c99b1342964616f0bcc13f5f06a5d4e2d9936afdaea61742b1c20b3856d513c5d5676e3a9b6fd95e997c842467d21dfa106845e65ab1720b2f4 languageName: node linkType: hard @@ -1317,14 +1317,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/search@npm:6.5.6": - version: 6.5.6 - resolution: "@codemirror/search@npm:6.5.6" +"@codemirror/search@npm:6.5.7": + version: 6.5.7 + resolution: "@codemirror/search@npm:6.5.7" dependencies: "@codemirror/state": "npm:^6.0.0" "@codemirror/view": "npm:^6.0.0" crelt: "npm:^1.0.5" - checksum: 10/6668a34b4617e909617d3d831627d74b7a7985e8cd86d396bfcb3e86262f2310fc029fd6c846f1b8f1e6768e75985c9f1b0b18b31e05341f06b5b75c1ffde38d + checksum: 10/0a4c5e23c42231ffb829513940ee43a630585b4277fa8cc919a947f3821c9c2dc095d334bb0e4d51b3ebb50739a34a81ddbcc39ca9c1f6f935fdaa51a86661bf languageName: node linkType: hard @@ -3944,7 +3944,27 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5": +"@types/eslint-scope@npm:^3.7.7": + version: 3.7.7 + resolution: "@types/eslint-scope@npm:3.7.7" + dependencies: + "@types/eslint": "npm:*" + "@types/estree": "npm:*" + checksum: 10/e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e + languageName: node + linkType: hard + +"@types/eslint@npm:*": + version: 9.6.1 + resolution: "@types/eslint@npm:9.6.1" + dependencies: + "@types/estree": "npm:*" + "@types/json-schema": "npm:*" + checksum: 10/719fcd255760168a43d0e306ef87548e1e15bffe361d5f4022b0f266575637acc0ecb85604ac97879ee8ae83c6a6d0613b0ed31d0209ddf22a0fe6d608fc56fe + languageName: node + linkType: hard + +"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": version: 1.0.6 resolution: "@types/estree@npm:1.0.6" checksum: 10/9d35d475095199c23e05b431bcdd1f6fec7380612aed068b14b2a08aa70494de8a9026765a5a91b1073f636fb0368f6d8973f518a31391d519e20c59388ed88d @@ -4090,7 +4110,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7 @@ -5187,15 +5207,6 @@ __metadata: languageName: node linkType: hard -"acorn-import-attributes@npm:^1.9.5": - version: 1.9.5 - resolution: "acorn-import-attributes@npm:1.9.5" - peerDependencies: - acorn: ^8 - checksum: 10/8bfbfbb6e2467b9b47abb4d095df717ab64fce2525da65eabee073e85e7975fb3a176b6c8bba17c99a7d8ede283a10a590272304eb54a93c4aa1af9790d47a8b - languageName: node - linkType: hard - "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -5205,12 +5216,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.13.0 - resolution: "acorn@npm:8.13.0" +"acorn@npm:^8.14.0, acorn@npm:^8.5.0, acorn@npm:^8.8.2, acorn@npm:^8.9.0": + version: 8.14.0 + resolution: "acorn@npm:8.14.0" bin: acorn: bin/acorn - checksum: 10/33e3a03114b02b3bc5009463b3d9549b31a90ee38ebccd5e66515830a02acf62a90edcc12abfb6c9fb3837b6c17a3ec9b72b3bf52ac31d8ad8248a4af871e0f5 + checksum: 10/6df29c35556782ca9e632db461a7f97947772c6c1d5438a81f0c873a3da3a792487e83e404d1c6c25f70513e91aa18745f6eafb1fcc3a43ecd1920b21dd173d2 languageName: node linkType: hard @@ -5881,7 +5892,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.10, browserslist@npm:^4.23.3, browserslist@npm:^4.24.0": +"browserslist@npm:^4.23.3, browserslist@npm:^4.24.0": version: 4.24.0 resolution: "browserslist@npm:4.24.0" dependencies: @@ -6540,10 +6551,10 @@ __metadata: languageName: node linkType: hard -"core-js@npm:3.38.1": - version: 3.38.1 - resolution: "core-js@npm:3.38.1" - checksum: 10/3c25fdf0b2595ed37ceb305213a61e2cf26185f628455e99d1c736dda5f69e2de4de7126e6a1da136f54260c4fcc982c4215e37b5a618790a597930f854c0a37 +"core-js@npm:3.39.0": + version: 3.39.0 + resolution: "core-js@npm:3.39.0" + checksum: 10/a3d34e669783dfc878e545f1983f60d9ff48a3867cd1d7ff8839b849e053002a208c7c14a5ca354b8e0b54982901e2f83dc87c3d9b95de0a94b4071d1c74e5f6 languageName: node linkType: hard @@ -8716,11 +8727,11 @@ __metadata: "@babel/runtime": "npm:7.26.0" "@braintree/sanitize-url": "npm:7.1.0" "@bundle-stats/plugin-webpack-filter": "npm:4.16.0" - "@codemirror/autocomplete": "npm:6.18.1" + "@codemirror/autocomplete": "npm:6.18.2" "@codemirror/commands": "npm:6.7.1" "@codemirror/language": "npm:6.10.3" "@codemirror/legacy-modes": "npm:6.4.1" - "@codemirror/search": "npm:6.5.6" + "@codemirror/search": "npm:6.5.7" "@codemirror/state": "npm:6.4.1" "@codemirror/view": "npm:6.34.1" "@egjs/hammerjs": "npm:2.0.17" @@ -8827,7 +8838,7 @@ __metadata: chart.js: "npm:4.4.6" color-name: "npm:2.0.0" comlink: "npm:4.4.1" - core-js: "npm:3.38.1" + core-js: "npm:3.39.0" cropperjs: "npm:1.6.2" date-fns: "npm:4.1.0" date-fns-tz: "npm:3.2.0" @@ -8877,7 +8888,7 @@ __metadata: map-stream: "npm:0.0.7" marked: "npm:14.1.3" memoize-one: "npm:6.0.0" - mocha: "npm:10.7.3" + mocha: "npm:10.8.2" node-vibrant: "npm:3.2.1-alpha.1" object-hash: "npm:3.0.0" open: "npm:10.1.0" @@ -8913,7 +8924,7 @@ __metadata: vis-network: "npm:9.1.9" vue: "npm:2.7.16" vue2-daterange-picker: "npm:0.6.8" - webpack: "npm:5.95.0" + webpack: "npm:5.96.1" webpack-cli: "npm:5.1.4" webpack-dev-server: "npm:5.1.0" webpack-manifest-plugin: "npm:5.0.0" @@ -8921,12 +8932,12 @@ __metadata: webpackbar: "npm:6.0.1" weekstart: "npm:2.0.0" workbox-build: "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch" - workbox-cacheable-response: "npm:7.1.0" - workbox-core: "npm:7.1.0" - workbox-expiration: "npm:7.1.0" - workbox-precaching: "npm:7.1.0" - workbox-routing: "npm:7.1.0" - workbox-strategies: "npm:7.1.0" + workbox-cacheable-response: "npm:7.3.0" + workbox-core: "npm:7.3.0" + workbox-expiration: "npm:7.3.0" + workbox-precaching: "npm:7.3.0" + workbox-routing: "npm:7.3.0" + workbox-strategies: "npm:7.3.0" xss: "npm:1.0.15" languageName: unknown linkType: soft @@ -10966,9 +10977,9 @@ __metadata: languageName: node linkType: hard -"mocha@npm:10.7.3": - version: 10.7.3 - resolution: "mocha@npm:10.7.3" +"mocha@npm:10.8.2": + version: 10.8.2 + resolution: "mocha@npm:10.8.2" dependencies: ansi-colors: "npm:^4.1.3" browser-stdout: "npm:^1.3.1" @@ -10993,7 +11004,7 @@ __metadata: bin: _mocha: bin/_mocha mocha: bin/mocha.js - checksum: 10/5757aeb320df2507338bfba41731070ce16d27177c5876672fff4bcc4f7b7bcf1afe6ec761bfded43a5d28032d7b797b8b905b5b44c9420203f3ee71457732c1 + checksum: 10/903bbffcb195ef9d36b27db54e3462c5486de1397289e0953735b3530397a139336c452bcf5188c663496c660d2285bbb6c7213290d36d536ad647b6145cb917 languageName: node linkType: hard @@ -14678,17 +14689,17 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.95.0": - version: 5.95.0 - resolution: "webpack@npm:5.95.0" +"webpack@npm:5.96.1": + version: 5.96.1 + resolution: "webpack@npm:5.96.1" dependencies: - "@types/estree": "npm:^1.0.5" + "@types/eslint-scope": "npm:^3.7.7" + "@types/estree": "npm:^1.0.6" "@webassemblyjs/ast": "npm:^1.12.1" "@webassemblyjs/wasm-edit": "npm:^1.12.1" "@webassemblyjs/wasm-parser": "npm:^1.12.1" - acorn: "npm:^8.7.1" - acorn-import-attributes: "npm:^1.9.5" - browserslist: "npm:^4.21.10" + acorn: "npm:^8.14.0" + browserslist: "npm:^4.24.0" chrome-trace-event: "npm:^1.0.2" enhanced-resolve: "npm:^5.17.1" es-module-lexer: "npm:^1.2.1" @@ -14710,7 +14721,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 10/0377ad3a550b041f26237c96fb55754625b0ce6bae83c1c2447e3262ad056b0b0ad770dcbb92b59f188e9a2bd56155ce910add17dcf023cfbe78bdec774380c1 + checksum: 10/d3419ffd198252e1d0301bd0c072cee93172f3e47937c745aa8202691d2f5d529d4ba4a1965d1450ad89a1bcd3c1f70ae09e57232b0d01dd38d69c1060e964d5 languageName: node linkType: hard @@ -14990,6 +15001,15 @@ __metadata: languageName: node linkType: hard +"workbox-cacheable-response@npm:7.3.0": + version: 7.3.0 + resolution: "workbox-cacheable-response@npm:7.3.0" + dependencies: + workbox-core: "npm:7.3.0" + checksum: 10/44cd7bc26e509ca96b1b84e3ff5964296efa645853f114f39789d21c0a214ca5fc047259910b303e220bb4052155cddc5639993fcee076fac496b4895ff17a15 + languageName: node + linkType: hard + "workbox-core@npm:7.1.0": version: 7.1.0 resolution: "workbox-core@npm:7.1.0" @@ -14997,6 +15017,13 @@ __metadata: languageName: node linkType: hard +"workbox-core@npm:7.3.0": + version: 7.3.0 + resolution: "workbox-core@npm:7.3.0" + checksum: 10/228fb7018a0568c329e21d47d84980f93ebfef9b1eb3f40ddc3516ca6ae58d51dc7ca4dddc829332775b59a3079e62d105c5e1c5c312805d177b963f8bf54393 + languageName: node + linkType: hard + "workbox-expiration@npm:7.1.0": version: 7.1.0 resolution: "workbox-expiration@npm:7.1.0" @@ -15007,6 +15034,16 @@ __metadata: languageName: node linkType: hard +"workbox-expiration@npm:7.3.0": + version: 7.3.0 + resolution: "workbox-expiration@npm:7.3.0" + dependencies: + idb: "npm:^7.0.1" + workbox-core: "npm:7.3.0" + checksum: 10/83e021d700e521a65a89907679d1a580aacc0419428286910ec7c6b0a538326f71f05566434f666ebf6c9fbe819ef3ea81428df1d868f9ea92527afe5d11152d + languageName: node + linkType: hard + "workbox-google-analytics@npm:7.1.0": version: 7.1.0 resolution: "workbox-google-analytics@npm:7.1.0" @@ -15039,6 +15076,17 @@ __metadata: languageName: node linkType: hard +"workbox-precaching@npm:7.3.0": + version: 7.3.0 + resolution: "workbox-precaching@npm:7.3.0" + dependencies: + workbox-core: "npm:7.3.0" + workbox-routing: "npm:7.3.0" + workbox-strategies: "npm:7.3.0" + checksum: 10/d14135c471a45de36438c40eed7cb7157cdb336d4216a775486c6307d1ac316794d64231c2e2d0a4c313bb4a4fec623ab77e391cc458b4f2afa64e2487acb2e8 + languageName: node + linkType: hard + "workbox-range-requests@npm:7.1.0": version: 7.1.0 resolution: "workbox-range-requests@npm:7.1.0" @@ -15071,6 +15119,15 @@ __metadata: languageName: node linkType: hard +"workbox-routing@npm:7.3.0": + version: 7.3.0 + resolution: "workbox-routing@npm:7.3.0" + dependencies: + workbox-core: "npm:7.3.0" + checksum: 10/0d729f9c5cfc5754404ac1f7b729c7740ddc806203792701ac642151fbec939b4aa0fb289eab2295e49180e8154ad9bb1380effb7e0f0362163b79db4291dba7 + languageName: node + linkType: hard + "workbox-strategies@npm:7.1.0": version: 7.1.0 resolution: "workbox-strategies@npm:7.1.0" @@ -15080,6 +15137,15 @@ __metadata: languageName: node linkType: hard +"workbox-strategies@npm:7.3.0": + version: 7.3.0 + resolution: "workbox-strategies@npm:7.3.0" + dependencies: + workbox-core: "npm:7.3.0" + checksum: 10/61ba672075ef8aaa70ad9221460dab80a7d8920e324e14137460f26ebe8b137e5589fb75c664e0efeaf4402e3d8435a9b1818f9a9c61f88863c0e0315af337e7 + languageName: node + linkType: hard + "workbox-streams@npm:7.1.0": version: 7.1.0 resolution: "workbox-streams@npm:7.1.0"