From 9c24dbe33364ab62f2c9633dad78db1af45b885f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 21 Dec 2022 11:22:09 +0100 Subject: [PATCH 01/20] Enforces disabled to false for ha-form (#14842) --- src/components/ha-form/ha-form.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index ce5208ec67..4e9c74da46 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -85,7 +85,7 @@ export class HaForm extends LitElement implements HaFormElement { .selector=${item.selector} .value=${getValue(this.data, item)} .label=${this._computeLabel(item, this.data)} - .disabled=${item.disabled || this.disabled} + .disabled=${item.disabled || this.disabled || false} .helper=${this._computeHelper(item)} .required=${item.required || false} .context=${this._generateContext(item)} @@ -95,7 +95,7 @@ export class HaForm extends LitElement implements HaFormElement { data: getValue(this.data, item), label: this._computeLabel(item, this.data), helper: this._computeHelper(item), - disabled: this.disabled || item.disabled, + disabled: this.disabled || item.disabled || false, hass: this.hass, computeLabel: this.computeLabel, computeHelper: this.computeHelper, From a7c3774c29b4398d022e3aa7509363d35c13bef5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 21 Dec 2022 11:22:48 +0100 Subject: [PATCH 02/20] Fixes alarm triggered color in alarm card (#14840) --- src/panels/lovelace/cards/hui-alarm-panel-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.ts b/src/panels/lovelace/cards/hui-alarm-panel-card.ts index b9d01da1c0..4ec7cda0a9 100644 --- a/src/panels/lovelace/cards/hui-alarm-panel-card.ts +++ b/src/panels/lovelace/cards/hui-alarm-panel-card.ts @@ -295,7 +295,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard { } .triggered { - --alarm-state-color: rgb(var(--rgb-state-alarm-trigger-color)); + --alarm-state-color: rgb(var(--rgb-state-alarm-triggered-color)); animation: pulse 1s infinite; } From ae04a5457e2484bb6967dcf89b3b37779b5b4f56 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 21 Dec 2022 11:51:35 +0100 Subject: [PATCH 03/20] Allow custom value for multiple select (#14839) --- src/components/ha-selector/ha-selector-select.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index 4029846998..9602131040 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -118,9 +118,11 @@ export class HaSelectSelector extends LitElement { .disabled=${this.disabled} .required=${this.required && !value.length} .value=${this._filter} + .items=${options} .filteredItems=${options.filter( (option) => !option.disabled && !value?.includes(option.value) )} + .allowCustomValue=${this.selector.select.custom_value ?? false} @filter-changed=${this._filterChanged} @value-changed=${this._comboBoxValueChanged} > From 825008e24a9c88ec3328405e72d762263bd25d1a Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 21 Dec 2022 17:06:40 +0100 Subject: [PATCH 04/20] Use a capitol for `Topic` in MQTT config panel. (#14843) --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 5cc1fd033d..58faa9c941 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3225,7 +3225,7 @@ "mqtt": { "title": "MQTT", "description_publish": "Publish a packet", - "topic": "topic", + "topic": "Topic", "payload": "Payload (template allowed)", "publish": "Publish", "description_listen": "Listen to a topic", From 36e99c3c0f430593ec4b17140d6668b4ef4d13c8 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 21 Dec 2022 08:07:31 -0800 Subject: [PATCH 05/20] Fix calendar date display and parsing issues (#14817) Co-authored-by: Bram Kragten fixes undefined --- package.json | 1 + .../calendar/dialog-calendar-event-detail.ts | 10 +- .../calendar/dialog-calendar-event-editor.ts | 116 ++++++++---------- yarn.lock | 10 ++ 4 files changed, 69 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index 51f4fca91d..531d6e4246 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "core-js": "^3.15.2", "cropperjs": "^1.5.12", "date-fns": "^2.23.0", + "date-fns-tz": "^1.3.7", "deep-clone-simple": "^1.1.1", "deep-freeze": "^0.0.1", "fuse.js": "^6.0.0", diff --git a/src/panels/calendar/dialog-calendar-event-detail.ts b/src/panels/calendar/dialog-calendar-event-detail.ts index 276459a0da..3f16f2c82c 100644 --- a/src/panels/calendar/dialog-calendar-event-detail.ts +++ b/src/panels/calendar/dialog-calendar-event-detail.ts @@ -1,6 +1,7 @@ import "@material/mwc-button"; import { mdiCalendarClock, mdiClose } from "@mdi/js"; import { addDays, isSameDay } from "date-fns/esm"; +import { toDate } from "date-fns-tz"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { property, state } from "lit/decorators"; import { RRule, Weekday } from "rrule"; @@ -185,11 +186,12 @@ class DialogCalendarEventDetail extends LitElement { }; private _formatDateRange() { - const start = new Date(this._data!.dtstart); + // Parse a dates in the browser timezone + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const start = toDate(this._data!.dtstart, { timeZone: timeZone }); + const endValue = toDate(this._data!.dtend, { timeZone: timeZone }); // All day events should be displayed as a day earlier - const end = isDate(this._data.dtend) - ? addDays(new Date(this._data!.dtend), -1) - : new Date(this._data!.dtend); + const end = isDate(this._data.dtend) ? addDays(endValue, -1) : endValue; // The range can be shortened when the start and end are on the same day. if (isSameDay(start, end)) { if (isDate(this._data.dtstart)) { diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 4efbeec587..e76341c474 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -7,6 +7,7 @@ import { differenceInMilliseconds, startOfHour, } from "date-fns/esm"; +import { formatInTimeZone, toDate } from "date-fns-tz"; import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -60,6 +61,12 @@ class DialogCalendarEventEditor extends LitElement { @state() private _submitting = false; + // Dates are manipulated and displayed in the browser timezone + // which may be different from the Home Assistant timezone. When + // events are persisted, they are relative to the Home Assistant + // timezone, but floating without a timezone. + private _timeZone?: string; + public showDialog(params: CalendarEventEditDialogParams): void { this._error = undefined; this._info = undefined; @@ -71,6 +78,9 @@ class DialogCalendarEventEditor extends LitElement { computeStateDomain(stateObj) === "calendar" && supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT) )?.entity_id; + this._timeZone = + Intl.DateTimeFormat().resolvedOptions().timeZone || + this.hass.config.time_zone; if (params.entry) { const entry = params.entry!; this._allDay = isDate(entry.dtstart); @@ -281,20 +291,30 @@ 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 - { - const timeZone = this.hass.config.time_zone; - return { - startDate: startDate?.toLocaleDateString("en-CA", { timeZone }), - startTime: startDate?.toLocaleTimeString("en-GB", { timeZone }), - endDate: endDate?.toLocaleDateString("en-CA", { timeZone }), - endTime: endDate?.toLocaleTimeString("en-GB", { timeZone }), - }; - } + private _getLocaleStrings = memoizeOne( + (startDate?: Date, endDate?: Date) => ({ + startDate: this._formatDate(startDate!), + startTime: this._formatTime(startDate!), + endDate: this._formatDate(endDate!), + endTime: this._formatTime(endDate!), + }) ); + // Formats a date in specified timezone, or defaulting to browser display timezone + private _formatDate(date: Date, timeZone: string = this._timeZone!): string { + return formatInTimeZone(date, timeZone, "yyyy-MM-dd"); + } + + // Formats a time in specified timezone, or defaulting to browser display timezone + private _formatTime(date: Date, timeZone: string = this._timeZone!): string { + return formatInTimeZone(date, timeZone, "HH:mm:ss"); // 24 hr + } + + // Parse a date in the browser timezone + private _parseDate(dateStr: string): Date { + return toDate(dateStr, { timeZone: this._timeZone! }); + } + private _clearInfo() { this._info = undefined; } @@ -319,27 +339,14 @@ class DialogCalendarEventEditor extends LitElement { // Store previous event duration const duration = differenceInMilliseconds(this._dtend!, this._dtstart!); - this._dtstart = new Date( - ev.detail.value + - "T" + - this._dtstart!.toLocaleTimeString("en-GB", { - timeZone: this.hass.config.time_zone, - }) + this._dtstart = this._parseDate( + `${ev.detail.value}T${this._formatTime(this._dtstart!)}` ); // Prevent that the end time can be before the start time. Try to keep the // duration the same. if (this._dtend! <= this._dtstart!) { - const newEnd = addMilliseconds(this._dtstart, duration); - // en-CA locale used for date format YYYY-MM-DD - // en-GB locale used for 24h time format HH:MM:SS - this._dtend = new Date( - `${newEnd.toLocaleDateString("en-CA", { - timeZone: this.hass.config.time_zone, - })}T${newEnd.toLocaleTimeString("en-GB", { - timeZone: this.hass.config.time_zone, - })}` - ); + this._dtend = addMilliseconds(this._dtstart, duration); this._info = this.hass.localize( "ui.components.calendar.event.end_auto_adjusted" ); @@ -347,12 +354,8 @@ class DialogCalendarEventEditor extends LitElement { } private _endDateChanged(ev: CustomEvent) { - this._dtend = new Date( - ev.detail.value + - "T" + - this._dtend!.toLocaleTimeString("en-GB", { - timeZone: this.hass.config.time_zone, - }) + this._dtend = this._parseDate( + `${ev.detail.value}T${this._formatTime(this._dtend!)}` ); } @@ -360,25 +363,14 @@ class DialogCalendarEventEditor extends LitElement { // Store previous event duration const duration = differenceInMilliseconds(this._dtend!, this._dtstart!); - this._dtstart = new Date( - this._dtstart!.toLocaleDateString("en-CA", { - timeZone: this.hass.config.time_zone, - }) + - "T" + - ev.detail.value + this._dtstart = this._parseDate( + `${this._formatDate(this._dtstart!)}T${ev.detail.value}` ); // Prevent that the end time can be before the start time. Try to keep the // duration the same. if (this._dtend! <= this._dtstart!) { - const newEnd = addMilliseconds(new Date(this._dtstart), duration); - this._dtend = new Date( - `${newEnd.toLocaleDateString("en-CA", { - timeZone: this.hass.config.time_zone, - })}T${newEnd.toLocaleTimeString("en-GB", { - timeZone: this.hass.config.time_zone, - })}` - ); + this._dtend = addMilliseconds(new Date(this._dtstart), duration); this._info = this.hass.localize( "ui.components.calendar.event.end_auto_adjusted" ); @@ -386,20 +378,12 @@ class DialogCalendarEventEditor extends LitElement { } private _endTimeChanged(ev: CustomEvent) { - this._dtend = new Date( - this._dtend!.toLocaleDateString("en-CA", { - timeZone: this.hass.config.time_zone, - }) + - "T" + - ev.detail.value + this._dtend = this._parseDate( + `${this._formatDate(this._dtend!)}T${ev.detail.value}` ); } private _calculateData() { - const { startDate, startTime, endDate, endTime } = this._getLocaleStrings( - this._dtstart, - this._dtend - ); const data: CalendarEventMutableParams = { summary: this._summary, description: this._description, @@ -408,14 +392,18 @@ class DialogCalendarEventEditor extends LitElement { dtend: "", }; if (this._allDay) { - data.dtstart = startDate!; + data.dtstart = this._formatDate(this._dtstart!); // End date/time is exclusive when persisted - data.dtend = addDays(new Date(this._dtend!), 1).toLocaleDateString( - "en-CA" - ); + data.dtend = this._formatDate(addDays(this._dtend!, 1)); } else { - data.dtstart = `${startDate}T${startTime}`; - data.dtend = `${endDate}T${endTime}`; + data.dtstart = `${this._formatDate( + this._dtstart!, + this.hass.config.time_zone + )}T${this._formatTime(this._dtstart!, this.hass.config.time_zone)}`; + data.dtend = `${this._formatDate( + this._dtend!, + this.hass.config.time_zone + )}T${this._formatTime(this._dtend!, this.hass.config.time_zone)}`; } return data; } diff --git a/yarn.lock b/yarn.lock index 40ccc226bd..c81d28f903 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6993,6 +6993,15 @@ __metadata: languageName: node linkType: hard +"date-fns-tz@npm:^1.3.7": + version: 1.3.7 + resolution: "date-fns-tz@npm:1.3.7" + peerDependencies: + date-fns: ">=2.0.0" + checksum: b749613669223056d5e6d715114c94bec57234b676d0cea0c72ca710626c81e9ea04df6441852a5fec74b42c5f27b2f076e13697ec43da360b67806a3042a10e + languageName: node + linkType: hard + "date-fns@npm:^2.23.0": version: 2.23.0 resolution: "date-fns@npm:2.23.0" @@ -9427,6 +9436,7 @@ fsevents@^1.2.7: core-js: ^3.15.2 cropperjs: ^1.5.12 date-fns: ^2.23.0 + date-fns-tz: ^1.3.7 deep-clone-simple: ^1.1.1 deep-freeze: ^0.0.1 del: ^4.0.0 From 491934187118e4847f5ac3c0d534de3bbaef8003 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 22 Dec 2022 12:47:33 +0100 Subject: [PATCH 06/20] Fix localization `Solar total` in Enery dashboard (#14841) Co-authored-by: Bram Kragten fixes undefined --- .../lovelace/cards/energy/hui-energy-sources-table-card.ts | 4 +++- src/translations/en.json | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts index d44bd49f5d..9a05d71990 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -279,7 +279,9 @@ export class HuiEnergySourcesTableCard ? html` - Solar total + ${this.hass.localize( + "ui.panel.lovelace.cards.energy.energy_sources_table.solar_total" + )} ${compare ? html` Date: Thu, 22 Dec 2022 03:48:48 -0800 Subject: [PATCH 07/20] Fix zwave automations not handling 0 values in the visual editor (#14835) Co-authored-by: Bram Kragten fixes undefined --- src/components/ha-selector/ha-selector-select.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index 9602131040..9e91b65fbe 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -192,7 +192,7 @@ export class HaSelectSelector extends LitElement { private _valueChanged(ev) { ev.stopPropagation(); const value = ev.detail?.value || ev.target.value; - if (this.disabled || !value) { + if (this.disabled || value === undefined) { return; } fireEvent(this, "value-changed", { From 5933c2eb8e2e695d1300f4dc011e08f1607d03fc Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 22 Dec 2022 12:51:36 +0100 Subject: [PATCH 08/20] Add Calendar redirect support for My (#14859) --- src/panels/my/ha-panel-my.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/panels/my/ha-panel-my.ts b/src/panels/my/ha-panel-my.ts index c5e60b5540..7960d31dba 100644 --- a/src/panels/my/ha-panel-my.ts +++ b/src/panels/my/ha-panel-my.ts @@ -41,6 +41,10 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({ server_controls: { redirect: "/developer-tools/yaml", }, + calendar: { + component: "calendar", + redirect: "/calendar", + }, config: { redirect: "/config/dashboard", }, From 9be6a47d8871a1df88b1b2c7d281b880e26e5724 Mon Sep 17 00:00:00 2001 From: smonesi <63957397+smonesi@users.noreply.github.com> Date: Thu, 22 Dec 2022 12:52:15 +0100 Subject: [PATCH 09/20] Attempt to fix picture-elements functionality broken in 2022.11 (#14813) fixes undefined --- src/panels/lovelace/components/hui-image.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 555bca93ad..573210f86d 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -193,7 +193,7 @@ export class HuiImage extends LitElement { style=${styleMap({ paddingBottom: useRatio ? `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%` - : !this._lastImageHeight + : this._lastImageHeight === undefined ? "56.25%" : undefined, backgroundImage: @@ -206,7 +206,7 @@ export class HuiImage extends LitElement { : undefined, })} class="container ${classMap({ - ratio: useRatio || !this._lastImageHeight, + ratio: useRatio || this._lastImageHeight === undefined, })}" > ${this.cameraImage && this.cameraView === "live" From 40cf15c1f33efea611f1df387024071c73f27002 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 22 Dec 2022 12:59:00 +0100 Subject: [PATCH 10/20] Fix history type device class (#14851) --- src/data/cached-history.ts | 63 ++----------------- src/data/history.ts | 27 +++++--- src/dialogs/more-info/ha-more-info-history.ts | 2 +- .../lovelace/cards/hui-history-graph-card.ts | 2 +- 4 files changed, 27 insertions(+), 67 deletions(-) diff --git a/src/data/cached-history.ts b/src/data/cached-history.ts index 30fa4956f9..2e4bf825f4 100644 --- a/src/data/cached-history.ts +++ b/src/data/cached-history.ts @@ -23,63 +23,8 @@ interface CachedResults { data: HistoryResult; } -// This is a different interface, a different cache :( -interface RecentCacheResults { - created: number; - language: string; - data: Promise; -} - -const RECENT_THRESHOLD = 60000; // 1 minute -const RECENT_CACHE: { [cacheKey: string]: RecentCacheResults } = {}; const stateHistoryCache: { [cacheKey: string]: CachedResults } = {}; -// Cached type 1 function. Without cache config. -export const getRecent = ( - hass: HomeAssistant, - entityId: string, - startTime: Date, - endTime: Date, - localize: LocalizeFunc, - language: string -) => { - const cacheKey = entityId; - const cache = RECENT_CACHE[cacheKey]; - - if ( - cache && - Date.now() - cache.created < RECENT_THRESHOLD && - cache.language === language - ) { - return cache.data; - } - - const noAttributes = !entityIdHistoryNeedsAttributes(hass, entityId); - const prom = fetchRecentWS( - hass, - entityId, - startTime, - endTime, - false, - undefined, - true, - noAttributes - ).then( - (stateHistory) => computeHistory(hass, stateHistory, localize), - (err) => { - delete RECENT_CACHE[entityId]; - throw err; - } - ); - - RECENT_CACHE[cacheKey] = { - created: Date.now(), - language, - data: prom, - }; - return prom; -}; - // Cache type 2 functionality function getEmptyCache( language: string, @@ -97,7 +42,7 @@ function getEmptyCache( export const getRecentWithCache = ( hass: HomeAssistant, - entityId: string, + entityIds: string[], cacheConfig: CacheConfig, localize: LocalizeFunc, language: string @@ -132,7 +77,9 @@ export const getRecentWithCache = ( } const curCacheProm = cache.prom; - const noAttributes = !entityIdHistoryNeedsAttributes(hass, entityId); + const noAttributes = !entityIds.some((entityId) => + entityIdHistoryNeedsAttributes(hass, entityId) + ); const genProm = async () => { let fetchedHistory: HistoryStates; @@ -142,7 +89,7 @@ export const getRecentWithCache = ( curCacheProm, fetchRecentWS( hass, - entityId, + entityIds, toFetchStartTime, endTime, appendingToCache, diff --git a/src/data/history.ts b/src/data/history.ts index 9940292b72..73688d4057 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -1,4 +1,8 @@ -import { HassEntities, HassEntity } from "home-assistant-js-websocket"; +import { + HassEntities, + HassEntity, + HassEntityAttributeBase, +} from "home-assistant-js-websocket"; import { computeDomain } from "../common/entity/compute_domain"; import { computeStateDisplayFromEntityAttributes } from "../common/entity/compute_state_display"; import { computeStateNameFromEntityAttributes } from "../common/entity/compute_state_name"; @@ -117,7 +121,7 @@ export const fetchRecent = ( export const fetchRecentWS = ( hass: HomeAssistant, - entityId: string, // This may be CSV + entityIds: string[], startTime: Date, endTime: Date, skipInitialState = false, @@ -133,7 +137,7 @@ export const fetchRecentWS = ( include_start_time_state: !skipInitialState, minimal_response: minimalResponse, no_attributes: noAttributes || false, - entity_ids: entityId.split(","), + entity_ids: entityIds, }); export const fetchDate = ( @@ -160,9 +164,9 @@ export const fetchDateWS = ( start_time: startTime.toISOString(), end_time: endTime.toISOString(), minimal_response: true, - no_attributes: !entityIds - .map((entityId) => entityIdHistoryNeedsAttributes(hass, entityId)) - .reduce((cur, next) => cur || next, false), + no_attributes: !entityIds.some((entityId) => + entityIdHistoryNeedsAttributes(hass, entityId) + ), }; if (entityIds.length !== 0) { return hass.callWS({ ...params, entity_ids: entityIds }); @@ -195,13 +199,22 @@ const processTimelineEntity = ( if (data.length > 0 && state.s === data[data.length - 1].state) { continue; } + + const currentAttributes: HassEntityAttributeBase = {}; + if (current_state?.attributes.device_class) { + currentAttributes.device_class = current_state?.attributes.device_class; + } + data.push({ state_localize: computeStateDisplayFromEntityAttributes( localize, language, entities, entityId, - state.a || first.a, + { + ...(state.a || first.a), + ...currentAttributes, + }, state.s ), state: state.s, diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index a9f30604f1..73061a09b9 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -139,7 +139,7 @@ export class MoreInfoHistory extends LitElement { } this._stateHistory = await getRecentWithCache( this.hass!, - this.entityId, + [this.entityId], { cacheKey: `more_info.${this.entityId}`, hoursToShow: 24, diff --git a/src/panels/lovelace/cards/hui-history-graph-card.ts b/src/panels/lovelace/cards/hui-history-graph-card.ts index c18864e4b3..c5bf22d9fb 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.ts +++ b/src/panels/lovelace/cards/hui-history-graph-card.ts @@ -162,7 +162,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { this._stateHistory = { ...(await getRecentWithCache( this.hass!, - this._cacheConfig!.cacheKey, + this._configEntities!.map((config) => config.entity), this._cacheConfig!, this.hass!.localize, this.hass!.language From 7780ae8f76bec76636282d1c08c2ad62273f4111 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 22 Dec 2022 13:03:14 +0100 Subject: [PATCH 11/20] Fixes select selector filter (#14850) --- .../ha-selector/ha-selector-select.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index 9e91b65fbe..ac2ba9d63b 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -88,6 +88,10 @@ export class HaSelectSelector extends LitElement { const value = !this.value || this.value === "" ? [] : (this.value as string[]); + const optionItems = options.filter( + (option) => !option.disabled && !value?.includes(option.value) + ); + return html` ${value?.length ? html` @@ -118,13 +122,11 @@ export class HaSelectSelector extends LitElement { .disabled=${this.disabled} .required=${this.required && !value.length} .value=${this._filter} - .items=${options} - .filteredItems=${options.filter( - (option) => !option.disabled && !value?.includes(option.value) - )} + .items=${optionItems} .allowCustomValue=${this.selector.select.custom_value ?? false} @filter-changed=${this._filterChanged} @value-changed=${this._comboBoxValueChanged} + @opened-changed=${this._openedChanged} > `; } @@ -132,11 +134,14 @@ export class HaSelectSelector extends LitElement { if (this.selector.select?.custom_value) { if ( this.value !== undefined && + !Array.isArray(this.value) && !options.find((option) => option.value === this.value) ) { options.unshift({ value: this.value, label: this.value }); } + const optionItems = options.filter((option) => !option.disabled); + return html` !item.disabled)} + .items=${optionItems} .value=${this.value} @filter-changed=${this._filterChanged} @value-changed=${this._comboBoxValueChanged} + @opened-changed=${this._openedChanged} > `; } @@ -273,13 +279,16 @@ export class HaSelectSelector extends LitElement { }); } + private _openedChanged(ev?: CustomEvent): void { + if (ev?.detail.value) { + this._filterChanged(); + } + } + private _filterChanged(ev?: CustomEvent): void { this._filter = ev?.detail.value || ""; const filteredItems = this.comboBox.items?.filter((item) => { - if (this.selector.select?.multiple && this.value?.includes(item.value)) { - return false; - } const label = item.label || item.value; return label.toLowerCase().includes(this._filter?.toLowerCase()); }); From f41330a29b8b02ecbbb4c1db851c202c89336dd8 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 22 Dec 2022 15:43:55 +0100 Subject: [PATCH 12/20] Allign MQTT config panel controls (#14818) Co-authored-by: Bram Kragten --- .../mqtt/mqtt-config-panel.ts | 68 ++++++++++------ .../mqtt/mqtt-subscribe-card.ts | 77 ++++++++++++------- 2 files changed, 97 insertions(+), 48 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 a60764a184..5ab4a00ab2 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 @@ -50,28 +50,30 @@ class HaPanelDevMqtt extends LitElement { )} >
- - ${qosLevel.map( - (qos) => - html`${qos}` - )} - - - - +
+ + ${qosLevel.map( + (qos) => + html`${qos}` + )} + + + + +

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

- - ${qosLevel.map( - (qos) => html`${qos}` - )} - - - ${this._subscribed - ? this.hass.localize("ui.panel.config.mqtt.stop_listening") - : this.hass.localize("ui.panel.config.mqtt.start_listening")} - +
+ + ${qosLevel.map( + (qos) => + html`${qos}` + )} + + + ${this._subscribed + ? this.hass.localize("ui.panel.config.mqtt.stop_listening") + : this.hass.localize("ui.panel.config.mqtt.start_listening")} + +
${this._messages.map( @@ -170,6 +173,28 @@ class MqttSubscribeCard extends LitElement { pre { font-family: var(--code-font-family, monospace); } + .panel-dev-mqtt-subscribe-fields { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + } + ha-select { + width: 96px; + margin: 0 8px; + } + ha-textfield { + flex: 1; + } + @media screen and (max-width: 600px) { + ha-select { + margin-left: 0px; + margin-top: 8px; + } + ha-textfield { + flex: auto; + width: 100%; + } + } `; } } From 6bb350b5ec05c31813096f6980bc94703abc554a Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 22 Dec 2022 08:14:30 -0800 Subject: [PATCH 13/20] Fix bug in non-recurring calendar event creation (#14854) Co-authored-by: Bram Kragten fixes undefined --- src/panels/calendar/dialog-calendar-event-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index e76341c474..19965a748e 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -387,7 +387,7 @@ class DialogCalendarEventEditor extends LitElement { const data: CalendarEventMutableParams = { summary: this._summary, description: this._description, - rrule: this._rrule, + rrule: this._rrule || undefined, dtstart: "", dtend: "", }; From 31993198307c28ebabb46d4ec4873b9789086b6a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 22 Dec 2022 17:20:40 +0100 Subject: [PATCH 14/20] Fix water compare (#14864) --- src/data/energy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/energy.ts b/src/data/energy.ts index 3e412bd127..4110bcde82 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -451,7 +451,7 @@ const getEnergyData = async ( ...(await fetchStatistics( hass!, compareStartMinHour, - end, + endCompare, waterStatIds, period, waterUnits, From 526c34993c207542f25ccce22571d4d09de9b78e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 23 Dec 2022 20:37:58 -0500 Subject: [PATCH 15/20] Allow opening conversation dialog via URL (#14868) * Allow opening conversation dialog via URL * Update URL --- src/panels/lovelace/hui-root.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 9637884550..37208ba078 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -38,9 +38,11 @@ import { fireEvent } from "../../common/dom/fire_event"; import scrollToTarget from "../../common/dom/scroll-to-target"; import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; import { navigate } from "../../common/navigate"; +import { constructUrlCurrentPath } from "../../common/url/construct-url"; import { addSearchParam, - extractSearchParam, + extractSearchParamsObject, + removeSearchParam, } from "../../common/url/search-params"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; @@ -556,8 +558,16 @@ class HUIRoot extends LitElement { protected firstUpdated() { // Check for requested edit mode - if (extractSearchParam("edit") === "1") { + const searchParams = extractSearchParamsObject(); + if (searchParams.edit === "1") { this.lovelace!.setEditMode(true); + } else if (searchParams.conversation === "1") { + showVoiceCommandDialog(this); + window.history.replaceState( + null, + "", + constructUrlCurrentPath(removeSearchParam("conversation")) + ); } } From a5863a9a67dc14d936caed6c3512b7101f79f321 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 23 Dec 2022 20:49:07 -0500 Subject: [PATCH 16/20] Redirect to new Matter device (#14867) * Redirect to new Matter device * Use hass.devices --- .../matter/matter-config-panel.ts | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/integration-panels/matter/matter-config-panel.ts b/src/panels/config/integrations/integration-panels/matter/matter-config-panel.ts index 852df1ebe1..77fa19ca74 100644 --- a/src/panels/config/integrations/integration-panels/matter/matter-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/matter/matter-config-panel.ts @@ -1,5 +1,5 @@ import "@material/mwc-button"; -import { css, html, LitElement, TemplateResult } from "lit"; +import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../../../components/ha-card"; import { @@ -13,6 +13,7 @@ import { haStyle } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; import "../../../../../components/ha-alert"; import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box"; +import { navigate } from "../../../../../common/navigate"; @customElement("matter-config-panel") export class MatterConfigPanel extends LitElement { @@ -22,6 +23,8 @@ export class MatterConfigPanel extends LitElement { @state() private _error?: string; + private _curMatterDevices?: Set; + private get _canCommissionMatter() { return this.hass.auth.external?.config.canCommissionMatter; } @@ -68,7 +71,30 @@ export class MatterConfigPanel extends LitElement { `; } + protected override updated(changedProps: PropertyValues) { + super.updated(changedProps); + + if (!this._curMatterDevices || !changedProps.has("hass")) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + if (!oldHass || oldHass.devices === this.hass.devices) { + return; + } + + const newMatterDevices = Object.values(this.hass.devices).filter( + (device) => + device.identifiers.find((identifier) => identifier[0] === "matter") && + !this._curMatterDevices!.has(device.id) + ); + if (newMatterDevices.length) { + navigate(`/config/devices/device/${newMatterDevices[0].id}`); + } + } + private _startMobileCommissioning() { + this._redirectOnNewDevice(); this.hass.auth.external!.fireMessage({ type: "matter/commission", }); @@ -112,6 +138,7 @@ export class MatterConfigPanel extends LitElement { return; } this._error = undefined; + this._redirectOnNewDevice(); try { await commissionMatterDevice(this.hass, code); } catch (err: any) { @@ -130,6 +157,7 @@ export class MatterConfigPanel extends LitElement { return; } this._error = undefined; + this._redirectOnNewDevice(); try { await acceptSharedMatterDevice(this.hass, Number(code)); } catch (err: any) { @@ -155,6 +183,19 @@ export class MatterConfigPanel extends LitElement { } } + private _redirectOnNewDevice() { + if (this._curMatterDevices) { + return; + } + this._curMatterDevices = new Set( + Object.values(this.hass.devices) + .filter((device) => + device.identifiers.find((identifier) => identifier[0] === "matter") + ) + .map((device) => device.id) + ); + } + static styles = [ haStyle, css` From 77e01812d1487db2be312511f866b831ad348086 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Dec 2022 08:25:47 +0100 Subject: [PATCH 17/20] Bump actions/stale from 6.0.1 to 7.0.0 (#14886) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 8ddf8091ce..bd5f019615 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 90 days stale policy - uses: actions/stale@v6.0.1 + uses: actions/stale@v7.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 90 From c6aa2886eddf716b75848a915f4dc95c0aedeeb2 Mon Sep 17 00:00:00 2001 From: albatorsk Date: Tue, 27 Dec 2022 13:55:16 +0100 Subject: [PATCH 18/20] Change Z-Wave to Zigbee in help setup dialog (#14892) --- src/common/integrations/protocolIntegrationPicked.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/integrations/protocolIntegrationPicked.ts b/src/common/integrations/protocolIntegrationPicked.ts index 52bd0b6316..a1f11551c4 100644 --- a/src/common/integrations/protocolIntegrationPicked.ts +++ b/src/common/integrations/protocolIntegrationPicked.ts @@ -86,7 +86,7 @@ export const protocolIntegrationPicked = async ( "ui.panel.config.integrations.config_flow.missing_zwave_zigbee", { integration: "Zigbee", - brand: options?.brand || options?.domain || "Z-Wave", + brand: options?.brand || options?.domain || "Zigbee", supported_hardware_link: html` Date: Tue, 27 Dec 2022 21:08:47 +0100 Subject: [PATCH 19/20] Uses backend translation for climate attributes (#14827) Co-authored-by: Bram Kragten --- .../entity/compute_attribute_display.ts | 52 ++++++++ src/components/ha-climate-state.ts | 25 ++-- src/data/climate.ts | 38 +++--- src/data/translation.ts | 1 + .../more-info/controls/more-info-climate.ts | 115 +++++++++++++----- src/layouts/home-assistant.ts | 2 + .../lovelace/cards/hui-thermostat-card.ts | 25 ++-- src/translations/en.json | 33 +---- 8 files changed, 199 insertions(+), 92 deletions(-) create mode 100644 src/common/entity/compute_attribute_display.ts diff --git a/src/common/entity/compute_attribute_display.ts b/src/common/entity/compute_attribute_display.ts new file mode 100644 index 0000000000..f969646265 --- /dev/null +++ b/src/common/entity/compute_attribute_display.ts @@ -0,0 +1,52 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { EntityRegistryEntry } from "../../data/entity_registry"; +import { HomeAssistant } from "../../types"; +import { LocalizeFunc } from "../translations/localize"; +import { computeDomain } from "./compute_domain"; + +export const computeAttributeValueDisplay = ( + localize: LocalizeFunc, + stateObj: HassEntity, + entities: HomeAssistant["entities"], + attribute: string, + value?: any +): string => { + const entityId = stateObj.entity_id; + const attributeValue = + value !== undefined ? value : stateObj.attributes[attribute]; + const domain = computeDomain(entityId); + const entity = entities[entityId] as EntityRegistryEntry | undefined; + const translationKey = entity?.translation_key; + + return ( + (translationKey && + localize( + `component.${entity.platform}.entity.${domain}.${translationKey}.state_attributes.${attribute}.state.${attributeValue}` + )) || + localize( + `component.${domain}.state_attributes._.${attribute}.state.${attributeValue}` + ) || + attributeValue + ); +}; + +export const computeAttributeNameDisplay = ( + localize: LocalizeFunc, + stateObj: HassEntity, + entities: HomeAssistant["entities"], + attribute: string +): string => { + const entityId = stateObj.entity_id; + const domain = computeDomain(entityId); + const entity = entities[entityId] as EntityRegistryEntry | undefined; + const translationKey = entity?.translation_key; + + return ( + (translationKey && + localize( + `component.${entity.platform}.entity.${domain}.${translationKey}.state_attributes.${attribute}.name` + )) || + localize(`component.${domain}.state_attributes._.${attribute}.name`) || + attribute + ); +}; diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts index 8b9f170b00..9443352fb5 100644 --- a/src/components/ha-climate-state.ts +++ b/src/components/ha-climate-state.ts @@ -1,5 +1,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; +import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display"; +import { computeStateDisplay } from "../common/entity/compute_state_display"; import { formatNumber } from "../common/number/format_number"; import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate"; import { isUnavailableState } from "../data/entity"; @@ -21,9 +23,12 @@ class HaClimateState extends LitElement { ${this.stateObj.attributes.preset_mode && this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE ? html`- - ${this.hass.localize( - `state_attributes.climate.preset_mode.${this.stateObj.attributes.preset_mode}` - ) || this.stateObj.attributes.preset_mode}` + ${computeAttributeValueDisplay( + this.hass.localize, + this.stateObj, + this.hass.entities, + "preset_mode" + )}` : ""}
${this._computeTarget()}
` @@ -112,13 +117,19 @@ class HaClimateState extends LitElement { return this.hass.localize(`state.default.${this.stateObj.state}`); } - const stateString = this.hass.localize( - `component.climate.state._.${this.stateObj.state}` + const stateString = computeStateDisplay( + this.hass.localize, + this.stateObj, + this.hass.locale, + this.hass.entities ); return this.stateObj.attributes.hvac_action - ? `${this.hass.localize( - `state_attributes.climate.hvac_action.${this.stateObj.attributes.hvac_action}` + ? `${computeAttributeValueDisplay( + this.hass.localize, + this.stateObj, + this.hass.entities, + "hvac_action" )} (${stateString})` : stateString; } diff --git a/src/data/climate.ts b/src/data/climate.ts index 064fb32c98..6ed6fa7523 100644 --- a/src/data/climate.ts +++ b/src/data/climate.ts @@ -2,7 +2,6 @@ import { HassEntityAttributeBase, HassEntityBase, } from "home-assistant-js-websocket"; -import { TranslationDict } from "../types"; export type HvacMode = | "off" @@ -15,12 +14,13 @@ export type HvacMode = export const CLIMATE_PRESET_NONE = "none"; -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 HvacAction = + | "off" + | "heating" + | "cooling" + | "drying" + | "idle" + | "fan"; export type ClimateEntity = HassEntityBase & { attributes: HassEntityAttributeBase & { @@ -40,23 +40,25 @@ export type ClimateEntity = HassEntityBase & { target_humidity_high?: number; min_humidity?: number; max_humidity?: number; - fan_mode?: FanMode; - fan_modes?: FanMode[]; - preset_mode?: PresetMode; - preset_modes?: PresetMode[]; + fan_mode?: string; + fan_modes?: string[]; + preset_mode?: string; + preset_modes?: string[]; swing_mode?: string; swing_modes?: string[]; aux_heat?: "on" | "off"; }; }; -export const CLIMATE_SUPPORT_TARGET_TEMPERATURE = 1; -export const CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE = 2; -export const CLIMATE_SUPPORT_TARGET_HUMIDITY = 4; -export const CLIMATE_SUPPORT_FAN_MODE = 8; -export const CLIMATE_SUPPORT_PRESET_MODE = 16; -export const CLIMATE_SUPPORT_SWING_MODE = 32; -export const CLIMATE_SUPPORT_AUX_HEAT = 64; +export const enum ClimateEntityFeature { + TARGET_TEMPERATURE = 1, + TARGET_TEMPERATURE_RANGE = 2, + TARGET_HUMIDITY = 4, + FAN_MODE = 8, + PRESET_MODE = 16, + SWING_MODE = 32, + AUX_HEAT = 64, +} const hvacModeOrdering: { [key in HvacMode]: number } = { auto: 1, diff --git a/src/data/translation.ts b/src/data/translation.ts index 56764f922a..1d3f5c2d71 100644 --- a/src/data/translation.ts +++ b/src/data/translation.ts @@ -44,6 +44,7 @@ declare global { export type TranslationCategory = | "title" | "state" + | "state_attributes" | "entity" | "config" | "config_panel" diff --git a/src/dialogs/more-info/controls/more-info-climate.ts b/src/dialogs/more-info/controls/more-info-climate.ts index 03b94c3827..559b9bfbef 100644 --- a/src/dialogs/more-info/controls/more-info-climate.ts +++ b/src/dialogs/more-info/controls/more-info-climate.ts @@ -11,6 +11,11 @@ import { property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../../../common/dom/fire_event"; import { stopPropagation } from "../../../common/dom/stop_propagation"; +import { + computeAttributeNameDisplay, + computeAttributeValueDisplay, +} from "../../../common/entity/compute_attribute_display"; +import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { supportsFeature } from "../../../common/entity/supports-feature"; import { computeRTLDirection } from "../../../common/util/compute_rtl"; import "../../../components/ha-climate-control"; @@ -19,13 +24,7 @@ import "../../../components/ha-slider"; import "../../../components/ha-switch"; import { ClimateEntity, - CLIMATE_SUPPORT_AUX_HEAT, - CLIMATE_SUPPORT_FAN_MODE, - CLIMATE_SUPPORT_PRESET_MODE, - CLIMATE_SUPPORT_SWING_MODE, - CLIMATE_SUPPORT_TARGET_HUMIDITY, - CLIMATE_SUPPORT_TARGET_TEMPERATURE, - CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE, + ClimateEntityFeature, compareClimateHvacModes, } from "../../../data/climate"; import { HomeAssistant } from "../../../types"; @@ -47,26 +46,32 @@ class MoreInfoClimate extends LitElement { const supportTargetTemperature = supportsFeature( stateObj, - CLIMATE_SUPPORT_TARGET_TEMPERATURE + ClimateEntityFeature.TARGET_TEMPERATURE ); const supportTargetTemperatureRange = supportsFeature( stateObj, - CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE ); const supportTargetHumidity = supportsFeature( stateObj, - CLIMATE_SUPPORT_TARGET_HUMIDITY + ClimateEntityFeature.TARGET_HUMIDITY + ); + const supportFanMode = supportsFeature( + stateObj, + ClimateEntityFeature.FAN_MODE ); - const supportFanMode = supportsFeature(stateObj, CLIMATE_SUPPORT_FAN_MODE); const supportPresetMode = supportsFeature( stateObj, - CLIMATE_SUPPORT_PRESET_MODE + ClimateEntityFeature.PRESET_MODE ); const supportSwingMode = supportsFeature( stateObj, - CLIMATE_SUPPORT_SWING_MODE + ClimateEntityFeature.SWING_MODE + ); + const supportAuxHeat = supportsFeature( + stateObj, + ClimateEntityFeature.AUX_HEAT ); - const supportAuxHeat = supportsFeature(stateObj, CLIMATE_SUPPORT_AUX_HEAT); const temperatureStepSize = stateObj.attributes.target_temp_step || @@ -94,7 +99,12 @@ class MoreInfoClimate extends LitElement { ${supportTargetTemperature || supportTargetTemperatureRange ? html`
- ${hass.localize("ui.card.climate.target_temperature")} + ${computeAttributeNameDisplay( + hass.localize, + stateObj, + hass.entities, + "temperature" + )}
` : ""} @@ -145,7 +155,14 @@ class MoreInfoClimate extends LitElement { ${supportTargetHumidity ? html`
-
${hass.localize("ui.card.climate.target_humidity")}
+
+ ${computeAttributeNameDisplay( + hass.localize, + stateObj, + hass.entities, + "humidity" + )} +
${stateObj.attributes.humidity} % @@ -182,7 +199,13 @@ class MoreInfoClimate extends LitElement { .map( (mode) => html` - ${hass.localize(`component.climate.state._.${mode}`)} + ${computeStateDisplay( + hass.localize, + stateObj, + hass.locale, + hass.entities, + mode + )} ` )} @@ -194,7 +217,12 @@ class MoreInfoClimate extends LitElement { ? html`
html` - ${hass.localize( - `state_attributes.climate.preset_mode.${mode}` - ) || mode} + ${computeAttributeValueDisplay( + hass.localize, + stateObj, + hass.entities, + "preset_mode", + mode + )} ` )} @@ -218,7 +250,12 @@ class MoreInfoClimate extends LitElement { ? html`
html` - ${hass.localize( - `state_attributes.climate.fan_mode.${mode}` - ) || mode} + ${computeAttributeValueDisplay( + hass.localize, + stateObj, + hass.entities, + "fan_mode", + mode + )} ` )} @@ -242,7 +283,12 @@ class MoreInfoClimate extends LitElement { ? html`
${stateObj.attributes.swing_modes!.map( (mode) => html` - ${mode} + + ${computeAttributeValueDisplay( + hass.localize, + stateObj, + hass.entities, + "swing_mode", + mode + )} + ` )} @@ -263,7 +317,12 @@ class MoreInfoClimate extends LitElement {
- ${hass.localize("ui.card.climate.aux_heat")} + ${computeAttributeNameDisplay( + hass.localize, + stateObj, + hass.entities, + "aux_heat" + )}
${ stateObj.attributes.hvac_action - ? this.hass!.localize( - `state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}` + ? computeAttributeValueDisplay( + this.hass.localize, + stateObj, + this.hass.entities, + "hvac_action" ) - : this.hass!.localize( - `component.climate.state._.${stateObj.state}` + : computeStateDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.entities ) } ${ @@ -225,9 +233,12 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE ? html` - - ${this.hass!.localize( - `state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}` - ) || stateObj.attributes.preset_mode} + ${computeAttributeValueDisplay( + this.hass.localize, + stateObj, + this.hass.entities, + "preset_mode" + )} ` : "" } diff --git a/src/translations/en.json b/src/translations/en.json index b8bebcaf5f..f8415ad180 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -22,31 +22,6 @@ } }, "state_attributes": { - "climate": { - "fan_mode": { - "off": "Off", - "on": "On", - "auto": "Auto" - }, - "preset_mode": { - "none": "None", - "eco": "Eco", - "away": "Away", - "boost": "Boost", - "comfort": "Comfort", - "home": "Home", - "sleep": "Sleep", - "activity": "Activity" - }, - "hvac_action": { - "off": "Off", - "heating": "Heating", - "cooling": "Cooling", - "drying": "Drying", - "idle": "Idle", - "fan": "Fan" - } - }, "humidifier": { "mode": { "normal": "Normal", @@ -140,7 +115,6 @@ "climate": { "currently": "Currently", "on_off": "On / off", - "target_temperature": "Target temperature", "target_temperature_entity": "{name} target temperature", "target_temperature_mode": "{name} target temperature {mode}", "current_temperature": "{name} current temperature", @@ -148,13 +122,8 @@ "cooling": "{name} cooling", "high": "high", "low": "low", - "target_humidity": "Target humidity", "operation": "Operation", - "fan_mode": "Fan mode", - "swing_mode": "Swing mode", - "preset_mode": "Preset", - "away_mode": "Away mode", - "aux_heat": "Aux heat" + "away_mode": "Away mode" }, "counter": { "actions": { From 5eb45209e8840f352681c9ac30d260310bc2cd45 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Tue, 27 Dec 2022 15:20:47 -0500 Subject: [PATCH 20/20] Pin action versions to minor and patch (#14894) --- .github/workflows/cast_deployment.yaml | 8 ++++---- .github/workflows/ci.yaml | 16 ++++++++-------- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/demo_deployment.yaml | 8 ++++---- .github/workflows/design_deployment.yaml | 4 ++-- .github/workflows/design_preview.yaml | 4 ++-- .github/workflows/nightly.yaml | 4 ++-- .github/workflows/release.yaml | 4 ++-- .github/workflows/translations.yaml | 2 +- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/cast_deployment.yaml b/.github/workflows/cast_deployment.yaml index 4634ae0136..8b8b0b98f7 100644 --- a/.github/workflows/cast_deployment.yaml +++ b/.github/workflows/cast_deployment.yaml @@ -22,12 +22,12 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 with: ref: dev - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -60,12 +60,12 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 with: ref: master - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 63f2305ff1..75c023ef90 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,9 +20,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -44,9 +44,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -63,9 +63,9 @@ jobs: needs: [lint, test] steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -82,9 +82,9 @@ jobs: needs: [lint, test] steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a15a50c233..7717444454 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/demo_deployment.yaml b/.github/workflows/demo_deployment.yaml index dc59829095..cc394b646f 100644 --- a/.github/workflows/demo_deployment.yaml +++ b/.github/workflows/demo_deployment.yaml @@ -23,12 +23,12 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 with: ref: dev - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn @@ -61,12 +61,12 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 with: ref: master - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/design_deployment.yaml b/.github/workflows/design_deployment.yaml index 0743c1d800..0574dd8676 100644 --- a/.github/workflows/design_deployment.yaml +++ b/.github/workflows/design_deployment.yaml @@ -17,10 +17,10 @@ jobs: url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/design_preview.yaml b/.github/workflows/design_preview.yaml index e53f0c83a3..084f69ad93 100644 --- a/.github/workflows/design_preview.yaml +++ b/.github/workflows/design_preview.yaml @@ -22,10 +22,10 @@ jobs: if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview') steps: - name: Check out files from GitHub - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index ba0b88f672..bd54ca3026 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -21,7 +21,7 @@ jobs: contents: write steps: - name: Checkout the repository - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v4 @@ -29,7 +29,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e0bddf81d2..662c7adcc6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -24,7 +24,7 @@ jobs: contents: write # Required to upload release assets steps: - name: Checkout the repository - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Verify version uses: home-assistant/actions/helpers/verify-version@master @@ -35,7 +35,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Node ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v3.5.1 with: node-version: ${{ env.NODE_VERSION }} cache: yarn diff --git a/.github/workflows/translations.yaml b/.github/workflows/translations.yaml index 6cc03e0883..4d593874d7 100644 --- a/.github/workflows/translations.yaml +++ b/.github/workflows/translations.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v3 + uses: actions/checkout@v3.2.0 - name: Upload Translations run: |