From 9fdef3df6d982b8ba16a1986ad8ef7ec69917c82 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 13 Dec 2022 23:10:57 -0500 Subject: [PATCH 01/70] Update conversation API (#14763) * Update conversation API * Update action done * Add query done data * Update conversation_id type --- src/data/conversation.ts | 57 +++++++++++++++++-- .../ha-voice-command-dialog.ts | 12 ++-- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/data/conversation.ts b/src/data/conversation.ts index 84ed420ff5..4e7a504f11 100644 --- a/src/data/conversation.ts +++ b/src/data/conversation.ts @@ -1,23 +1,68 @@ import { HomeAssistant } from "../types"; -interface ProcessResults { - card: { [key: string]: Record }; - speech: { - [SpeechType in "plain" | "ssml"]: { extra_data: any; speech: string }; +interface IntentTarget { + type: "area" | "device" | "entity" | "domain" | "device_class" | "custom"; + name: string; + id: string | null; +} + +interface IntentResultBase { + language: string; + speech: + | { + [SpeechType in "plain" | "ssml"]: { extra_data: any; speech: string }; + } + | null; +} + +interface IntentResultActionDone extends IntentResultBase { + response_type: "action_done"; + data: { + targets: IntentTarget[]; + success: IntentTarget[]; + failed: IntentTarget[]; }; } +interface IntentResultQueryAnswer extends IntentResultBase { + response_type: "query_answer"; + data: { + targets: IntentTarget[]; + success: IntentTarget[]; + failed: IntentTarget[]; + }; +} + +interface IntentResultError extends IntentResultBase { + response_type: "error"; + data: { + code: + | "no_intent_match" + | "no_valid_targets" + | "failed_to_handle" + | "unknown"; + }; +} + +interface ConversationResult { + conversation_id: string | null; + response: + | IntentResultActionDone + | IntentResultQueryAnswer + | IntentResultError; +} + export interface AgentInfo { attribution?: { name: string; url: string }; onboarding?: { text: string; url: string }; } -export const processText = ( +export const processConversationInput = ( hass: HomeAssistant, text: string, // eslint-disable-next-line: variable-name conversation_id: string -): Promise => +): Promise => hass.callWS({ type: "conversation/process", text, 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 64f9d0900c..49c998bcb4 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -22,7 +22,7 @@ import type { HaTextField } from "../../components/ha-textfield"; import { AgentInfo, getAgentInfo, - processText, + processConversationInput, setConversationOnboarding, } from "../../data/conversation"; import { haStyleDialog } from "../../resources/styles"; @@ -274,13 +274,17 @@ export class HaVoiceCommandDialog extends LitElement { // To make sure the answer is placed at the right user text, we add it before we process it this._addMessage(message); try { - const response = await processText( + const response = await processConversationInput( this.hass, text, this._conversationId! ); - const plain = response.speech.plain; - message.text = plain.speech; + const plain = response.response.speech?.plain; + if (plain) { + message.text = plain.speech; + } else { + message.text = ""; + } this.requestUpdate("_conversation"); } catch { From 2176d4dceae888f32b510f611c867648e7c59368 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 14 Dec 2022 09:13:31 +0100 Subject: [PATCH 02/70] Make editing home location more clear (#14636) --- src/components/map/ha-locations-editor.ts | 15 ++++++++++----- .../config/core/ha-config-section-general.ts | 2 +- src/panels/config/zone/ha-config-zone.ts | 19 ++++++++++++++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/components/map/ha-locations-editor.ts b/src/components/map/ha-locations-editor.ts index 43f68553a7..9995094c95 100644 --- a/src/components/map/ha-locations-editor.ts +++ b/src/components/map/ha-locations-editor.ts @@ -67,23 +67,28 @@ export class HaLocationsEditor extends LitElement { private Leaflet?: LeafletModuleType; + private _loadPromise: Promise; + constructor() { super(); - import("leaflet").then((module) => { + this._loadPromise = import("leaflet").then((module) => import("leaflet-draw").then(() => { this.Leaflet = module.default as LeafletModuleType; this._updateMarkers(); - this.updateComplete.then(() => this.fitMap()); - }); - }); + return this.updateComplete.then(() => this.fitMap()); + }) + ); } public fitMap(): void { this.map.fitMap(); } - public fitMarker(id: string): void { + public async fitMarker(id: string): Promise { + if (!this.Leaflet) { + await this._loadPromise; + } if (!this.map.leafletMap || !this._locationMarkers) { return; } diff --git a/src/panels/config/core/ha-config-section-general.ts b/src/panels/config/core/ha-config-section-general.ts index 635c455d79..10d9ffe10f 100644 --- a/src/panels/config/core/ha-config-section-general.ts +++ b/src/panels/config/core/ha-config-section-general.ts @@ -391,7 +391,7 @@ class HaConfigSectionGeneral extends LitElement { ); private _editLocation() { - navigate("/config/zone"); + navigate("/config/zone/edit/zone.home"); } static styles = [ diff --git a/src/panels/config/zone/ha-config-zone.ts b/src/panels/config/zone/ha-config-zone.ts index 1f90d1e312..7089e7735a 100644 --- a/src/panels/config/zone/ha-config-zone.ts +++ b/src/panels/config/zone/ha-config-zone.ts @@ -1,4 +1,4 @@ -import { mdiPencil, mdiPencilOff, mdiPlus } from "@mdi/js"; +import { mdiCog, mdiPencil, mdiPencilOff, mdiPlus } from "@mdi/js"; import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-listbox/paper-listbox"; @@ -191,7 +191,7 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { !this._canEditCore} .path=${stateObject.entity_id === "zone.home" && this._canEditCore - ? mdiPencil + ? mdiCog : mdiPencilOff} .label=${stateObject.entity_id === "zone.home" ? hass.localize("ui.panel.config.zone.edit_home") @@ -273,6 +273,19 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { } } + protected updated() { + if ( + !this.route.path.startsWith("/edit/") || + !this._stateItems || + !this._storageItems + ) { + return; + } + const id = this.route.path.slice(6); + navigate("/config/zone", { replace: true }); + this._zoomZone(id); + } + public willUpdate(changedProps: PropertyValues) { super.updated(changedProps); const oldHass = changedProps.get("hass") as HomeAssistant | undefined; @@ -374,7 +387,7 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { this._zoomZone(entityId); } - private _zoomZone(id: string) { + private async _zoomZone(id: string) { this._map?.fitMarker(id); } From 6aba5c1017a6a4d6cf91ab79ba275235aee80d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 14 Dec 2022 09:37:16 +0100 Subject: [PATCH 03/70] Add action to publish demo when pushing to master (#14723) --- .github/workflows/demo_deployment.yaml | 47 ++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/.github/workflows/demo_deployment.yaml b/.github/workflows/demo_deployment.yaml index 24338b948c..dc59829095 100644 --- a/.github/workflows/demo_deployment.yaml +++ b/.github/workflows/demo_deployment.yaml @@ -7,20 +7,25 @@ on: push: branches: - dev + - master env: NODE_VERSION: 16 NODE_OPTIONS: --max_old_space_size=6144 jobs: - deploy: + deploy_dev: runs-on: ubuntu-latest + name: Demo Development + if: github.event_name != 'push' || github.ref != 'master' environment: - name: Demo + name: Demo Development url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub uses: actions/checkout@v3 + with: + ref: dev - name: Set up Node ${{ env.NODE_VERSION }} uses: actions/setup-node@v3 @@ -46,3 +51,41 @@ jobs: env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }} + + deploy_master: + runs-on: ubuntu-latest + name: Demo Production + if: github.event_name == 'push' && github.ref == 'master' + environment: + name: Demo Production + url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} + steps: + - name: Check out files from GitHub + uses: actions/checkout@v3 + with: + ref: master + + - name: Set up Node ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Install dependencies + run: yarn install + env: + CI: true + + - name: Build Demo + run: ./node_modules/.bin/gulp build-demo + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Deploy to Netlify + id: deploy + uses: netlify/actions/cli@master + with: + args: deploy --dir=demo/dist --prod + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }} \ No newline at end of file From 498102ddd99f2408a5dacb02bfaed56ecf77929d Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 14 Dec 2022 09:44:43 +0100 Subject: [PATCH 04/70] Classify binary sensor locks active state as alert (= red) (#14761) fixes undefined --- src/common/entity/color/binary_sensor_color.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/entity/color/binary_sensor_color.ts b/src/common/entity/color/binary_sensor_color.ts index f3458214fd..90f7247be4 100644 --- a/src/common/entity/color/binary_sensor_color.ts +++ b/src/common/entity/color/binary_sensor_color.ts @@ -6,6 +6,7 @@ const ALERTING_DEVICE_CLASSES = new Set([ "carbon_monoxide", "gas", "heat", + "lock", "moisture", "problem", "safety", From 02b763e8f3ee2217656ffb8a35fe2cdef567f4d4 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 14 Dec 2022 10:17:34 +0100 Subject: [PATCH 05/70] Add snow weather icon SVG class (#14655) fixes undefined --- src/data/weather.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/data/weather.ts b/src/data/weather.ts index 6c2fb20fcb..fe64a4ff8a 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -319,6 +319,12 @@ export const weatherSVGStyles = css` .cloud-front { fill: var(--weather-icon-cloud-front-color, #f9f9f9); } + .snow { + fill: var(--weather-icon-snow-color, #f9f9f9); + stroke: var(--weather-icon-snow-stroke-color, #d4d4d4); + stroke-width: 1; + paint-order: stroke; + } `; const getWeatherStateSVG = ( @@ -434,15 +440,15 @@ const getWeatherStateSVG = ( snowyStates.has(state) ? svg` ` From e151520d7435424ea660b267a30617ff4d55dd63 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 14 Dec 2022 10:17:55 +0100 Subject: [PATCH 06/70] Add stones to weight units (#14749) --- src/panels/config/entities/entity-registry-settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/entities/entity-registry-settings.ts b/src/panels/config/entities/entity-registry-settings.ts index d53ed18f15..0d737237db 100644 --- a/src/panels/config/entities/entity-registry-settings.ts +++ b/src/panels/config/entities/entity-registry-settings.ts @@ -124,7 +124,7 @@ const OVERRIDE_SENSOR_UNITS = { temperature: ["°C", "°F", "K"], volume: ["fl. oz.", "ft³", "gal", "L", "mL", "m³"], water: ["ft³", "gal", "L", "m³"], - weight: ["g", "kg", "lb", "mg", "oz", "µg"], + weight: ["g", "kg", "lb", "mg", "oz", "st", "µg"], wind_speed: ["ft/s", "km/h", "kn", "mph", "m/s"], }; From b168f8d0276301c317b38fb5b640de4e9d865c81 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 14 Dec 2022 10:18:11 +0100 Subject: [PATCH 07/70] Add new data size device class (#14595) --- src/common/const.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/const.ts b/src/common/const.ts index f9ed0e8ff6..8b86ced750 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -21,6 +21,7 @@ import { mdiCommentAlert, mdiCounter, mdiCurrentAc, + mdiDatabase, mdiEye, mdiFan, mdiFlash, @@ -133,6 +134,7 @@ export const FIXED_DEVICE_CLASS_ICONS = { carbon_dioxide: mdiMoleculeCo2, carbon_monoxide: mdiMoleculeCo, current: mdiCurrentAc, + data_size: mdiDatabase, date: mdiCalendar, distance: mdiArrowLeftRight, duration: mdiProgressClock, From 14513e59057fb53ad81cb73cdaf2d31ef4b220b4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 14 Dec 2022 10:18:55 +0100 Subject: [PATCH 08/70] Add new data rate device class (#14594) Co-authored-by: Bram Kragten --- src/common/const.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/const.ts b/src/common/const.ts index 8b86ced750..983eb392d6 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -58,6 +58,7 @@ import { mdiThermometerLines, mdiThermostat, mdiTimerOutline, + mdiTransmissionTower, mdiVideo, mdiWater, mdiWaterPercent, @@ -134,6 +135,7 @@ export const FIXED_DEVICE_CLASS_ICONS = { carbon_dioxide: mdiMoleculeCo2, carbon_monoxide: mdiMoleculeCo, current: mdiCurrentAc, + data_rate: mdiTransmissionTower, data_size: mdiDatabase, date: mdiCalendar, distance: mdiArrowLeftRight, From 1bce5efc9ee32a143a19deca7134227d3cc2aade Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 14 Dec 2022 10:20:27 +0100 Subject: [PATCH 09/70] Add new sound pressure device class (#14592) * Add sound pressure device class * Update const.ts * sort Co-authored-by: Bram Kragten --- src/common/const.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/const.ts b/src/common/const.ts index 983eb392d6..a279cfd348 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -22,6 +22,7 @@ import { mdiCounter, mdiCurrentAc, mdiDatabase, + mdiEarHearing, mdiEye, mdiFan, mdiFlash, @@ -162,6 +163,7 @@ export const FIXED_DEVICE_CLASS_ICONS = { pressure: mdiGauge, reactive_power: mdiFlash, signal_strength: mdiWifi, + sound_pressure: mdiEarHearing, speed: mdiSpeedometer, sulphur_dioxide: mdiMolecule, temperature: mdiThermometer, From 363092ff03f0c49a1f8c2ac1e8da7bcc91fb46c7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 14 Dec 2022 02:25:24 -0800 Subject: [PATCH 10/70] Remove min/max >=1 requirement from gauge-card-editor (#14682) fixes undefined --- .../lovelace/editor/config-elements/hui-gauge-card-editor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts index 89c2656cf3..477b949a78 100644 --- a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts @@ -78,8 +78,8 @@ export class HuiGaugeCardEditor name: "", type: "grid", schema: [ - { name: "min", selector: { number: { min: 1, mode: "box" } } }, - { name: "max", selector: { number: { min: 1, mode: "box" } } }, + { name: "min", selector: { number: { mode: "box" } } }, + { name: "max", selector: { number: { mode: "box" } } }, ], }, { From 50cc8594befb01d9df67d8636882979a466bd8d0 Mon Sep 17 00:00:00 2001 From: Denis Shulyaka Date: Wed, 14 Dec 2022 13:34:10 +0300 Subject: [PATCH 11/70] humidifier card: fix humidity not visible (#14575) --- src/panels/lovelace/cards/hui-humidifier-card.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/cards/hui-humidifier-card.ts b/src/panels/lovelace/cards/hui-humidifier-card.ts index cab66cfc5a..ed5f3e21d3 100644 --- a/src/panels/lovelace/cards/hui-humidifier-card.ts +++ b/src/panels/lovelace/cards/hui-humidifier-card.ts @@ -93,6 +93,8 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { ? stateObj.attributes.humidity : stateObj.attributes.min_humidity; + const setHumidity = this._setHum ? this._setHum : targetHumidity; + const rtlDirection = computeRTLDirection(this.hass); const slider = UNAVAILABLE_STATES.includes(stateObj.state) @@ -114,11 +116,11 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { ${UNAVAILABLE_STATES.includes(stateObj.state) || - this._setHum === undefined || - this._setHum === null + setHumidity === undefined || + setHumidity === null ? "" : svg` - ${this._setHum.toFixed()} + ${setHumidity.toFixed()} % From e768c78dce0bb4729b6b7a587fefcbbdfe1f21b6 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 14 Dec 2022 11:35:25 +0100 Subject: [PATCH 12/70] Enable weather entity row to show secondary info (#14639) --- .../entity-rows/hui-weather-entity-row.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts index d8c53fa802..54d592670c 100644 --- a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts @@ -69,6 +69,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow { this._config.tap_action && this._config.tap_action.action !== "none" ); + const hasSecondary = this._config.secondary_info; const weatherStateIcon = getWeatherStateIcon(stateObj.state, this); return html` @@ -94,6 +95,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
${this._config.name || computeStateName(stateObj)} + ${hasSecondary + ? html` +
+ ${this._config.secondary_info === "entity-id" + ? stateObj.entity_id + : this._config.secondary_info === "last-changed" + ? html` + + ` + : this._config.secondary_info === "last-updated" + ? html` + + ` + : ""} +
+ ` + : ""}
Date: Wed, 14 Dec 2022 18:38:20 +0800 Subject: [PATCH 14/70] Enable http cache for local media-player-browse thumbnails (#13339) --- .../media-player/ha-media-player-browse.ts | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index da98917f43..b27f48385d 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -27,7 +27,6 @@ import { until } from "lit/directives/until"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; -import { getSignedPath } from "../../data/auth"; import { UNAVAILABLE_STATES } from "../../data/entity"; import type { MediaPlayerItem } from "../../data/media-player"; import { @@ -339,7 +338,7 @@ export class HaMediaPlayerBrowse extends LitElement { : MediaClassBrowserSettings.directory; const backgroundImage = currentItem.thumbnail - ? this._getSignedThumbnail(currentItem.thumbnail).then( + ? this._getThumbnailURLorBase64(currentItem.thumbnail).then( (value) => `url(${value})` ) : "none"; @@ -550,7 +549,7 @@ export class HaMediaPlayerBrowse extends LitElement { private _renderGridItem = (child: MediaPlayerItem): TemplateResult => { const backgroundImage = child.thumbnail - ? this._getSignedThumbnail(child.thumbnail).then( + ? this._getThumbnailURLorBase64(child.thumbnail).then( (value) => `url(${value})` ) : "none"; @@ -615,7 +614,7 @@ export class HaMediaPlayerBrowse extends LitElement { const backgroundImage = mediaClass.show_list_images && child.thumbnail - ? this._getSignedThumbnail(child.thumbnail).then( + ? this._getThumbnailURLorBase64(child.thumbnail).then( (value) => `url(${value})` ) : "none"; @@ -652,7 +651,7 @@ export class HaMediaPlayerBrowse extends LitElement { `; }; - private async _getSignedThumbnail( + private async _getThumbnailURLorBase64( thumbnailUrl: string | undefined ): Promise { if (!thumbnailUrl) { @@ -661,7 +660,24 @@ export class HaMediaPlayerBrowse extends LitElement { if (thumbnailUrl.startsWith("/")) { // Thumbnails served by local API require authentication - return (await getSignedPath(this.hass, thumbnailUrl)).path; + return new Promise((resolve, reject) => { + this.hass + .fetchWithAuth(thumbnailUrl!) + // Since we are fetching with an authorization header, we cannot just put the + // URL directly into the document; we need to embed the image. We could do this + // using blob URLs, but then we would need to keep track of them in order to + // release them properly. Instead, we embed the thumbnail using base64. + .then((response) => response.blob()) + .then((blob) => { + const reader = new FileReader(); + reader.onload = () => { + const result = reader.result; + resolve(typeof result === "string" ? result : ""); + }; + reader.onerror = (e) => reject(e); + reader.readAsDataURL(blob); + }); + }); } if (isBrandUrl(thumbnailUrl)) { From 175a3888227cda9c170b1ef9a8e97791338a2691 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 14 Dec 2022 11:43:49 +0100 Subject: [PATCH 15/70] Add sun domain to gallery entity states (#14742) --- gallery/src/pages/misc/entity-state.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gallery/src/pages/misc/entity-state.ts b/gallery/src/pages/misc/entity-state.ts index 00218f0b4a..ae12a09abe 100644 --- a/gallery/src/pages/misc/entity-state.ts +++ b/gallery/src/pages/misc/entity-state.ts @@ -219,6 +219,11 @@ const ENTITIES: HassEntity[] = [ // Siren createEntity("siren.off", "off"), createEntity("siren.on", "on"), + // Sun + createEntity("sun.below", "below_horizon"), + createEntity("sun.above", "above_horizon"), + createEntity("sun.unknown", "unknown"), + createEntity("sun.unavailable", "unavailable"), // Switch createEntity("switch.off", "off"), createEntity("switch.on", "on"), @@ -322,7 +327,7 @@ export class DemoEntityState extends LitElement { `, }, entity_id: { - title: "Entity id", + title: "Entity ID", width: "30%", filterable: true, sortable: true, From d48853fcdd6ea8933b4fc1ba6199137649783072 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Dec 2022 06:11:58 -0500 Subject: [PATCH 16/70] Add precommit hook to deduplicate dependencies (#14609) --- lint-staged.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lint-staged.config.js b/lint-staged.config.js index a41455f949..d5f63c77bf 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -5,4 +5,5 @@ module.exports = { 'printf "%s\n" "Translation files should not be added or modified here. Instead, make the necessary modifications in src/translations/en.json. Other languages are managed externally. Please see https://developers.home-assistant.io/docs/translations/ for details." ' + files.join(" ") + " >&2 && exit 1", + "/yarn.lock": () => "yarn dedupe", }; From 66a22ae10287c258e5ef68658aeb76b6aea87795 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Dec 2022 06:13:56 -0500 Subject: [PATCH 17/70] Enable dependabot for yarn packages (#14607) --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b7b4ca2ddc..054d15b6e5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,3 +6,13 @@ updates: interval: weekly time: "06:00" open-pull-requests-limit: 10 + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + time: "06:00" + open-pull-requests-limit: 5 + commit-message: + include: scope + prefix: "prod" + prefix-development: "dev" From ef7d839c0fabd1639601c257c83f8e718ec8d5ff Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 14 Dec 2022 12:15:36 +0100 Subject: [PATCH 18/70] Use the calendar color for state icon in event details (#14670) Co-authored-by: Bram Kragten --- src/components/entity/state-badge.ts | 8 +++++++- src/components/entity/state-info.ts | 5 ++++- src/panels/calendar/dialog-calendar-event-detail.ts | 1 + src/panels/calendar/ha-full-calendar.ts | 1 + src/panels/calendar/show-dialog-calendar-event-detail.ts | 1 + 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/components/entity/state-badge.ts b/src/components/entity/state-badge.ts index 972ea11ea7..99ab8a9575 100644 --- a/src/components/entity/state-badge.ts +++ b/src/components/entity/state-badge.ts @@ -32,6 +32,8 @@ export class StateBadge extends LitElement { @property({ type: Boolean }) public stateColor?: boolean; + @property() public color?: string; + @property({ type: Boolean, reflect: true, attribute: "icon" }) private _showIcon = true; @@ -75,7 +77,8 @@ export class StateBadge extends LitElement { !changedProps.has("stateObj") && !changedProps.has("overrideImage") && !changedProps.has("overrideIcon") && - !changedProps.has("stateColor") + !changedProps.has("stateColor") && + !changedProps.has("color") ) { return; } @@ -106,6 +109,9 @@ export class StateBadge extends LitElement { } hostStyle.backgroundImage = `url(${imageUrl})`; this._showIcon = false; + } else if (this.color) { + // Externally provided overriding color wins over state color + iconStyle.color = this.color; } else if (this._stateColor && stateActive(stateObj)) { const color = stateColorCss(stateObj); if (color) { diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts index 01f95f88bb..7ee6c6ba39 100644 --- a/src/components/entity/state-info.ts +++ b/src/components/entity/state-info.ts @@ -19,6 +19,8 @@ class StateInfo extends LitElement { // property used only in CSS @property({ type: Boolean, reflect: true }) public rtl = false; + @property() public color?: string; + protected render(): TemplateResult { if (!this.hass || !this.stateObj) { return html``; @@ -26,9 +28,10 @@ class StateInfo extends LitElement { const name = computeStateName(this.stateObj); - return html`
diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index ebe36d920c..adb732c08e 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -101,6 +101,7 @@ class DialogCalendarEventDetail extends LitElement {
diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index d3007ba5e6..f913ba78d0 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -312,6 +312,7 @@ export class HAFullCalendar extends LitElement { calendars: this.calendars, calendarId: info.event.extendedProps.calendar, entry: info.event.extendedProps.eventData, + color: info.event.backgroundColor, updated: () => { this._fireViewChanged(); }, diff --git a/src/panels/calendar/show-dialog-calendar-event-detail.ts b/src/panels/calendar/show-dialog-calendar-event-detail.ts index 9946ed9bdb..05306294b4 100644 --- a/src/panels/calendar/show-dialog-calendar-event-detail.ts +++ b/src/panels/calendar/show-dialog-calendar-event-detail.ts @@ -8,6 +8,7 @@ export interface CalendarEventDetailDialogParams { canDelete?: boolean; canEdit?: boolean; updated: () => void; + color?: string; } export const loadCalendarEventDetailDialog = () => From 00c2cb731b790c90f364408ea7ac5a5f7b237932 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Dec 2022 06:16:45 -0500 Subject: [PATCH 19/70] Remove unnecessary labels from dashboard menu (#14605) --- src/panels/lovelace/hui-root.ts | 62 ++++++++------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index d735132ca1..9637884550 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -174,9 +174,6 @@ class HUIRoot extends LitElement { : html` - ${this.hass!.localize( - "ui.panel.lovelace.menu.search" - )} + ${this.hass!.localize( + "ui.panel.lovelace.menu.search" + )} + - ${this.hass!.localize( - "ui.panel.lovelace.menu.start_conversation" - )} + ${this.hass!.localize( + "ui.panel.lovelace.menu.start_conversation" + )} + - ${this.hass!.localize( - "ui.common.refresh" - )} + ${this.hass!.localize("ui.common.refresh")} + - ${this.hass!.localize( - "ui.panel.lovelace.unused_entities.title" - )} + ${this.hass!.localize( + "ui.panel.lovelace.unused_entities.title" + )} + @@ -432,9 +408,6 @@ class HUIRoot extends LitElement { ? html` @@ -459,12 +432,7 @@ class HUIRoot extends LitElement { class="menu-link" target="_blank" > - + ${this.hass!.localize( "ui.panel.lovelace.menu.help" )} From 239d3ca00c549ac8cd0bcc04d09b2d0a4d9668b0 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 14 Dec 2022 16:42:11 +0100 Subject: [PATCH 20/70] Add QoS option for MQTT subscribe (#14565) Co-authored-by: Bram Kragten --- src/data/mqtt.ts | 4 +- .../mqtt/mqtt-subscribe-card.ts | 48 ++++++++++++++----- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/data/mqtt.ts b/src/data/mqtt.ts index 2cc710fd51..151097bdd6 100644 --- a/src/data/mqtt.ts +++ b/src/data/mqtt.ts @@ -37,11 +37,13 @@ export interface MQTTDeviceDebugInfo { export const subscribeMQTTTopic = ( hass: HomeAssistant, topic: string, - callback: (message: MQTTMessage) => void + callback: (message: MQTTMessage) => void, + qos?: number ) => hass.connection.subscribeMessage(callback, { type: "mqtt/subscribe", topic, + qos, }); export const fetchMQTTDebugInfo = ( diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts index 0602b4b698..037cbfef23 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts @@ -1,17 +1,26 @@ import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { formatTime } from "../../../../../common/datetime/format_time"; import "../../../../../components/ha-card"; +import "../../../../../components/ha-select"; +import "../../../../../components/ha-textfield"; +import { formatTime } from "../../../../../common/datetime/format_time"; import { MQTTMessage, subscribeMQTTTopic } from "../../../../../data/mqtt"; import { HomeAssistant } from "../../../../../types"; +import "@material/mwc-list/mwc-list-item"; +import { LocalStorage } from "../../../../../common/decorators/local-storage"; + +const qosLevel = ["0", "1", "2"]; @customElement("mqtt-subscribe-card") class MqttSubscribeCard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private _topic = ""; + @LocalStorage("panel-dev-mqtt-topic-subscribe", true, false) + private _topic = ""; + + @LocalStorage("panel-dev-mqtt-qos-subscribe", true, false) + private _qos = "0"; @state() private _subscribed?: () => void; @@ -38,14 +47,23 @@ class MqttSubscribeCard extends LitElement { header=${this.hass.localize("ui.panel.config.mqtt.description_listen")} >
- + @change=${this._handleTopic} + > + ${qosLevel.map( + (qos) => html`${qos}` + )} + = 0 && newValue !== this._qos) { + this._qos = newValue; + } } private async _handleSubmit(): Promise { @@ -94,7 +119,8 @@ class MqttSubscribeCard extends LitElement { this._subscribed = await subscribeMQTTTopic( this.hass!, this._topic, - (message) => this._handleMessage(message) + (message) => this._handleMessage(message), + parseInt(this._qos) ); } } @@ -125,10 +151,6 @@ class MqttSubscribeCard extends LitElement { display: block; padding: 16px; } - paper-input { - display: inline-block; - width: 200px; - } .events { margin: -16px 0; padding: 0 16px; From 1044b3c3992e4e44a0e33bfa3ecfb2027e2a05a5 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 14 Dec 2022 16:42:44 +0100 Subject: [PATCH 21/70] Add retain switch for MQTT publish (#14714) --- .../mqtt/mqtt-config-panel.ts | 53 +++++++++---------- src/translations/en.json | 3 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts index c5eab6b79e..a60764a184 100644 --- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-config-panel.ts @@ -1,8 +1,11 @@ import "@material/mwc-button"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; +import { LocalStorage } from "../../../../../common/decorators/local-storage"; import "../../../../../components/ha-card"; import "../../../../../components/ha-code-editor"; +import "../../../../../components/ha-formfield"; +import "../../../../../components/ha-switch"; import { getConfigEntries } from "../../../../../data/config_entries"; import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow"; import "../../../../../layouts/hass-subpage"; @@ -18,26 +21,17 @@ class HaPanelDevMqtt extends LitElement { @property({ type: Boolean }) public narrow!: boolean; - @state() private topic = ""; + @LocalStorage("panel-dev-mqtt-topic-ls", true, false) + private topic = ""; - @state() private payload = ""; + @LocalStorage("panel-dev-mqtt-payload-ls", true, false) + private payload = ""; - @state() private qos = "0"; + @LocalStorage("panel-dev-mqtt-qos-ls", true, false) + private qos = "0"; - private inited = false; - - protected firstUpdated() { - if (localStorage && localStorage["panel-dev-mqtt-topic"]) { - this.topic = localStorage["panel-dev-mqtt-topic"]; - } - if (localStorage && localStorage["panel-dev-mqtt-payload"]) { - this.payload = localStorage["panel-dev-mqtt-payload"]; - } - if (localStorage && localStorage["panel-dev-mqtt-qos"]) { - this.qos = localStorage["panel-dev-mqtt-qos"]; - } - this.inited = true; - } + @LocalStorage("panel-dev-mqtt-retain-ls", true, false) + private retain = false; protected render(): TemplateResult { return html` @@ -70,7 +64,14 @@ class HaPanelDevMqtt extends LitElement { html`${qos}` )} - + + +

${this.hass.localize("ui.panel.config.mqtt.payload")}

= 0 && newValue !== this.qos && localStorage && this.inited) { + if (newValue >= 0 && newValue !== this.qos) { this.qos = newValue; - localStorage["panel-dev-mqtt-qos"] = this.qos; } } + private _handleRetain(ev: CustomEvent) { + this.retain = (ev.target! as any).checked; + } + private _publish(): void { if (!this.hass) { return; @@ -127,6 +125,7 @@ class HaPanelDevMqtt extends LitElement { topic: this.topic, payload_template: this.payload, qos: parseInt(this.qos), + retain: this.retain, }); } diff --git a/src/translations/en.json b/src/translations/en.json index aaa1798ded..5cc1fd033d 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3234,7 +3234,8 @@ "start_listening": "Start listening", "stop_listening": "Stop listening", "message_received": "Message {id} received on {topic} at {time}:", - "qos": "QoS" + "qos": "QoS", + "retain": "Retain" }, "zha": { "common": { From 7611a99f55b47c0f8f0cd62e44d5c80d220ccc9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 16:47:24 +0100 Subject: [PATCH 22/70] dev(deps-dev): bump @typescript-eslint/eslint-plugin from 5.44.0 to 5.46.1 (#14774) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 87 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 8577ac673e..64458c5032 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ "@types/sortablejs": "^1", "@types/tar": "^6", "@types/webspeechapi": "^0.0.29", - "@typescript-eslint/eslint-plugin": "^5.44.0", + "@typescript-eslint/eslint-plugin": "^5.46.1", "@typescript-eslint/parser": "^5.44.0", "@web/dev-server": "^0.0.24", "@web/dev-server-rollup": "^0.2.11", diff --git a/yarn.lock b/yarn.lock index 49872cc55b..aaf4d0a2a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4360,13 +4360,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.44.0": - version: 5.44.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.44.0" +"@typescript-eslint/eslint-plugin@npm:^5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/eslint-plugin@npm:5.46.1" dependencies: - "@typescript-eslint/scope-manager": 5.44.0 - "@typescript-eslint/type-utils": 5.44.0 - "@typescript-eslint/utils": 5.44.0 + "@typescript-eslint/scope-manager": 5.46.1 + "@typescript-eslint/type-utils": 5.46.1 + "@typescript-eslint/utils": 5.46.1 debug: ^4.3.4 ignore: ^5.2.0 natural-compare-lite: ^1.4.0 @@ -4379,7 +4379,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 88784e77e8e35ea50ca9c49d46df94cabc3447f4b332f3ca53974d3b5370cb5dcd85cc9ee0e317b91083812012369209574725dcfc3b2b4056b60371b68ca854 + checksum: 4ea4af5faa5c509113d575bf6ea1f76989f253b4839bb98bcda28450be7fc8a87788169e822b0f6d739aee67e32e824440ef00cebc294d4212ffb3fed1f0756a languageName: node linkType: hard @@ -4410,12 +4410,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.44.0": - version: 5.44.0 - resolution: "@typescript-eslint/type-utils@npm:5.44.0" +"@typescript-eslint/scope-manager@npm:5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/scope-manager@npm:5.46.1" dependencies: - "@typescript-eslint/typescript-estree": 5.44.0 - "@typescript-eslint/utils": 5.44.0 + "@typescript-eslint/types": 5.46.1 + "@typescript-eslint/visitor-keys": 5.46.1 + checksum: bf934603dc9c7da71eb26f415d13018f2a96dbba193a773bc440a5c93828365f09bb3db9be55189dfbbace414c6c48d7fad246c0d9717dab4676d0d79d6d8676 + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/type-utils@npm:5.46.1" + dependencies: + "@typescript-eslint/typescript-estree": 5.46.1 + "@typescript-eslint/utils": 5.46.1 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -4423,7 +4433,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 4c7b594f8afa52d57d0512951a874fa390eb791dcefcd0e1efff8817872293b2e4e04eff3c54d1595c1720a34d5fd315729af4e459882033d13cb6069ae9d28f + checksum: a47f1b73ac773a7f33421a8e8f9bc6e047f55236e3257b2c9eaa04d18595ab6505129ffdab86685e9c3bef564091315a2347150cd6e61a511cc2160667458014 languageName: node linkType: hard @@ -4434,6 +4444,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/types@npm:5.46.1" + checksum: 91143d3304b8c70d69d9c8e5b7428cce3a222eacfbeb99e592d278668bcf998760731deae064a76157b9a0fc4911fe3178aa24e4ea6fe2ba68dd37113834c924 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.44.0": version: 5.44.0 resolution: "@typescript-eslint/typescript-estree@npm:5.44.0" @@ -4452,21 +4469,39 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.44.0": - version: 5.44.0 - resolution: "@typescript-eslint/utils@npm:5.44.0" +"@typescript-eslint/typescript-estree@npm:5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/typescript-estree@npm:5.46.1" + dependencies: + "@typescript-eslint/types": 5.46.1 + "@typescript-eslint/visitor-keys": 5.46.1 + debug: ^4.3.4 + globby: ^11.1.0 + is-glob: ^4.0.3 + semver: ^7.3.7 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 21499b927b4118cd51e841b2e1b7e55621135f923f461b75dc8ca8442de38a82da5a0232dce5229e0266b6fc12d70696e0e912fcf1483d4c44f02e4cad39ed98 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/utils@npm:5.46.1" dependencies: "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.44.0 - "@typescript-eslint/types": 5.44.0 - "@typescript-eslint/typescript-estree": 5.44.0 + "@typescript-eslint/scope-manager": 5.46.1 + "@typescript-eslint/types": 5.46.1 + "@typescript-eslint/typescript-estree": 5.46.1 eslint-scope: ^5.1.1 eslint-utils: ^3.0.0 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: bc5bb28e41898464d35b8eb47cc452103852541e3b6be56252c15a5a81c45e10aad3db4c749eb92d752b0c358df8074e23ec6f9e65f8089baadeda7f395c7e31 + checksum: db9fd1dfb2390e66041f9529d564f38ccf74042de68e0e1e3d319ba4d02d7cd969d75dc056f938b98acab53ad7c1e36c68eabb15c0b2e2296b081652fa8d3820 languageName: node linkType: hard @@ -4480,6 +4515,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:5.46.1": + version: 5.46.1 + resolution: "@typescript-eslint/visitor-keys@npm:5.46.1" + dependencies: + "@typescript-eslint/types": 5.46.1 + eslint-visitor-keys: ^3.3.0 + checksum: 952cf20e29a040e0820e52d6815097abf042ea8e1fd5d013c0a319284ea0e2e29e0ca9ef244717450a6eb9d32ebf7fa9ed91185675a27adc35c9ad070d561b7c + languageName: node + linkType: hard + "@ungap/promise-all-settled@npm:1.1.2": version: 1.1.2 resolution: "@ungap/promise-all-settled@npm:1.1.2" @@ -9353,7 +9398,7 @@ fsevents@^1.2.7: "@types/sortablejs": ^1 "@types/tar": ^6 "@types/webspeechapi": ^0.0.29 - "@typescript-eslint/eslint-plugin": ^5.44.0 + "@typescript-eslint/eslint-plugin": ^5.46.1 "@typescript-eslint/parser": ^5.44.0 "@vaadin/combo-box": ^23.2.9 "@vaadin/vaadin-themable-mixin": ^23.2.9 From c400e771cb20d7d98a91b2272a73724959996faa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 16:49:35 +0100 Subject: [PATCH 23/70] dev(deps-dev): bump fancy-log from 1.3.3 to 2.0.0 (#14773) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 64458c5032..51f4fca91d 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "eslint-plugin-lit": "^1.6.1", "eslint-plugin-unused-imports": "^1.1.5", "eslint-plugin-wc": "^1.3.2", - "fancy-log": "^1.3.3", + "fancy-log": "^2.0.0", "fs-extra": "^7.0.1", "glob": "^7.2.0", "gulp": "^4.0.2", diff --git a/yarn.lock b/yarn.lock index aaf4d0a2a7..40ccc226bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8232,7 +8232,7 @@ __metadata: languageName: node linkType: hard -"fancy-log@npm:^1.3.2, fancy-log@npm:^1.3.3": +"fancy-log@npm:^1.3.2": version: 1.3.3 resolution: "fancy-log@npm:1.3.3" dependencies: @@ -8244,6 +8244,15 @@ __metadata: languageName: node linkType: hard +"fancy-log@npm:^2.0.0": + version: 2.0.0 + resolution: "fancy-log@npm:2.0.0" + dependencies: + color-support: ^1.1.3 + checksum: 5652bf35e6a0be68bc011cf38706c78c7724808311e88905a3e4623d0e86168f084a9f38109dce2b2f78078ae704e4b55095effda743b4795c37f30ddb70497e + languageName: node + linkType: hard + "fast-deep-equal@npm:^2.0.1": version: 2.0.1 resolution: "fast-deep-equal@npm:2.0.1" @@ -9431,7 +9440,7 @@ fsevents@^1.2.7: eslint-plugin-lit: ^1.6.1 eslint-plugin-unused-imports: ^1.1.5 eslint-plugin-wc: ^1.3.2 - fancy-log: ^1.3.3 + fancy-log: ^2.0.0 fs-extra: ^7.0.1 fuse.js: ^6.0.0 glob: ^7.2.0 From 311d11f2daf5c88d8103eb0b6c70fd141a883112 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Dec 2022 10:50:14 -0500 Subject: [PATCH 24/70] Add title attributes to iframes for accessibility (#14760) Co-authored-by: Bram Kragten --- .../src/ingress-view/hassio-ingress-view.ts | 6 ++++- src/panels/custom/ha-panel-custom.ts | 25 +++++++++++-------- src/panels/iframe/ha-panel-iframe.ts | 8 +++--- src/panels/lovelace/cards/hui-iframe-card.ts | 8 +++--- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/hassio/src/ingress-view/hassio-ingress-view.ts b/hassio/src/ingress-view/hassio-ingress-view.ts index 9deb9bb45a..6682531632 100644 --- a/hassio/src/ingress-view/hassio-ingress-view.ts +++ b/hassio/src/ingress-view/hassio-ingress-view.ts @@ -59,7 +59,11 @@ class HassioIngressView extends LitElement { return html` `; } - const iframe = html``; + const iframe = html``; if (!this.ingressPanel) { return html` - iframe { - border: 0; - width: 100%; - height: 100%; - display: block; - background-color: var(--primary-background-color); - } - - - `.trim(); + + `.trim(); const iframeDoc = this.querySelector("iframe")!.contentWindow!.document; iframeDoc.open(); iframeDoc.write( diff --git a/src/panels/iframe/ha-panel-iframe.ts b/src/panels/iframe/ha-panel-iframe.ts index f2d98b0e33..98080afac4 100644 --- a/src/panels/iframe/ha-panel-iframe.ts +++ b/src/panels/iframe/ha-panel-iframe.ts @@ -1,5 +1,6 @@ import { html, css, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; +import { ifDefined } from "lit/directives/if-defined"; import "../../layouts/hass-error-screen"; import "../../layouts/hass-subpage"; import { HomeAssistant, PanelInfo } from "../../types"; @@ -35,11 +36,12 @@ class HaPanelIframe extends LitElement { main-page > `; diff --git a/src/panels/lovelace/cards/hui-iframe-card.ts b/src/panels/lovelace/cards/hui-iframe-card.ts index f791c08ce6..0ce53d5cef 100644 --- a/src/panels/lovelace/cards/hui-iframe-card.ts +++ b/src/panels/lovelace/cards/hui-iframe-card.ts @@ -1,12 +1,13 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; -import "../../../components/ha-card"; import "../../../components/ha-alert"; +import "../../../components/ha-card"; +import type { HomeAssistant } from "../../../types"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { IframeCardConfig } from "./types"; -import type { HomeAssistant } from "../../../types"; @customElement("hui-iframe-card") export class HuiIframeCard extends LitElement implements LovelaceCard { @@ -88,9 +89,10 @@ export class HuiIframeCard extends LitElement implements LovelaceCard { })} >
From 01a4b55ed8fda91cf335154a5f00082de9823fe9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 14 Dec 2022 17:27:05 +0100 Subject: [PATCH 25/70] Use entity picker in calendar event editor (#14772) --- .../calendar/dialog-calendar-event-detail.ts | 2 +- .../calendar/dialog-calendar-event-editor.ts | 45 +++++++++---------- src/panels/calendar/ha-full-calendar.ts | 24 +++++----- .../show-dialog-calendar-event-detail.ts | 7 ++- .../show-dialog-calendar-event-editor.ts | 3 +- 5 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index adb732c08e..276459a0da 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -47,7 +47,7 @@ class DialogCalendarEventDetail extends LitElement { if (params.entry) { const entry = params.entry!; this._data = entry; - this._calendarId = params.calendarId || params.calendars[0].entity_id; + this._calendarId = params.calendarId; } } diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 5330108172..4efbeec587 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -1,6 +1,5 @@ import "@material/mwc-button"; import { mdiClose } from "@mdi/js"; -import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit"; import { addDays, addHours, @@ -8,16 +7,20 @@ import { differenceInMilliseconds, startOfHour, } from "date-fns/esm"; +import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeStateDomain } from "../../common/entity/compute_state_domain"; +import { supportsFeature } from "../../common/entity/supports-feature"; import { isDate } from "../../common/string/is_date"; +import "../../components/entity/ha-entity-picker"; import "../../components/ha-date-input"; import "../../components/ha-textarea"; import "../../components/ha-time-input"; import { - Calendar, + CalendarEntityFeature, CalendarEventMutableParams, createCalendarEvent, deleteCalendarEvent, @@ -27,14 +30,9 @@ import { HomeAssistant } from "../../types"; import "../lovelace/components/hui-generic-entity-row"; import "./ha-recurrence-rule-editor"; import { showConfirmEventDialog } from "./show-confirm-event-dialog-box"; -import { CalendarEventDetailDialogParams } from "./show-dialog-calendar-event-detail"; import { CalendarEventEditDialogParams } from "./show-dialog-calendar-event-editor"; -const rowRenderer: ComboBoxLitRenderer = ( - item -) => html` - ${item.name} -`; +const CALENDAR_DOMAINS = ["calendar"]; @customElement("dialog-calendar-event-editor") class DialogCalendarEventEditor extends LitElement { @@ -44,9 +42,7 @@ class DialogCalendarEventEditor extends LitElement { @state() private _info?: string; - @state() private _params?: CalendarEventDetailDialogParams; - - @state() private _calendars: Calendar[] = []; + @state() private _params?: CalendarEventEditDialogParams; @state() private _calendarId?: string; @@ -68,8 +64,13 @@ class DialogCalendarEventEditor extends LitElement { this._error = undefined; this._info = undefined; this._params = params; - this._calendars = params.calendars; - this._calendarId = params.calendarId || this._calendars[0].entity_id; + this._calendarId = + params.calendarId || + Object.values(this.hass.states).find( + (stateObj) => + computeStateDomain(stateObj) === "calendar" && + supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT) + )?.entity_id; if (params.entry) { const entry = params.entry!; this._allDay = isDate(entry.dtstart); @@ -99,7 +100,6 @@ class DialogCalendarEventEditor extends LitElement { if (!this._params) { return; } - this._calendars = []; this._calendarId = undefined; this._params = undefined; this._dtstart = undefined; @@ -172,19 +172,16 @@ class DialogCalendarEventEditor extends LitElement { @change=${this._handleDescriptionChanged} autogrow > - + > @@ -281,6 +278,9 @@ class DialogCalendarEventEditor extends LitElement { `; } + private _isEditableCalendar = (entityStateObj: HassEntity) => + supportsFeature(entityStateObj, CalendarEntityFeature.CREATE_EVENT); + private _getLocaleStrings = memoizeOne((startDate?: Date, endDate?: Date) => // en-CA locale used for date format YYYY-MM-DD // en-GB locale used for 24h time format HH:MM:SS @@ -557,9 +557,6 @@ class DialogCalendarEventEditor extends LitElement { ha-rrule { display: block; } - ha-combo-box { - display: block; - } ha-svg-icon { width: 40px; margin-right: 8px; diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index f913ba78d0..e0e2a114d3 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -200,7 +200,7 @@ export class HAFullCalendar extends LitElement { : ""}
- ${this._mutableCalendars.length > 0 + ${this._hasMutableCalendars ? html` { - const entityStateObj = this.hass.states[selCal.entity_id]; - return ( - entityStateObj && - supportsFeature(entityStateObj, CalendarEntityFeature.CREATE_EVENT) - ); - }) - .map((cal) => cal); + // Return if there are calendars that support creating events + private get _hasMutableCalendars(): boolean { + return this.calendars.some((selCal) => { + const entityStateObj = this.hass.states[selCal.entity_id]; + return ( + entityStateObj && + supportsFeature(entityStateObj, CalendarEntityFeature.CREATE_EVENT) + ); + }); } private _createEvent(_info) { @@ -289,7 +287,6 @@ export class HAFullCalendar extends LitElement { // current actual month, as for that one the current day is automatically highlighted and // defaulting to a different day in the event creation dialog would be weird. showCalendarEventEditDialog(this, { - calendars: this._mutableCalendars, selectedDate: this._activeView === "dayGridWeek" || this._activeView === "dayGridDay" || @@ -309,7 +306,6 @@ export class HAFullCalendar extends LitElement { entityStateObj && supportsFeature(entityStateObj, CalendarEntityFeature.DELETE_EVENT); showCalendarEventDetailDialog(this, { - calendars: this.calendars, calendarId: info.event.extendedProps.calendar, entry: info.event.extendedProps.eventData, color: info.event.backgroundColor, diff --git a/src/panels/calendar/show-dialog-calendar-event-detail.ts b/src/panels/calendar/show-dialog-calendar-event-detail.ts index 05306294b4..659db3b1b6 100644 --- a/src/panels/calendar/show-dialog-calendar-event-detail.ts +++ b/src/panels/calendar/show-dialog-calendar-event-detail.ts @@ -1,10 +1,9 @@ import { fireEvent } from "../../common/dom/fire_event"; -import { Calendar, CalendarEventData } from "../../data/calendar"; +import { CalendarEventData } from "../../data/calendar"; export interface CalendarEventDetailDialogParams { - calendars: Calendar[]; // When creating new events, is the list of calendar entities that support creation - calendarId?: string; - entry?: CalendarEventData; + calendarId: string; + entry: CalendarEventData; canDelete?: boolean; canEdit?: boolean; updated: () => void; diff --git a/src/panels/calendar/show-dialog-calendar-event-editor.ts b/src/panels/calendar/show-dialog-calendar-event-editor.ts index f2ab58a4f6..6f6fddcf22 100644 --- a/src/panels/calendar/show-dialog-calendar-event-editor.ts +++ b/src/panels/calendar/show-dialog-calendar-event-editor.ts @@ -1,8 +1,7 @@ import { fireEvent } from "../../common/dom/fire_event"; -import { Calendar, CalendarEventData } from "../../data/calendar"; +import { CalendarEventData } from "../../data/calendar"; export interface CalendarEventEditDialogParams { - calendars: Calendar[]; // When creating new events, is the list of calendar entities that support creation calendarId?: string; selectedDate?: Date; // When provided is used as the pre-filled date for the event creation dialog entry?: CalendarEventData; From ebcbfda92d5290e826f3fc15506b7d4f7167fa31 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Dec 2022 11:42:36 -0500 Subject: [PATCH 26/70] Remove prefixes from dependabot commit messages (#14778) --- .github/dependabot.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 054d15b6e5..024c3be9ae 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,7 +12,3 @@ updates: interval: "daily" time: "06:00" open-pull-requests-limit: 5 - commit-message: - include: scope - prefix: "prod" - prefix-development: "dev" From 77b8152c55685c4db2552293ed4d112a19c206e7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:39:38 -0800 Subject: [PATCH 27/70] Make map card trails clickable, provide time context. (#14515) Co-authored-by: Bram Kragten --- src/components/map/ha-map.ts | 48 +++++++++++++++-------- src/panels/lovelace/cards/hui-map-card.ts | 30 ++++++++++++-- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/components/map/ha-map.ts b/src/components/map/ha-map.ts index d9294bc58c..466fe0a0d1 100644 --- a/src/components/map/ha-map.ts +++ b/src/components/map/ha-map.ts @@ -23,8 +23,12 @@ import "./ha-entity-marker"; const getEntityId = (entity: string | HaMapEntity): string => typeof entity === "string" ? entity : entity.entity_id; +export interface HaMapPathPoint { + point: LatLngTuple; + tooltip: string; +} export interface HaMapPaths { - points: LatLngTuple[]; + points: HaMapPathPoint[]; color?: string; gradualOpacity?: number; } @@ -247,19 +251,21 @@ export class HaMap extends ReactiveElement { // DRAW point this._mapPaths.push( - Leaflet!.circleMarker(path.points[pointIndex], { - radius: 3, - color: path.color || darkPrimaryColor, - opacity, - fillOpacity: opacity, - interactive: false, - }) + Leaflet! + .circleMarker(path.points[pointIndex].point, { + radius: 3, + color: path.color || darkPrimaryColor, + opacity, + fillOpacity: opacity, + interactive: true, + }) + .bindTooltip(path.points[pointIndex].tooltip, { direction: "top" }) ); // DRAW line between this and next point this._mapPaths.push( Leaflet!.polyline( - [path.points[pointIndex], path.points[pointIndex + 1]], + [path.points[pointIndex].point, path.points[pointIndex + 1].point], { color: path.color || darkPrimaryColor, opacity, @@ -275,13 +281,15 @@ export class HaMap extends ReactiveElement { : undefined; // DRAW end path point this._mapPaths.push( - Leaflet!.circleMarker(path.points[pointIndex], { - radius: 3, - color: path.color || darkPrimaryColor, - opacity, - fillOpacity: opacity, - interactive: false, - }) + Leaflet! + .circleMarker(path.points[pointIndex].point, { + radius: 3, + color: path.color || darkPrimaryColor, + opacity, + fillOpacity: opacity, + interactive: true, + }) + .bindTooltip(path.points[pointIndex].tooltip, { direction: "top" }) ); } this._mapPaths.forEach((marker) => map.addLayer(marker)); @@ -491,6 +499,14 @@ export class HaMap extends ReactiveElement { .leaflet-bottom { z-index: 1 !important; } + .leaflet-tooltip { + padding: 8px; + font-size: 90%; + background: rgba(80, 80, 80, 0.9) !important; + color: white !important; + border-radius: 4px; + box-shadow: none !important; + } `; } } diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index 59b1e780bf..e5b7951160 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -11,6 +11,7 @@ import { import { customElement, property, query, state } from "lit/decorators"; import { mdiImageFilterCenterFocus } from "@mdi/js"; import memoizeOne from "memoize-one"; +import { isToday } from "date-fns"; import { computeDomain } from "../../../common/entity/compute_domain"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import "../../../components/ha-card"; @@ -23,8 +24,17 @@ import { EntityConfig } from "../entity-rows/types"; import { LovelaceCard } from "../types"; import { MapCardConfig } from "./types"; import "../../../components/map/ha-map"; -import type { HaMap, HaMapPaths } from "../../../components/map/ha-map"; +import type { + HaMap, + HaMapPaths, + HaMapPathPoint, +} from "../../../components/map/ha-map"; import { getColorByIndex } from "../../../common/color/colors"; +import { formatDateTime } from "../../../common/datetime/format_date_time"; +import { + formatTime, + formatTimeWeekday, +} from "../../../common/datetime/format_time"; const MINUTE = 60000; @@ -274,16 +284,28 @@ class HuiMapCard extends LitElement implements LovelaceCard { } // filter location data from states and remove all invalid locations const points = entityStates.reduce( - (accumulator: LatLngTuple[], entityState) => { + (accumulator: HaMapPathPoint[], entityState) => { const latitude = entityState.attributes.latitude; const longitude = entityState.attributes.longitude; if (latitude && longitude) { - accumulator.push([latitude, longitude] as LatLngTuple); + const p = {} as HaMapPathPoint; + p.point = [latitude, longitude] as LatLngTuple; + const t = new Date(entityState.last_updated); + if (config.hours_to_show! > 144) { + // if showing > 6 days in the history trail, show the full + // date and time + p.tooltip = formatDateTime(t, this.hass.locale); + } else if (isToday(t)) { + p.tooltip = formatTime(t, this.hass.locale); + } else { + p.tooltip = formatTimeWeekday(t, this.hass.locale); + } + accumulator.push(p); } return accumulator; }, [] - ) as LatLngTuple[]; + ) as HaMapPathPoint[]; paths.push({ points, From 25a5bd568ad8893c484b016e05c3fe3214a34a25 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:51:33 -0800 Subject: [PATCH 28/70] Fix entity-filter handling of numeric states for == and != operators. (#14726) Co-authored-by: Bram Kragten fixes undefined --- src/panels/lovelace/common/evaluate-filter.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/common/evaluate-filter.ts b/src/panels/lovelace/common/evaluate-filter.ts index 70a9837c77..9bc190e494 100644 --- a/src/panels/lovelace/common/evaluate-filter.ts +++ b/src/panels/lovelace/common/evaluate-filter.ts @@ -2,11 +2,24 @@ import { HassEntity } from "home-assistant-js-websocket"; export const evaluateFilter = (stateObj: HassEntity, filter: any): boolean => { const operator = filter.operator || "=="; - const value = filter.value ?? filter; - const state = filter.attribute + let value = filter.value ?? filter; + let state = filter.attribute ? stateObj.attributes[filter.attribute] : stateObj.state; + if (operator === "==" || operator === "!=") { + const valueIsNumeric = + typeof value === "number" || + (typeof value === "string" && value.trim() && !isNaN(Number(value))); + const stateIsNumeric = + typeof state === "number" || + (typeof state === "string" && state.trim() && !isNaN(Number(state))); + if (valueIsNumeric && stateIsNumeric) { + value = Number(value); + state = Number(state); + } + } + switch (operator) { case "==": return state === value; From b4d6fc3c20db4a8ffc3e298daea7a9db6bade760 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 14 Dec 2022 19:08:08 +0100 Subject: [PATCH 29/70] Handle "idle" state of alert entity (#14779) --- gallery/src/pages/misc/entity-state.ts | 1 + src/common/entity/color/alert_color.ts | 10 ++++++++++ src/common/entity/state_active.ts | 19 +++++++++++++++---- src/common/entity/state_color.ts | 5 ++++- src/resources/ha-style.ts | 1 + 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/common/entity/color/alert_color.ts diff --git a/gallery/src/pages/misc/entity-state.ts b/gallery/src/pages/misc/entity-state.ts index ae12a09abe..2d3326c3b7 100644 --- a/gallery/src/pages/misc/entity-state.ts +++ b/gallery/src/pages/misc/entity-state.ts @@ -106,6 +106,7 @@ const ENTITIES: HassEntity[] = [ // Alert createEntity("alert.off", "off"), createEntity("alert.on", "on"), + createEntity("alert.idle", "idle"), // Automation createEntity("automation.off", "off"), createEntity("automation.on", "on"), diff --git a/src/common/entity/color/alert_color.ts b/src/common/entity/color/alert_color.ts new file mode 100644 index 0000000000..3e3ccbe6a4 --- /dev/null +++ b/src/common/entity/color/alert_color.ts @@ -0,0 +1,10 @@ +export const alertColor = (state?: string): string | undefined => { + switch (state) { + case "on": + return "alert"; + case "off": + return "alert-off"; + default: + return undefined; + } +}; diff --git a/src/common/entity/state_active.ts b/src/common/entity/state_active.ts index 415be5f055..2c673f3a6b 100644 --- a/src/common/entity/state_active.ts +++ b/src/common/entity/state_active.ts @@ -1,5 +1,5 @@ import { HassEntity } from "home-assistant-js-websocket"; -import { OFF_STATES, UNAVAILABLE } from "../../data/entity"; +import { OFF, UNAVAILABLE, UNAVAILABLE_STATES } from "../../data/entity"; import { computeDomain } from "./compute_domain"; export function stateActive(stateObj: HassEntity, state?: string): boolean { @@ -10,7 +10,15 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean { return compareState !== UNAVAILABLE; } - if (OFF_STATES.includes(compareState)) { + if (UNAVAILABLE_STATES.includes(compareState)) { + return false; + } + + // The "off" check is relevant for most domains, but there are exceptions + // such as "alert" where "off" is still a somewhat active state and + // therefore gets a custom color and "idle" is instead the state that + // matches what most other domains consider inactive. + if (compareState === OFF && domain !== "alert") { return false; } @@ -18,6 +26,9 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean { switch (domain) { case "alarm_control_panel": return compareState !== "disarmed"; + case "alert": + // "on" and "off" are active, as "off" just means alert was acknowledged but is still active + return compareState !== "idle"; case "cover": return !["closed", "closing"].includes(compareState); case "device_tracker": @@ -37,7 +48,7 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean { return compareState === "active"; case "camera": return compareState === "streaming"; - default: - return true; } + + return true; } diff --git a/src/common/entity/state_color.ts b/src/common/entity/state_color.ts index 613ce4e7d8..2815b23a16 100644 --- a/src/common/entity/state_color.ts +++ b/src/common/entity/state_color.ts @@ -2,6 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { UNAVAILABLE } from "../../data/entity"; import { alarmControlPanelColor } from "./color/alarm_control_panel_color"; +import { alertColor } from "./color/alert_color"; import { binarySensorColor } from "./color/binary_sensor_color"; import { climateColor } from "./color/climate_color"; import { lockColor } from "./color/lock_color"; @@ -12,7 +13,6 @@ import { computeDomain } from "./compute_domain"; import { stateActive } from "./state_active"; const STATIC_ACTIVE_COLORED_DOMAIN = new Set([ - "alert", "automation", "calendar", "camera", @@ -65,6 +65,9 @@ export const stateColor = (stateObj: HassEntity, state?: string) => { case "alarm_control_panel": return alarmControlPanelColor(compareState); + case "alert": + return alertColor(compareState); + case "binary_sensor": return binarySensorColor(stateObj, compareState); diff --git a/src/resources/ha-style.ts b/src/resources/ha-style.ts index 56eced1a57..18bac68682 100644 --- a/src/resources/ha-style.ts +++ b/src/resources/ha-style.ts @@ -145,6 +145,7 @@ documentContainer.innerHTML = ` --rgb-state-alarm-pending-color: var(--rgb-orange-color); --rgb-state-alarm-triggered-color: var(--rgb-red-color); --rgb-state-alert-color: var(--rgb-red-color); + --rgb-state-alert-off-color: var(--rgb-orange-color); --rgb-state-automation-color: var(--rgb-amber-color); --rgb-state-binary-sensor-alerting-color: var(--rgb-red-color); --rgb-state-binary-sensor-color: var(--rgb-amber-color); From e8e4733fc9c6adf03bc77d36d34698a22c3246a4 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Dec 2022 13:39:10 -0500 Subject: [PATCH 30/70] Fix localize key type errors for states (#14691) * Replace unavailable state checks with type predicate * Remove localize exceptions related to state * Use literal types for climate attributes * Add fan action to climate tile badge * Use literal types for truncated states in badges * Use literal type for humidifier state * Replace unavailable state checks in calendar and tile card * Avoid string split for truncated key --- src/common/entity/color/climate_color.ts | 1 + src/common/entity/get_states.ts | 4 +- src/common/entity/state_active.ts | 4 +- src/common/translations/localize.ts | 3 -- src/components/entity/ha-entity-toggle.ts | 4 +- src/components/entity/ha-state-label-badge.ts | 51 ++++++++++++++----- src/components/ha-climate-state.ts | 13 +++-- .../media-player/ha-media-player-browse.ts | 4 +- src/data/calendar.ts | 4 +- src/data/climate.ts | 16 ++++-- src/data/entity.ts | 9 +++- src/data/humidifier.ts | 14 ++++- src/data/media-player.ts | 4 +- .../controls/more-info-automation.ts | 4 +- .../more-info/controls/more-info-counter.ts | 4 +- .../controls/more-info-input_datetime.ts | 6 +-- .../more-info/controls/more-info-update.ts | 4 +- src/panels/config/scene/ha-scene-dashboard.ts | 4 +- src/panels/lovelace/cards/hui-area-card.ts | 5 +- src/panels/lovelace/cards/hui-entity-card.ts | 4 +- src/panels/lovelace/cards/hui-glance-card.ts | 4 +- .../lovelace/cards/hui-humidifier-card.ts | 13 +++-- src/panels/lovelace/cards/hui-light-card.ts | 8 +-- .../lovelace/cards/hui-media-control-card.ts | 4 +- src/panels/lovelace/cards/hui-tile-card.ts | 4 +- .../cards/tile/badges/tile-badge-climate.ts | 2 + .../editor/card-editor/hui-card-picker.ts | 6 +-- .../entity-rows/hui-humidifier-entity-row.ts | 3 +- .../hui-input-datetime-entity-row.ts | 6 +-- .../hui-input-number-entity-row.ts | 6 +-- .../entity-rows/hui-input-text-entity-row.ts | 4 +- .../entity-rows/hui-lock-entity-row.ts | 4 +- .../hui-media-player-entity-row.ts | 6 +-- .../entity-rows/hui-script-entity-row.ts | 4 +- .../entity-rows/hui-sensor-entity-row.ts | 5 +- .../entity-rows/hui-text-entity-row.ts | 4 +- .../entity-rows/hui-toggle-entity-row.ts | 4 +- .../entity-rows/hui-weather-entity-row.ts | 4 +- src/state-summary/state-card-display.ts | 4 +- src/state-summary/state-card-script.ts | 4 +- src/state-summary/state-card-text.ts | 4 +- 41 files changed, 153 insertions(+), 112 deletions(-) diff --git a/src/common/entity/color/climate_color.ts b/src/common/entity/color/climate_color.ts index 9d7d17ef6a..390e9ab739 100644 --- a/src/common/entity/color/climate_color.ts +++ b/src/common/entity/color/climate_color.ts @@ -3,6 +3,7 @@ import { HvacAction } from "../../../data/climate"; export const CLIMATE_HVAC_ACTION_COLORS: Record = { cooling: "var(--rgb-state-climate-cool-color)", drying: "var(--rgb-state-climate-dry-color)", + fan: "var(--rgb-state-climate-fan-only-color)", heating: "var(--rgb-state-climate-heat-color)", idle: "var(--rgb-state-climate-idle-color)", off: "var(--rgb-state-climate-off-color)", diff --git a/src/common/entity/get_states.ts b/src/common/entity/get_states.ts index 92c1365b14..f79a16e5e2 100644 --- a/src/common/entity/get_states.ts +++ b/src/common/entity/get_states.ts @@ -2,7 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { computeStateDomain } from "./compute_state_domain"; import { UNAVAILABLE_STATES } from "../../data/entity"; -const FIXED_DOMAIN_STATES = { +export const FIXED_DOMAIN_STATES = { alarm_control_panel: [ "armed_away", "armed_custom_bypass", @@ -57,7 +57,7 @@ const FIXED_DOMAIN_STATES = { "windy-variant", "windy", ], -}; +} as const; const FIXED_DOMAIN_ATTRIBUTE_STATES = { alarm_control_panel: { diff --git a/src/common/entity/state_active.ts b/src/common/entity/state_active.ts index 2c673f3a6b..af5f75d126 100644 --- a/src/common/entity/state_active.ts +++ b/src/common/entity/state_active.ts @@ -1,5 +1,5 @@ import { HassEntity } from "home-assistant-js-websocket"; -import { OFF, UNAVAILABLE, UNAVAILABLE_STATES } from "../../data/entity"; +import { isUnavailableState, OFF, UNAVAILABLE } from "../../data/entity"; import { computeDomain } from "./compute_domain"; export function stateActive(stateObj: HassEntity, state?: string): boolean { @@ -10,7 +10,7 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean { return compareState !== UNAVAILABLE; } - if (UNAVAILABLE_STATES.includes(compareState)) { + if (isUnavailableState(compareState)) { return false; } diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts index 0b5fc77030..5a38952c9a 100644 --- a/src/common/translations/localize.ts +++ b/src/common/translations/localize.ts @@ -12,9 +12,6 @@ import { getLocalLanguage } from "../../util/common-translation"; export type LocalizeKeys = | FlattenObjectKeys> | `panel.${string}` - | `state.${string}` - | `state_attributes.${string}` - | `state_badge.${string}` | `ui.card.alarm_control_panel.${string}` | `ui.card.weather.attributes.${string}` | `ui.card.weather.cardinal_direction.${string}` diff --git a/src/components/entity/ha-entity-toggle.ts b/src/components/entity/ha-entity-toggle.ts index 0bdf2e3126..223b208e1e 100644 --- a/src/components/entity/ha-entity-toggle.ts +++ b/src/components/entity/ha-entity-toggle.ts @@ -12,7 +12,7 @@ import { property, state } from "lit/decorators"; import { STATES_OFF } from "../../common/const"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; -import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../data/entity"; +import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { forwardHaptic } from "../../data/haptics"; import { HomeAssistant } from "../../types"; import "../ha-formfield"; @@ -22,7 +22,7 @@ import "../ha-switch"; const isOn = (stateObj?: HassEntity) => stateObj !== undefined && !STATES_OFF.includes(stateObj.state) && - !UNAVAILABLE_STATES.includes(stateObj.state); + !isUnavailableState(stateObj.state); export class HaEntityToggle extends LitElement { // hass is not a property so that we only re-render on stateObj changes diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index 3c7cc3dfa7..35fa54748e 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -10,21 +10,45 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import { arrayLiteralIncludes } from "../../common/array/literal-includes"; import secondsToDuration from "../../common/datetime/seconds_to_duration"; import { computeStateDisplay } from "../../common/entity/compute_state_display"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; +import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states"; import { formatNumber, getNumberFormatOptions, isNumericState, } from "../../common/number/format_number"; -import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; +import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { timerTimeRemaining } from "../../data/timer"; import { HomeAssistant } from "../../types"; import "../ha-label-badge"; import "../ha-state-icon"; +// Define the domains whose states have special truncated strings +const TRUNCATED_DOMAINS = [ + "alarm_control_panel", + "device_tracker", + "person", +] as const satisfies ReadonlyArray; + +type TruncatedDomain = typeof TRUNCATED_DOMAINS[number]; +type TruncatedKey = { + [T in TruncatedDomain]: `${T}.${typeof FIXED_DOMAIN_STATES[T][number]}`; +}[TruncatedDomain]; + +const getTruncatedKey = (domainKey: string, stateKey: string) => { + if ( + arrayLiteralIncludes(TRUNCATED_DOMAINS)(domainKey) && + arrayLiteralIncludes(FIXED_DOMAIN_STATES[domainKey])(stateKey) + ) { + return `${domainKey}.${stateKey}` as TruncatedKey; + } + return null; +}; + @customElement("ha-state-label-badge") export class HaStateLabelBadge extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; @@ -186,19 +210,18 @@ export class HaStateLabelBadge extends LitElement { } } - private _computeLabel(domain, entityState, _timerTimeRemaining) { - if ( - entityState.state === UNAVAILABLE || - ["device_tracker", "alarm_control_panel", "person"].includes(domain) - ) { - // Localize the state with a special state_badge namespace, which has variations of - // the state translations that are truncated to fit within the badge label. Translations - // are only added for device_tracker, alarm_control_panel and person. - return ( - this.hass!.localize(`state_badge.${domain}.${entityState.state}`) || - this.hass!.localize(`state_badge.default.${entityState.state}`) || - entityState.state - ); + private _computeLabel( + domain: string, + entityState: HassEntity, + _timerTimeRemaining = 0 + ) { + // For unavailable states or certain domains, use a special translation that is truncated to fit within the badge label + if (isUnavailableState(entityState.state)) { + return this.hass!.localize(`state_badge.default.${entityState.state}`); + } + const domainStateKey = getTruncatedKey(domain, entityState.state); + if (domainStateKey) { + return this.hass!.localize(`state_badge.${domainStateKey}`); } if (domain === "timer") { return secondsToDuration(_timerTimeRemaining); diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts index 52ecb1de7f..8b9f170b00 100644 --- a/src/components/ha-climate-state.ts +++ b/src/components/ha-climate-state.ts @@ -1,22 +1,21 @@ -import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { formatNumber } from "../common/number/format_number"; -import { CLIMATE_PRESET_NONE } from "../data/climate"; -import { UNAVAILABLE_STATES } from "../data/entity"; +import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate"; +import { isUnavailableState } from "../data/entity"; import type { HomeAssistant } from "../types"; @customElement("ha-climate-state") class HaClimateState extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public stateObj!: HassEntity; + @property({ attribute: false }) public stateObj!: ClimateEntity; protected render(): TemplateResult { const currentStatus = this._computeCurrentStatus(); return html`
- ${!UNAVAILABLE_STATES.includes(this.stateObj.state) + ${!isUnavailableState(this.stateObj.state) ? html` ${this._localizeState()} ${this.stateObj.attributes.preset_mode && @@ -31,7 +30,7 @@ class HaClimateState extends LitElement { : this._localizeState()}
- ${currentStatus && !UNAVAILABLE_STATES.includes(this.stateObj.state) + ${currentStatus && !isUnavailableState(this.stateObj.state) ? html`
${this.hass.localize("ui.card.climate.currently")}:
${currentStatus}
@@ -109,7 +108,7 @@ class HaClimateState extends LitElement { } private _localizeState(): string { - if (UNAVAILABLE_STATES.includes(this.stateObj.state)) { + if (isUnavailableState(this.stateObj.state)) { return this.hass.localize(`state.default.${this.stateObj.state}`); } diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index b27f48385d..3a760c89d9 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -27,7 +27,7 @@ import { until } from "lit/directives/until"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; -import { UNAVAILABLE_STATES } from "../../data/entity"; +import { isUnavailableState } from "../../data/entity"; import type { MediaPlayerItem } from "../../data/media-player"; import { browseMediaPlayer, @@ -247,7 +247,7 @@ export class HaMediaPlayerBrowse extends LitElement { }); } else if ( err.code === "entity_not_found" && - UNAVAILABLE_STATES.includes(this.hass.states[this.entityId]?.state) + isUnavailableState(this.hass.states[this.entityId]?.state) ) { this._setError({ message: this.hass.localize( diff --git a/src/data/calendar.ts b/src/data/calendar.ts index 2cd6b0b944..0b856de9b9 100644 --- a/src/data/calendar.ts +++ b/src/data/calendar.ts @@ -2,7 +2,7 @@ import { getColorByIndex } from "../common/color/colors"; import { computeDomain } from "../common/entity/compute_domain"; import { computeStateName } from "../common/entity/compute_state_name"; import type { HomeAssistant } from "../types"; -import { UNAVAILABLE_STATES } from "./entity"; +import { isUnavailableState } from "./entity"; export interface Calendar { entity_id: string; @@ -138,7 +138,7 @@ export const getCalendars = (hass: HomeAssistant): Calendar[] => .filter( (eid) => computeDomain(eid) === "calendar" && - !UNAVAILABLE_STATES.includes(hass.states[eid].state) + !isUnavailableState(hass.states[eid].state) ) .sort() .map((eid, idx) => ({ diff --git a/src/data/climate.ts b/src/data/climate.ts index a6fbea3bae..064fb32c98 100644 --- a/src/data/climate.ts +++ b/src/data/climate.ts @@ -2,6 +2,7 @@ import { HassEntityAttributeBase, HassEntityBase, } from "home-assistant-js-websocket"; +import { TranslationDict } from "../types"; export type HvacMode = | "off" @@ -14,7 +15,12 @@ export type HvacMode = export const CLIMATE_PRESET_NONE = "none"; -export type HvacAction = "off" | "heating" | "cooling" | "drying" | "idle"; +type ClimateAttributes = TranslationDict["state_attributes"]["climate"]; +export type HvacAction = keyof ClimateAttributes["hvac_action"]; +export type FanMode = keyof ClimateAttributes["fan_mode"]; +export type PresetMode = + | keyof ClimateAttributes["preset_mode"] + | typeof CLIMATE_PRESET_NONE; export type ClimateEntity = HassEntityBase & { attributes: HassEntityAttributeBase & { @@ -34,10 +40,10 @@ export type ClimateEntity = HassEntityBase & { target_humidity_high?: number; min_humidity?: number; max_humidity?: number; - fan_mode?: string; - fan_modes?: string[]; - preset_mode?: string; - preset_modes?: string[]; + fan_mode?: FanMode; + fan_modes?: FanMode[]; + preset_mode?: PresetMode; + preset_modes?: PresetMode[]; swing_mode?: string; swing_modes?: string[]; aux_heat?: "on" | "off"; diff --git a/src/data/entity.ts b/src/data/entity.ts index dbb7f759cc..fe6ca3d6dc 100644 --- a/src/data/entity.ts +++ b/src/data/entity.ts @@ -1,7 +1,12 @@ +import { arrayLiteralIncludes } from "../common/array/literal-includes"; + export const UNAVAILABLE = "unavailable"; export const UNKNOWN = "unknown"; export const ON = "on"; export const OFF = "off"; -export const UNAVAILABLE_STATES = [UNAVAILABLE, UNKNOWN]; -export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF]; +export const UNAVAILABLE_STATES = [UNAVAILABLE, UNKNOWN] as const; +export const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF] as const; + +export const isUnavailableState = arrayLiteralIncludes(UNAVAILABLE_STATES); +export const isOffState = arrayLiteralIncludes(OFF_STATES); diff --git a/src/data/humidifier.ts b/src/data/humidifier.ts index 968aad1dcd..5f0fed7a5f 100644 --- a/src/data/humidifier.ts +++ b/src/data/humidifier.ts @@ -2,14 +2,24 @@ import { HassEntityAttributeBase, HassEntityBase, } from "home-assistant-js-websocket"; +import { FIXED_DOMAIN_STATES } from "../common/entity/get_states"; +import { TranslationDict } from "../types"; +import { UNAVAILABLE_STATES } from "./entity"; + +type HumidifierState = + | typeof FIXED_DOMAIN_STATES.humidifier[number] + | typeof UNAVAILABLE_STATES[number]; +type HumidifierMode = + keyof TranslationDict["state_attributes"]["humidifier"]["mode"]; export type HumidifierEntity = HassEntityBase & { + state: HumidifierState; attributes: HassEntityAttributeBase & { humidity?: number; min_humidity?: number; max_humidity?: number; - mode?: string; - available_modes?: string[]; + mode?: HumidifierMode; + available_modes?: HumidifierMode[]; }; }; diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 1bf35025fc..69c03646ec 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -35,7 +35,7 @@ import type { import { supportsFeature } from "../common/entity/supports-feature"; import { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse"; import type { HomeAssistant, TranslationDict } from "../types"; -import { UNAVAILABLE_STATES } from "./entity"; +import { isUnavailableState } from "./entity"; import { isTTSMediaSource } from "./tts"; interface MediaPlayerEntityAttributes extends HassEntityAttributeBase { @@ -259,7 +259,7 @@ export const computeMediaControls = ( const state = stateObj.state; - if (UNAVAILABLE_STATES.includes(state)) { + if (isUnavailableState(state)) { return undefined; } diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index fab8cfb54b..880c29437f 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -4,7 +4,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-relative-time"; import { triggerAutomationActions } from "../../../data/automation"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; @customElement("more-info-automation") @@ -32,7 +32,7 @@ class MoreInfoAutomation extends LitElement {
${this.hass.localize("ui.card.automation.trigger")} diff --git a/src/dialogs/more-info/controls/more-info-counter.ts b/src/dialogs/more-info/controls/more-info-counter.ts index a33fba0c90..a9e57fe679 100644 --- a/src/dialogs/more-info/controls/more-info-counter.ts +++ b/src/dialogs/more-info/controls/more-info-counter.ts @@ -2,7 +2,7 @@ import "@material/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; @customElement("more-info-counter") @@ -16,7 +16,7 @@ class MoreInfoCounter extends LitElement { return html``; } - const disabled = UNAVAILABLE_STATES.includes(this.stateObj!.state); + const disabled = isUnavailableState(this.stateObj!.state); return html`
diff --git a/src/dialogs/more-info/controls/more-info-input_datetime.ts b/src/dialogs/more-info/controls/more-info-input_datetime.ts index fbe6917fd6..bd5a8cd8e4 100644 --- a/src/dialogs/more-info/controls/more-info-input_datetime.ts +++ b/src/dialogs/more-info/controls/more-info-input_datetime.ts @@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-date-input"; import "../../../components/ha-time-input"; -import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; +import { isUnavailableState, UNKNOWN } from "../../../data/entity"; import { setInputDateTimeValue, stateToIsoDateString, @@ -28,7 +28,7 @@ class MoreInfoInputDatetime extends LitElement { @@ -45,7 +45,7 @@ class MoreInfoInputDatetime extends LitElement { ? this.stateObj.state.split(" ")[1] : this.stateObj.state} .locale=${this.hass.locale} - .disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)} + .disabled=${isUnavailableState(this.stateObj.state)} @value-changed=${this._timeChanged} @click=${this._stopEventPropagation} > diff --git a/src/dialogs/more-info/controls/more-info-update.ts b/src/dialogs/more-info/controls/more-info-update.ts index 7801e69730..de73cc1f3c 100644 --- a/src/dialogs/more-info/controls/more-info-update.ts +++ b/src/dialogs/more-info/controls/more-info-update.ts @@ -9,7 +9,7 @@ import "../../../components/ha-checkbox"; import "../../../components/ha-circular-progress"; import "../../../components/ha-formfield"; import "../../../components/ha-markdown"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { UpdateEntity, updateIsInstalling, @@ -37,7 +37,7 @@ class MoreInfoUpdate extends LitElement { if ( !this.hass || !this.stateObj || - UNAVAILABLE_STATES.includes(this.stateObj.state) + isUnavailableState(this.stateObj.state) ) { return html``; } diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 652420dca0..7146903b3c 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -45,7 +45,7 @@ import { showToast } from "../../../util/toast"; import { configSections } from "../ha-panel-config"; import { formatShortDateTime } from "../../../common/datetime/format_date_time"; import { relativeTime } from "../../../common/datetime/relative_time"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; @customElement("ha-scene-dashboard") class HaSceneDashboard extends LitElement { @@ -116,7 +116,7 @@ class HaSceneDashboard extends LitElement { const now = new Date(); const dayDifference = differenceInDays(now, date); return html` - ${last_activated && !UNAVAILABLE_STATES.includes(last_activated) + ${last_activated && !isUnavailableState(last_activated) ? dayDifference > 3 ? formatShortDateTime(date, this.hass.locale) : relativeTime(date, this.hass.locale) diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index a2bf35cbbe..adfcbe04c1 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -42,7 +42,7 @@ import { DeviceRegistryEntry, subscribeDeviceRegistry, } from "../../../data/device_registry"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { EntityRegistryEntry, subscribeEntityRegistry, @@ -181,8 +181,7 @@ export class HuiAreaCard : entities ).some( (entity) => - !UNAVAILABLE_STATES.includes(entity.state) && - !STATES_OFF.includes(entity.state) + !isUnavailableState(entity.state) && !STATES_OFF.includes(entity.state) ); } diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index 6065c116f5..a0abc1a25c 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -27,7 +27,7 @@ import { import { iconColorCSS } from "../../../common/style/icon_color_css"; import "../../../components/ha-card"; import "../../../components/ha-icon"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { formatAttributeValue } from "../../../data/entity_attributes"; import { LightEntity } from "../../../data/light"; import { HomeAssistant } from "../../../types"; @@ -130,7 +130,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { const domain = computeStateDomain(stateObj); const showUnit = this._config.attribute ? this._config.attribute in stateObj.attributes - : !UNAVAILABLE_STATES.includes(stateObj.state); + : !isUnavailableState(stateObj.state); const name = this._config.name || computeStateName(stateObj); diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 7c1668d7b8..6678ced43a 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -17,7 +17,7 @@ import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon"; import "../../../components/ha-relative-time"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { ActionHandlerEvent, CallServiceActionConfig, @@ -315,7 +315,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { ${computeDomain(entityConf.entity) === "sensor" && stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - !UNAVAILABLE_STATES.includes(stateObj.state) + !isUnavailableState(stateObj.state) ? html` ` : html` - ${UNAVAILABLE_STATES.includes(stateObj.state) || + ${isUnavailableState(stateObj.state) || setHumidity === undefined || setHumidity === null ? "" @@ -132,8 +132,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { ${this.hass!.localize(`state.default.${stateObj.state}`)} - ${stateObj.attributes.mode && - !UNAVAILABLE_STATES.includes(stateObj.state) + ${stateObj.attributes.mode && !isUnavailableState(stateObj.state) ? html` - ${this.hass!.localize( @@ -161,7 +160,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
@@ -225,7 +224,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { } private _getSetHum(stateObj: HassEntity): undefined | number { - if (UNAVAILABLE_STATES.includes(stateObj.state)) { + if (isUnavailableState(stateObj.state)) { return undefined; } diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index c1410dbc56..2b60d831cb 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -18,7 +18,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; import "../../../components/ha-state-icon"; -import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../../../data/entity"; import { LightEntity, lightSupportsBrightness } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; @@ -118,7 +118,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { min="1" max="100" .value=${brightness} - .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} + .disabled=${isUnavailableState(stateObj.state)} @value-changing=${this._dragEvent} @value-changed=${this._setBrightness} style=${styleMap({ @@ -133,7 +133,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { "state-on": stateObj.state === "on", "state-unavailable": stateObj.state === UNAVAILABLE, })}" - .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} + .disabled=${isUnavailableState(stateObj.state)} style=${styleMap({ filter: this._computeBrightness(stateObj), color: this._computeColor(stateObj), @@ -154,7 +154,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
- ${UNAVAILABLE_STATES.includes(stateObj.state) + ${isUnavailableState(stateObj.state) ? html`
${computeStateDisplay( diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index 481daffa73..8f2f47303f 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -22,7 +22,7 @@ import "../../../components/ha-card"; import "../../../components/ha-icon-button"; import "../../../components/ha-state-icon"; import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { cleanupMediaTitle, computeMediaControls, @@ -173,7 +173,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { const isOffState = entityState === "off"; const isUnavailable = - UNAVAILABLE_STATES.includes(entityState) || + isUnavailableState(entityState) || (entityState === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON)); const hasNoImage = !this._image; const controls = computeMediaControls(stateObj, false); diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 4ac603bbfe..9d74391b99 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -20,7 +20,7 @@ import "../../../components/tile/ha-tile-image"; import "../../../components/tile/ha-tile-info"; import { cameraUrlWithWidthHeight } from "../../../data/camera"; import { CoverEntity } from "../../../data/cover"; -import { ON, UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState, ON } from "../../../data/entity"; import { FanEntity } from "../../../data/fan"; import { LightEntity } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; @@ -171,7 +171,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard { if ( (stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP || TIMESTAMP_STATE_DOMAINS.includes(domain)) && - !UNAVAILABLE_STATES.includes(stateObj.state) + !isUnavailableState(stateObj.state) ) { return html` = { cooling: mdiSnowflake, drying: mdiWaterPercent, + fan: mdiFan, heating: mdiFire, idle: mdiClockOutline, off: mdiPower, diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index df8cd17670..9de121f0dc 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -17,7 +17,7 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/search-input"; import "../../../../components/ha-circular-progress"; -import { UNAVAILABLE_STATES } from "../../../../data/entity"; +import { isUnavailableState } from "../../../../data/entity"; import type { LovelaceCardConfig, LovelaceConfig, @@ -163,12 +163,12 @@ export class HuiCardPicker extends LitElement { this._usedEntities = [...usedEntities].filter( (eid) => this.hass!.states[eid] && - !UNAVAILABLE_STATES.includes(this.hass!.states[eid].state) + !isUnavailableState(this.hass!.states[eid].state) ); this._unusedEntities = [...unusedEntities].filter( (eid) => this.hass!.states[eid] && - !UNAVAILABLE_STATES.includes(this.hass!.states[eid].state) + !isUnavailableState(this.hass!.states[eid].state) ); this._loadCards(); diff --git a/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts b/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts index 9170e34ebd..5b2b91154b 100644 --- a/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-humidifier-entity-row.ts @@ -1,6 +1,7 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/entity/ha-entity-toggle"; +import { HumidifierEntity } from "../../../data/humidifier"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; @@ -30,7 +31,7 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow { return html``; } - const stateObj = this.hass.states[this._config.entity]; + const stateObj = this.hass.states[this._config.entity] as HumidifierEntity; if (!stateObj) { return html` diff --git a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts index 93609b883f..2fb6e23fd0 100644 --- a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts @@ -8,7 +8,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-date-input"; -import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; +import { isUnavailableState, UNKNOWN } from "../../../data/entity"; import { setInputDateTimeValue, stateToIsoDateString, @@ -67,7 +67,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { @@ -83,7 +83,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { ? stateObj.state.split(" ")[1] : stateObj.state} .locale=${this.hass.locale} - .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} + .disabled=${isUnavailableState(stateObj.state)} @value-changed=${this._timeChanged} @click=${this._stopEventPropagation} > diff --git a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts index 1cdd25958c..e20bf2bb9f 100644 --- a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts @@ -12,7 +12,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { debounce } from "../../../common/util/debounce"; import "../../../components/ha-slider"; import "../../../components/ha-textfield"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { setValue } from "../../../data/input_text"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -85,7 +85,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow { ? html`
${stateObj.state === "locked" diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts index 68e5526c50..2e1fa726cc 100644 --- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts @@ -27,7 +27,7 @@ import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { debounce } from "../../../common/util/debounce"; import "../../../components/ha-icon-button"; import "../../../components/ha-slider"; -import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; +import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../../data/entity"; import { computeMediaDescription, ControlButton, @@ -203,7 +203,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
${supportsFeature(stateObj, SUPPORT_TURN_ON) && entityState === "off" && - !UNAVAILABLE_STATES.includes(entityState) + !isUnavailableState(entityState) ? html` ${this._config.action_name || diff --git a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts index 297bf97aeb..c5fc4c83ff 100644 --- a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts @@ -8,7 +8,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { isUnavailableState } from "../../../data/entity"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import { HomeAssistant } from "../../../types"; @@ -70,8 +70,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow { })} > ${stateObj.attributes.device_class === - SENSOR_DEVICE_CLASS_TIMESTAMP && - !UNAVAILABLE_STATES.includes(stateObj.state) + SENSOR_DEVICE_CLASS_TIMESTAMP && !isUnavailableState(stateObj.state) ? html`
- ${UNAVAILABLE_STATES.includes(stateObj.state) || + ${isUnavailableState(stateObj.state) || stateObj.attributes.temperature === undefined || stateObj.attributes.temperature === null ? computeStateDisplay( diff --git a/src/state-summary/state-card-display.ts b/src/state-summary/state-card-display.ts index 0d42269577..956d90cd7b 100755 --- a/src/state-summary/state-card-display.ts +++ b/src/state-summary/state-card-display.ts @@ -7,7 +7,7 @@ import { computeDomain } from "../common/entity/compute_domain"; import { computeStateDisplay } from "../common/entity/compute_state_display"; import { computeRTL } from "../common/util/compute_rtl"; import "../components/entity/state-info"; -import { UNAVAILABLE_STATES } from "../data/entity"; +import { isUnavailableState } from "../data/entity"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../data/sensor"; import "../panels/lovelace/components/hui-timestamp-display"; import { haStyle } from "../resources/styles"; @@ -42,7 +42,7 @@ export class StateCardDisplay extends LitElement { ${computeDomain(this.stateObj.entity_id) === "sensor" && this.stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - !UNAVAILABLE_STATES.includes(this.stateObj.state) + !isUnavailableState(this.stateObj.state) ? html` ${this.hass!.localize("ui.card.script.run")} diff --git a/src/state-summary/state-card-text.ts b/src/state-summary/state-card-text.ts index 5ef0456b95..563d055d4c 100644 --- a/src/state-summary/state-card-text.ts +++ b/src/state-summary/state-card-text.ts @@ -4,7 +4,7 @@ import { customElement, property } from "lit/decorators"; import { computeStateName } from "../common/entity/compute_state_name"; import { stopPropagation } from "../common/dom/stop_propagation"; import "../components/entity/state-badge"; -import { UNAVAILABLE, UNAVAILABLE_STATES } from "../data/entity"; +import { isUnavailableState, UNAVAILABLE } from "../data/entity"; import { TextEntity, setValue } from "../data/text"; import type { HomeAssistant } from "../types"; @@ -37,7 +37,7 @@ class StateCardText extends LitElement { const value = ev.target.value; // Filter out invalid text states - if (value && UNAVAILABLE_STATES.includes(value)) { + if (value && isUnavailableState(value)) { ev.target.value = this.stateObj.state; return; } From 139cbb363c25c88adcae20461895d1ea6595dd09 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Thu, 15 Dec 2022 11:02:47 +0100 Subject: [PATCH 31/70] Cover in state "closing" should be in "active" color (#14785) --- src/common/entity/state_active.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/entity/state_active.ts b/src/common/entity/state_active.ts index af5f75d126..a22ee5c9e5 100644 --- a/src/common/entity/state_active.ts +++ b/src/common/entity/state_active.ts @@ -30,7 +30,7 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean { // "on" and "off" are active, as "off" just means alert was acknowledged but is still active return compareState !== "idle"; case "cover": - return !["closed", "closing"].includes(compareState); + return compareState !== "closed"; case "device_tracker": case "person": return compareState !== "not_home"; From 5b17c59a568ccbcc35e23e367cb05adbd8b82422 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 15 Dec 2022 11:09:49 +0100 Subject: [PATCH 32/70] Check if area exists during default dashboard generation (#14767) --- .../common/generate-lovelace-config.ts | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 12dd3fc537..07b99cb22d 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -47,6 +47,7 @@ interface SplittedByAreaDevice { } const splitByAreaDevice = ( + areaEntries: HomeAssistant["areas"], deviceEntries: HomeAssistant["devices"], entityEntries: HomeAssistant["entities"], entities: HassEntities @@ -55,25 +56,21 @@ const splitByAreaDevice = ( const areasWithEntities: SplittedByAreaDevice["areasWithEntities"] = {}; const devicesWithEntities: SplittedByAreaDevice["devicesWithEntities"] = {}; - const areaDevices = new Set( - Object.values(deviceEntries) - .filter((device) => device.area_id) - .map((device) => device.id) - ); for (const entity of Object.values(entityEntries)) { - if ( - (entity.area_id || - (entity.device_id && areaDevices.has(entity.device_id))) && - entity.entity_id in allEntities - ) { - const areaId = - entity.area_id || deviceEntries[entity.device_id!].area_id!; + const areaId = + entity.area_id || + (entity.device_id && deviceEntries[entity.device_id].area_id); + if (areaId && areaId in areaEntries && entity.entity_id in allEntities) { if (!(areaId in areasWithEntities)) { areasWithEntities[areaId] = []; } areasWithEntities[areaId].push(allEntities[entity.entity_id]); delete allEntities[entity.entity_id]; - } else if (entity.device_id && entity.entity_id in allEntities) { + } else if ( + entity.device_id && + entity.device_id in deviceEntries && + entity.entity_id in allEntities + ) { if (!(entity.device_id in devicesWithEntities)) { devicesWithEntities[entity.device_id] = []; } @@ -460,6 +457,7 @@ export const generateDefaultViewConfig = ( } const splittedByAreaDevice = splitByAreaDevice( + areaEntries, deviceEntries, entityEntries, states From b18160d98735fecc15ecef39315011f6c3b58941 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 15 Dec 2022 11:27:45 +0100 Subject: [PATCH 33/70] Use CSS colors for tile components (#14770) * Do not use rgb colors for tile components * Fixes gallery * Change tile color * Do not use rgb colors in tile button --- gallery/src/pages/components/ha-bar-slider.ts | 3 +- gallery/src/pages/components/ha-bar-switch.ts | 4 +- src/common/color/compute-color.ts | 13 +---- src/components/ha-bar-slider.ts | 4 +- src/components/ha-bar-switch.ts | 27 +++++++---- src/components/tile/ha-tile-button.ts | 37 +++++++++----- src/components/tile/ha-tile-icon.ts | 20 ++++++-- src/components/tile/ha-tile-slider.ts | 10 ++-- src/panels/lovelace/cards/hui-tile-card.ts | 48 ++++++++++++------- .../lovelace/components/hui-color-picker.ts | 6 +-- .../hui-light-brightness-tile-feature.ts | 5 +- 11 files changed, 113 insertions(+), 64 deletions(-) diff --git a/gallery/src/pages/components/ha-bar-slider.ts b/gallery/src/pages/components/ha-bar-slider.ts index ece20c2a7f..261169d6dc 100644 --- a/gallery/src/pages/components/ha-bar-slider.ts +++ b/gallery/src/pages/components/ha-bar-slider.ts @@ -142,7 +142,8 @@ export class DemoHaBarSlider extends LitElement { } .custom { --slider-bar-color: #ffcf4c; - --slider-bar-background: #ffcf4c64; + --slider-bar-background: #ffcf4c; + --slider-bar-background-opacity: 0.2; --slider-bar-thickness: 100px; --slider-bar-border-radius: 24px; } diff --git a/gallery/src/pages/components/ha-bar-switch.ts b/gallery/src/pages/components/ha-bar-switch.ts index a9de6f0905..aeca191bff 100644 --- a/gallery/src/pages/components/ha-bar-switch.ts +++ b/gallery/src/pages/components/ha-bar-switch.ts @@ -115,8 +115,8 @@ export class DemoHaBarSwitch extends LitElement { font-weight: 600; } .custom { - --switch-bar-color-on: var(--rgb-green-color); - --switch-bar-color-off: var(--rgb-red-color); + --switch-bar-on-color: rgb(var(--rgb-green-color)); + --switch-bar-off-color: rgb(var(--rgb-red-color)); --switch-bar-thickness: 100px; --switch-bar-border-radius: 24px; --switch-bar-padding: 6px; diff --git a/src/common/color/compute-color.ts b/src/common/color/compute-color.ts index e7e3409a9d..9408b1e7ac 100644 --- a/src/common/color/compute-color.ts +++ b/src/common/color/compute-color.ts @@ -1,5 +1,3 @@ -import { hex2rgb } from "./convert-color"; - export const THEME_COLORS = new Set([ "primary", "accent", @@ -27,16 +25,9 @@ export const THEME_COLORS = new Set([ "white", ]); -export function computeRgbColor(color: string): string { +export function computeCssColor(color: string): string { if (THEME_COLORS.has(color)) { - return `var(--rgb-${color}-color)`; - } - if (color.startsWith("#")) { - try { - return hex2rgb(color).join(", "); - } catch (err) { - return ""; - } + return `rgb(var(--rgb-${color}-color))`; } return color; } diff --git a/src/components/ha-bar-slider.ts b/src/components/ha-bar-slider.ts index a50fd154ad..11b8558cda 100644 --- a/src/components/ha-bar-slider.ts +++ b/src/components/ha-bar-slider.ts @@ -272,7 +272,8 @@ export class HaBarSlider extends LitElement { :host { display: block; --slider-bar-color: rgb(var(--rgb-primary-color)); - --slider-bar-background: rgba(var(--rgb-disabled-color), 0.2); + --slider-bar-background: rgb(var(--rgb-disabled-color)); + --slider-bar-background-opacity: 0.2; --slider-bar-thickness: 40px; --slider-bar-border-radius: 10px; height: var(--slider-bar-thickness); @@ -301,6 +302,7 @@ export class HaBarSlider extends LitElement { height: 100%; width: 100%; background: var(--slider-bar-background); + opacity: var(--slider-bar-background-opacity); } .slider .slider-track-bar { --border-radius: var(--slider-bar-border-radius); diff --git a/src/components/ha-bar-switch.ts b/src/components/ha-bar-switch.ts index 13b22a286b..7c0f687e59 100644 --- a/src/components/ha-bar-switch.ts +++ b/src/components/ha-bar-switch.ts @@ -74,6 +74,7 @@ export class HaBarSwitch extends LitElement { protected render(): TemplateResult { return html`
+