From ea3fae2ce40180685d3639145d5b150dfa5315f1 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 6 Oct 2021 10:33:15 +0200 Subject: [PATCH 01/17] Make add-on sorting case insensitive (#10061) --- hassio/src/addon-store/hassio-addon-repository.ts | 3 ++- hassio/src/dashboard/hassio-addons.ts | 4 ++-- .../dialogs/repositories/dialog-hassio-repositories.ts | 3 ++- src/dialogs/config-flow/step-flow-pick-handler.ts | 3 ++- src/dialogs/quick-bar/ha-quick-bar.ts | 9 +++------ 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hassio/src/addon-store/hassio-addon-repository.ts b/hassio/src/addon-store/hassio-addon-repository.ts index 513f053d1c..49da9c3c21 100644 --- a/hassio/src/addon-store/hassio-addon-repository.ts +++ b/hassio/src/addon-store/hassio-addon-repository.ts @@ -4,6 +4,7 @@ import { property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { atLeastVersion } from "../../../src/common/config/version"; import { navigate } from "../../../src/common/navigate"; +import { caseInsensitiveStringCompare } from "../../../src/common/string/compare"; import "../../../src/components/ha-card"; import { HassioAddonInfo, @@ -32,7 +33,7 @@ class HassioAddonRepositoryEl extends LitElement { return filterAndSort(addons, filter); } return addons.sort((a, b) => - a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1 + caseInsensitiveStringCompare(a.name, b.name) ); } ); diff --git a/hassio/src/dashboard/hassio-addons.ts b/hassio/src/dashboard/hassio-addons.ts index 0a5d1308e8..358bb3788c 100644 --- a/hassio/src/dashboard/hassio-addons.ts +++ b/hassio/src/dashboard/hassio-addons.ts @@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { atLeastVersion } from "../../../src/common/config/version"; import { navigate } from "../../../src/common/navigate"; -import { stringCompare } from "../../../src/common/string/compare"; +import { caseInsensitiveStringCompare } from "../../../src/common/string/compare"; import "../../../src/components/ha-card"; import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { haStyle } from "../../../src/resources/styles"; @@ -33,7 +33,7 @@ class HassioAddons extends LitElement { ` : this.supervisor.supervisor.addons - .sort((a, b) => stringCompare(a.name, b.name)) + .sort((a, b) => caseInsensitiveStringCompare(a.name, b.name)) .map( (addon) => html` diff --git a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts index 4ab98e50c3..64eb324a20 100644 --- a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts +++ b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts @@ -9,6 +9,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../src/common/dom/fire_event"; +import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare"; import "../../../../src/components/ha-alert"; import "../../../../src/components/ha-circular-progress"; import { createCloseHeading } from "../../../../src/components/ha-dialog"; @@ -57,7 +58,7 @@ class HassioRepositoriesDialog extends LitElement { private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) => repos .filter((repo) => repo.slug !== "core" && repo.slug !== "local") - .sort((a, b) => (a.name < b.name ? -1 : 1)) + .sort((a, b) => caseInsensitiveStringCompare(a.name, b.name)) ); protected render(): TemplateResult { diff --git a/src/dialogs/config-flow/step-flow-pick-handler.ts b/src/dialogs/config-flow/step-flow-pick-handler.ts index 97c43f67aa..e444729a64 100644 --- a/src/dialogs/config-flow/step-flow-pick-handler.ts +++ b/src/dialogs/config-flow/step-flow-pick-handler.ts @@ -7,6 +7,7 @@ import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import "../../common/search/search-input"; +import { caseInsensitiveStringCompare } from "../../common/string/compare"; import { LocalizeFunc } from "../../common/translations/localize"; import "../../components/ha-icon-next"; import { domainToName } from "../../data/integration"; @@ -59,7 +60,7 @@ class StepFlowPickHandler extends LitElement { return fuse.search(filter).map((result) => result.item); } return handlers.sort((a, b) => - a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1 + caseInsensitiveStringCompare(a.name, b.name) ); } ); diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index bde8b0eba6..c0f9caf20b 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -25,7 +25,7 @@ import { computeStateName } from "../../common/entity/compute_state_name"; import { domainIcon } from "../../common/entity/domain_icon"; import { navigate } from "../../common/navigate"; import "../../common/search/search-input"; -import { stringCompare } from "../../common/string/compare"; +import { caseInsensitiveStringCompare } from "../../common/string/compare"; import { fuzzyFilterSort, ScorableTextItem, @@ -399,7 +399,7 @@ export class QuickBar extends LitElement { }; }) .sort((a, b) => - stringCompare(a.primaryText.toLowerCase(), b.primaryText.toLowerCase()) + caseInsensitiveStringCompare(a.primaryText, b.primaryText) ); } @@ -409,10 +409,7 @@ export class QuickBar extends LitElement { ...this._generateServerControlCommands(), ...this._generateNavigationCommands(), ].sort((a, b) => - stringCompare( - a.strings.join(" ").toLowerCase(), - b.strings.join(" ").toLowerCase() - ) + caseInsensitiveStringCompare(a.strings.join(" "), b.strings.join(" ")) ); } From a696d849b2e748e4f4026c8ee3f71a70a9e25db2 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 6 Oct 2021 10:38:44 +0200 Subject: [PATCH 02/17] Add missing translations to statistics fixing (#10159) --- .../statistics/developer-tools-statistics.ts | 6 ++++-- .../statistics/dialog-statistics-fix-units-changed.ts | 2 +- src/translations/en.json | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index f0d9c13504..66fbd5354b 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -81,14 +81,16 @@ class HaPanelDevStatistics extends LitElement { issue.data ) || issue.type ) - : "No issues"}`, + : localize("ui.panel.developer-tools.tabs.statistics.no_issue")}`, }, fix: { title: "", template: (_, data: any) => html`${data.issues ? html` - Fix issue + ${localize( + "ui.panel.developer-tools.tabs.statistics.fix_issue.fix" + )} ` : ""}`, width: "113px", diff --git a/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts b/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts index 671fc81177..5338213dfd 100644 --- a/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts +++ b/src/panels/developer-tools/statistics/dialog-statistics-fix-units-changed.ts @@ -82,7 +82,7 @@ export class DialogStatisticsFixUnitsChanged extends LitElement { ${this.hass.localize( - "ui.panel.developer-tools.tabs.statistics.fix_issue.units_changed.fix" + "ui.panel.developer-tools.tabs.statistics.fix_issue.fix" )} diff --git a/src/translations/en.json b/src/translations/en.json index 75098c53d8..1ee18be54a 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3730,6 +3730,7 @@ "title": "Statistics", "entity": "Entity", "issue": "Issue", + "no_issue": "No issue", "issues": { "units_changed": "The unit of this entity changed from ''{metadata_unit}'' to ''{state_unit}''.", "unsupported_unit_state": "The unit (''{state_unit}'') of this entity doesn't match a unit of device class ''{device_class}''.", @@ -3738,11 +3739,11 @@ "entity_not_recorded": "This entity is excluded from being recorded." }, "fix_issue": { + "fix": "Fix issue", "units_changed": { "title": "The unit of this entity changed", "update": "Update the historic statistic values from ''{metadata_unit}'' to ''{state_unit}''", - "clear": "Delete all old statistic data for this entity", - "fix": "Fix issue" + "clear": "Delete all old statistic data for this entity" } } } From ec21f4c2c69855ec994ce4aa50378019f1e952c3 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 6 Oct 2021 13:56:52 +0200 Subject: [PATCH 03/17] Capitalize relative time strings (#10165) --- hassio/src/components/hassio-card-content.ts | 15 --------------- src/common/string/capitalize-first-letter.ts | 2 ++ src/components/entity/state-info.ts | 2 +- src/components/ha-relative-time.ts | 8 +++++++- .../more-info/controls/more-info-automation.ts | 1 + .../more-info/controls/more-info-script.ts | 1 + src/dialogs/more-info/controls/more-info-sun.ts | 3 --- .../notifications/persistent-notification-item.ts | 1 + .../integration-panels/zwave_js/zwave_js-logs.ts | 3 ++- src/panels/config/tags/ha-config-tags.ts | 2 ++ src/panels/logbook/ha-logbook.ts | 1 + .../lovelace/components/hui-generic-entity-row.ts | 3 +++ .../editor/card-editor/hui-entity-picker-table.ts | 1 + src/util/hass-attributes-util.ts | 3 ++- 14 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 src/common/string/capitalize-first-letter.ts diff --git a/hassio/src/components/hassio-card-content.ts b/hassio/src/components/hassio-card-content.ts index d1be7b741b..d1f804ed60 100644 --- a/hassio/src/components/hassio-card-content.ts +++ b/hassio/src/components/hassio-card-content.ts @@ -1,7 +1,6 @@ import { mdiHelpCircle } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; -import "../../../src/components/ha-relative-time"; import "../../../src/components/ha-svg-icon"; import { HomeAssistant } from "../../../src/types"; @@ -19,8 +18,6 @@ class HassioCardContent extends LitElement { @property() public topbarClass?: string; - @property() public datetime?: string; - @property() public iconTitle?: string; @property() public iconClass?: string; @@ -56,15 +53,6 @@ class HassioCardContent extends LitElement { /* treat as available when undefined */ this.available === false ? " (Not available)" : "" } - ${this.datetime - ? html` - - ` - : undefined} `; @@ -106,9 +94,6 @@ class HassioCardContent extends LitElement { height: 2.4em; line-height: 1.2em; } - ha-relative-time { - display: block; - } .icon_image img { max-height: 40px; max-width: 40px; diff --git a/src/common/string/capitalize-first-letter.ts b/src/common/string/capitalize-first-letter.ts new file mode 100644 index 0000000000..794b5b740e --- /dev/null +++ b/src/common/string/capitalize-first-letter.ts @@ -0,0 +1,2 @@ +export const capitalizeFirstLetter = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1); diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts index a4dbd8b87e..d9e2f2fe6d 100644 --- a/src/components/entity/state-info.ts +++ b/src/components/entity/state-info.ts @@ -38,6 +38,7 @@ class StateInfo extends LitElement { id="last_changed" .hass=${this.hass} .datetime=${this.stateObj.last_changed} + capitalize >
@@ -92,7 +93,6 @@ class StateInfo extends LitElement { state-badge { float: left; } - :host([rtl]) state-badge { float: right; } diff --git a/src/components/ha-relative-time.ts b/src/components/ha-relative-time.ts index 3c9f991f5e..b7c950942d 100644 --- a/src/components/ha-relative-time.ts +++ b/src/components/ha-relative-time.ts @@ -1,6 +1,7 @@ import { PropertyValues, ReactiveElement } from "lit"; import { customElement, property } from "lit/decorators"; import { relativeTime } from "../common/datetime/relative_time"; +import { capitalizeFirstLetter } from "../common/string/capitalize-first-letter"; import type { HomeAssistant } from "../types"; @customElement("ha-relative-time") @@ -9,6 +10,8 @@ class HaRelativeTime extends ReactiveElement { @property({ attribute: false }) public datetime?: string | Date; + @property({ type: Boolean }) public capitalize = false; + private _interval?: number; public disconnectedCallback(): void { @@ -55,7 +58,10 @@ class HaRelativeTime extends ReactiveElement { if (!this.datetime) { this.innerHTML = this.hass.localize("ui.components.relative_time.never"); } else { - this.innerHTML = relativeTime(new Date(this.datetime), this.hass.locale); + const relTime = relativeTime(new Date(this.datetime), this.hass.locale); + this.innerHTML = this.capitalize + ? capitalizeFirstLetter(relTime) + : relTime; } } } diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index 0584b977ed..fab8cfb54b 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -25,6 +25,7 @@ class MoreInfoAutomation extends LitElement {
diff --git a/src/dialogs/more-info/controls/more-info-script.ts b/src/dialogs/more-info/controls/more-info-script.ts index 39ce62214e..2f42f1b6e7 100644 --- a/src/dialogs/more-info/controls/more-info-script.ts +++ b/src/dialogs/more-info/controls/more-info-script.ts @@ -28,6 +28,7 @@ class MoreInfoScript extends LitElement { ` : this.hass.localize("ui.components.relative_time.never")} diff --git a/src/dialogs/more-info/controls/more-info-sun.ts b/src/dialogs/more-info/controls/more-info-sun.ts index 254693e681..728dcfcf72 100644 --- a/src/dialogs/more-info/controls/more-info-sun.ts +++ b/src/dialogs/more-info/controls/more-info-sun.ts @@ -73,9 +73,6 @@ class MoreInfoSun extends LitElement { display: inline-block; white-space: nowrap; } - ha-relative-time::first-letter { - text-transform: lowercase; - } hr { border-color: var(--divider-color); border-bottom: none; diff --git a/src/dialogs/notifications/persistent-notification-item.ts b/src/dialogs/notifications/persistent-notification-item.ts index 93abe761ad..1bd0c408d8 100644 --- a/src/dialogs/notifications/persistent-notification-item.ts +++ b/src/dialogs/notifications/persistent-notification-item.ts @@ -31,6 +31,7 @@ export class HuiPersistentNotificationItem extends LitElement { ${this._computeTooltip(this.hass, this.notification)} diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts index 9edf199887..8df84d6576 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts @@ -16,6 +16,7 @@ import { haStyle } from "../../../../../resources/styles"; import { HomeAssistant, Route } from "../../../../../types"; import { fileDownload } from "../../../../../util/file_download"; import { configTabs } from "./zwave_js-config-router"; +import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter"; @customElement("zwave_js-logs") class ZWaveJSLogs extends SubscribeMixin(LitElement) { @@ -149,7 +150,7 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) { setZWaveJSLogLevel(this.hass!, this.configEntryId, selected); this._textarea!.value += `${this.hass.localize( "ui.panel.config.zwave_js.logs.log_level_changed", - { level: selected.charAt(0).toUpperCase() + selected.slice(1) } + { level: capitalizeFirstLetter(selected) } )}\n`; } diff --git a/src/panels/config/tags/ha-config-tags.ts b/src/panels/config/tags/ha-config-tags.ts index 50b4eeccf3..09d5096d16 100644 --- a/src/panels/config/tags/ha-config-tags.ts +++ b/src/panels/config/tags/ha-config-tags.ts @@ -79,6 +79,7 @@ export class HaConfigTags extends SubscribeMixin(LitElement) { ? html`` : this.hass.localize("ui.panel.config.tag.never_scanned")} ` @@ -96,6 +97,7 @@ export class HaConfigTags extends SubscribeMixin(LitElement) { ? html`` : this.hass.localize("ui.panel.config.tag.never_scanned")} `, diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 7ccae86727..2ac78f9515 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -210,6 +210,7 @@ class HaLogbook extends LitElement { ${item.domain === "automation" && item.context_id! in this.traceContexts diff --git a/src/panels/lovelace/components/hui-generic-entity-row.ts b/src/panels/lovelace/components/hui-generic-entity-row.ts index 9c7ca08df3..9a1efd1c2a 100644 --- a/src/panels/lovelace/components/hui-generic-entity-row.ts +++ b/src/panels/lovelace/components/hui-generic-entity-row.ts @@ -95,6 +95,7 @@ class HuiGenericEntityRow extends LitElement { ` : this.config.secondary_info === "last-updated" @@ -102,6 +103,7 @@ class HuiGenericEntityRow extends LitElement { ` : this.config.secondary_info === "last-triggered" @@ -110,6 +112,7 @@ class HuiGenericEntityRow extends LitElement { ` : this.hass.localize( diff --git a/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts b/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts index eb5ba784c0..3a5791e8e3 100644 --- a/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts +++ b/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts @@ -103,6 +103,7 @@ export class HuiEntityPickerTable extends LitElement { `, }; diff --git a/src/util/hass-attributes-util.ts b/src/util/hass-attributes-util.ts index 0d08b6ed87..5025aa1766 100644 --- a/src/util/hass-attributes-util.ts +++ b/src/util/hass-attributes-util.ts @@ -3,6 +3,7 @@ import { until } from "lit/directives/until"; import checkValidDate from "../common/datetime/check_valid_date"; import { formatDate } from "../common/datetime/format_date"; import { formatDateTimeWithSeconds } from "../common/datetime/format_date_time"; +import { capitalizeFirstLetter } from "../common/string/capitalize-first-letter"; import { isDate } from "../common/string/is_date"; import { isTimestamp } from "../common/string/is_timestamp"; import { HomeAssistant } from "../types"; @@ -159,7 +160,7 @@ export function formatAttributeName(value: string): string { .replace(/\bip\b/g, "IP") .replace(/\bmac\b/g, "MAC") .replace(/\bgps\b/g, "GPS"); - return value.charAt(0).toUpperCase() + value.slice(1); + return capitalizeFirstLetter(value); } export function formatAttributeValue( From b226b20e3d955c4938c2889b835f98a9538dfc3f Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 6 Oct 2021 14:07:18 +0200 Subject: [PATCH 04/17] Prevent `computeDomain()` call if no entity selected (#10166) --- .../editor/config-elements/hui-generic-entity-row-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts b/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts index 412547ad07..59bd7eefe3 100644 --- a/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-generic-entity-row-editor.ts @@ -63,7 +63,7 @@ export class HuiGenericEntityRowEditor return html``; } - const domain = computeDomain(this._config.entity); + const domain = computeDomain(this._entity); return html`
From 365cf1f7ef66aa064e9871f65268033e9f3dcb79 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 6 Oct 2021 14:53:40 +0200 Subject: [PATCH 05/17] Break lines in error card (#10169) --- src/panels/lovelace/cards/hui-error-card.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-error-card.ts b/src/panels/lovelace/cards/hui-error-card.ts index ee6dea049d..b5941c91ab 100644 --- a/src/panels/lovelace/cards/hui-error-card.ts +++ b/src/panels/lovelace/cards/hui-error-card.ts @@ -44,9 +44,8 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { return css` pre { font-family: var(--code-font-family, monospace); - text-overflow: ellipsis; + white-space: break-spaces; user-select: text; - overflow: hidden; } `; } From 8e010618bb6c943073dd6b28eae8c3b651b5269d Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 6 Oct 2021 17:38:32 +0200 Subject: [PATCH 06/17] Show correct units for prices in energy gas settings (#10164) --- .../dialogs/dialog-energy-gas-settings.ts | 36 ++++++++++++++----- src/translations/en.json | 5 ++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts b/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts index 102c6ab5b7..595ec1c942 100644 --- a/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts +++ b/src/panels/config/energy/dialogs/dialog-energy-gas-settings.ts @@ -34,6 +34,8 @@ export class DialogEnergyGasSettings @state() private _costs?: "no-costs" | "number" | "entity" | "statistic"; + @state() private _unit?: string; + @state() private _error?: string; public async showDialog( @@ -55,6 +57,7 @@ export class DialogEnergyGasSettings public closeDialog(): void { this._params = undefined; this._source = undefined; + this._unit = undefined; this._error = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -64,6 +67,14 @@ export class DialogEnergyGasSettings return html``; } + const unit = + this._unit || + (this._params.unit === undefined + ? "m³ or kWh" + : this._params.unit === "energy" + ? "kWh" + : "m³"); + return html` ` @@ -174,7 +186,8 @@ export class DialogEnergyGasSettings ${this._costs === "number" ? html` - ${this.hass.localize( - `ui.panel.config.energy.gas.dialog.cost_number_suffix`, - { currency: this.hass.config.currency } - )} + ${this.hass.config.currency}/${unit} ` : ""} @@ -239,6 +247,18 @@ export class DialogEnergyGasSettings } private _statisticChanged(ev: CustomEvent<{ value: string }>) { + if (ev.detail.value) { + const entity = this.hass.states[ev.detail.value]; + if (entity?.attributes.unit_of_measurement) { + // Wh is normalized to kWh by stats generation + this._unit = + entity.attributes.unit_of_measurement === "Wh" + ? "kWh" + : entity.attributes.unit_of_measurement; + } + } else { + this._unit = undefined; + } this._source = { ...this._source!, stat_energy_from: ev.detail.value, diff --git a/src/translations/en.json b/src/translations/en.json index 1ee18be54a..3fb886faf8 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1045,10 +1045,9 @@ "cost_stat": "Use an entity tracking the total costs", "cost_stat_input": "Total Costs Entity", "cost_entity": "Use an entity with current price", - "cost_entity_input": "Entity with the current price", + "cost_entity_input": "Entity with the current price per {unit}", "cost_number": "Use a static price", - "cost_number_input": "Price per m³", - "cost_number_suffix": "{currency}/m³" + "cost_number_input": "Price per {unit}" } }, "device_consumption": { From fb1deb838ccae18296463227e9a4fe0c95eae024 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 6 Oct 2021 17:41:37 +0200 Subject: [PATCH 07/17] Add `title` property to elements showing entity names (#9264) --- src/components/chart/ha-chart-base.ts | 1 + src/components/entity/ha-state-label-badge.ts | 2 +- src/components/entity/state-info.ts | 6 ++++-- src/dialogs/more-info/ha-more-info-dialog.ts | 13 ++++++++++--- src/panels/lovelace/cards/hui-button-card.ts | 11 +++++------ src/panels/lovelace/cards/hui-entity-card.ts | 6 +++--- src/panels/lovelace/cards/hui-gauge-card.ts | 6 +++--- src/panels/lovelace/cards/hui-glance-card.ts | 10 +++------- src/panels/lovelace/cards/hui-humidifier-card.ts | 2 +- src/panels/lovelace/cards/hui-light-card.ts | 6 ++++-- src/panels/lovelace/cards/hui-thermostat-card.ts | 2 +- .../lovelace/cards/hui-weather-forecast-card.ts | 5 ++--- .../lovelace/components/hui-generic-entity-row.ts | 4 +++- src/panels/lovelace/special-rows/hui-button-row.ts | 7 ++++--- src/panels/lovelace/special-rows/hui-section-row.ts | 6 +++++- src/panels/lovelace/special-rows/hui-text-row.ts | 4 ++-- src/panels/lovelace/special-rows/hui-weblink-row.ts | 2 +- 17 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 1606fc07ba..ca49c02522 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -86,6 +86,7 @@ export default class HaChartBase extends LitElement { class=${classMap({ hidden: this._hiddenDatasets.has(index), })} + .title=${dataset.label} >
`; } diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts index d9e2f2fe6d..cbc7b1c0f9 100644 --- a/src/components/entity/state-info.ts +++ b/src/components/entity/state-info.ts @@ -24,13 +24,15 @@ class StateInfo extends LitElement { return html``; } + const name = computeStateName(this.stateObj); + return html`
-
- ${computeStateName(this.stateObj)} +
+ ${name}
${this.inDialog ? html`
diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index b5c72f87da..e74420c9da 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -94,12 +94,14 @@ export class MoreInfoDialog extends LitElement { } const entityId = this._entityId; const stateObj = this.hass.states[entityId]; - const domain = computeDomain(entityId); if (!stateObj) { return html``; } + const domain = computeDomain(entityId); + const name = computeStateName(stateObj); + return html` -
- ${computeStateName(stateObj)} +
+ ${name}
${this.hass.user!.is_admin ? html` diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index cab1b8b652..6893279f1a 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -143,6 +143,10 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { `; } + const name = this._config.show_name + ? this._config.name || (stateObj ? computeStateName(stateObj) : "") + : ""; + return html` - ${this._config.name || - (stateObj ? computeStateName(stateObj) : "")} - - ` + ? html`${name}` : ""} ${this._config.show_state && stateObj ? html` diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index d5db077ec0..d63d721a05 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -115,12 +115,12 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { ? this._config.attribute in stateObj.attributes : !UNAVAILABLE_STATES.includes(stateObj.state); + const name = this._config.name || computeStateName(stateObj); + return html`
-
- ${this._config.name || computeStateName(stateObj)} -
+
${name}
-
- ${this._config.name || computeStateName(stateObj)} -
+
${name}
`; } diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 40bfc833b8..fd1528c329 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -278,6 +278,8 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
`; } + const name = entityConf.name ?? computeStateName(stateObj); + return html`
${this._config!.show_name - ? html` -
- ${"name" in entityConf - ? entityConf.name - : computeStateName(stateObj)} -
- ` + ? html`
${name}
` : ""} ${this._config!.show_icon ? html` diff --git a/src/panels/lovelace/cards/hui-humidifier-card.ts b/src/panels/lovelace/cards/hui-humidifier-card.ts index 1c418a259b..b1337abf0a 100644 --- a/src/panels/lovelace/cards/hui-humidifier-card.ts +++ b/src/panels/lovelace/cards/hui-humidifier-card.ts @@ -173,7 +173,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
-
${name}
+
${name}
`; diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index 3883815fa6..618f193010 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -96,6 +96,8 @@ export class HuiLightCard extends LitElement implements LovelaceCard { const brightness = Math.round((stateObj.attributes.brightness / 255) * 100) || 0; + const name = this._config.name ?? computeStateName(stateObj); + return html`
-
+
${UNAVAILABLE_STATES.includes(stateObj.state) ? html`
@@ -157,7 +159,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
` : html`
%
`} - ${this._config.name || computeStateName(stateObj)} + ${name}
diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index 64c8dc7f07..d2869a5a4b 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -251,7 +251,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
-
+
${(stateObj.attributes.hvac_modes || []) .concat() diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts index adce427921..7e6acae221 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts @@ -190,6 +190,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { } const weatherStateIcon = getWeatherStateIcon(stateObj.state, this); + const name = this._config.name ?? computeStateName(stateObj); return html` -
- ${this._config.name || computeStateName(stateObj)} -
+
${name}
diff --git a/src/panels/lovelace/components/hui-generic-entity-row.ts b/src/panels/lovelace/components/hui-generic-entity-row.ts index 9a1efd1c2a..b1554bfcdd 100644 --- a/src/panels/lovelace/components/hui-generic-entity-row.ts +++ b/src/panels/lovelace/components/hui-generic-entity-row.ts @@ -54,6 +54,7 @@ class HuiGenericEntityRow extends LitElement { !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(this.config.entity))); const hasSecondary = this.secondaryText || this.config.secondary_info; + const name = this.config.name ?? computeStateName(stateObj); return html` - ${this.config.name || computeStateName(stateObj)} + ${name} ${hasSecondary ? html`
diff --git a/src/panels/lovelace/special-rows/hui-button-row.ts b/src/panels/lovelace/special-rows/hui-button-row.ts index c67887bee2..4292af6600 100644 --- a/src/panels/lovelace/special-rows/hui-button-row.ts +++ b/src/panels/lovelace/special-rows/hui-button-row.ts @@ -50,6 +50,9 @@ export class HuiButtonRow extends LitElement implements LovelaceRow { ? this.hass.states[this._config.entity] : undefined; + const name = + this._config.name ?? (stateObj ? computeStateName(stateObj) : ""); + return html`
-
- ${this._config.name || (stateObj ? computeStateName(stateObj) : "")} -
+
${name}
${this._config.label - ? html`
${this._config.label}
` + ? html` +
+ ${this._config.label} +
+ ` : html``} `; } diff --git a/src/panels/lovelace/special-rows/hui-text-row.ts b/src/panels/lovelace/special-rows/hui-text-row.ts index 6953de6102..b64d8a3842 100644 --- a/src/panels/lovelace/special-rows/hui-text-row.ts +++ b/src/panels/lovelace/special-rows/hui-text-row.ts @@ -22,8 +22,8 @@ class HuiTextRow extends LitElement implements LovelaceRow { return html` -
${this._config.name}
-
${this._config.text}
+
${this._config.name}
+
${this._config.text}
`; } diff --git a/src/panels/lovelace/special-rows/hui-weblink-row.ts b/src/panels/lovelace/special-rows/hui-weblink-row.ts index 3ff017470f..5af61bb49e 100644 --- a/src/panels/lovelace/special-rows/hui-weblink-row.ts +++ b/src/panels/lovelace/special-rows/hui-weblink-row.ts @@ -36,7 +36,7 @@ class HuiWeblinkRow extends LitElement implements LovelaceRow { ?download=${this._config.download} > -
${this._config.name}
+
${this._config.name}
`; } From 9e35c1ab68343de15049783c5d8ac4db7fbf1911 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 00:41:37 +0200 Subject: [PATCH 08/17] Make sure we have no `ha-icon` in supervisor (#10176) --- src/components/data-table/ha-data-table.ts | 27 ++++++++++--------- src/components/ha-chip.ts | 1 - src/components/ha-dialog.ts | 3 ++- .../ha-form/ha-form-multi_select.ts | 9 ++++--- src/components/ha-tab.ts | 2 -- src/components/user/ha-user-picker.ts | 1 - src/layouts/hass-tabs-subpage-data-table.ts | 2 +- src/layouts/hass-tabs-subpage.ts | 4 +-- 8 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index 2209d54f59..6419406adf 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -1,4 +1,5 @@ import { Layout1d, scroll } from "@lit-labs/virtualizer"; +import { mdiArrowDown, mdiArrowUp } from "@mdi/js"; import deepClone from "deep-clone-simple"; import { css, @@ -27,7 +28,7 @@ import { nextRender } from "../../common/util/render-status"; import { haStyleScrollbar } from "../../resources/styles"; import "../ha-checkbox"; import type { HaCheckbox } from "../ha-checkbox"; -import "../ha-icon"; +import "../ha-svg-icon"; import { filterData, sortData } from "./sort-filter"; declare global { @@ -311,11 +312,11 @@ export class HaDataTable extends LitElement { > ${column.sortable ? html` - + ` : ""} ${column.title} @@ -863,14 +864,14 @@ export class HaDataTable extends LitElement { :host([dir="rtl"]) .mdc-data-table__header-cell > * { transition: right 0.2s ease; } - .mdc-data-table__header-cell ha-icon { + .mdc-data-table__header-cell ha-svg-icon { top: -3px; position: absolute; } - .mdc-data-table__header-cell.not-sorted ha-icon { + .mdc-data-table__header-cell.not-sorted ha-svg-icon { left: -20px; } - :host([dir="rtl"]) .mdc-data-table__header-cell.not-sorted ha-icon { + :host([dir="rtl"]) .mdc-data-table__header-cell.not-sorted ha-svg-icon { right: -20px; } .mdc-data-table__header-cell.sortable:not(.not-sorted) span, @@ -886,16 +887,16 @@ export class HaDataTable extends LitElement { left: auto; right: 24px; } - .mdc-data-table__header-cell.sortable:not(.not-sorted) ha-icon, - .mdc-data-table__header-cell.sortable:hover.not-sorted ha-icon { + .mdc-data-table__header-cell.sortable:not(.not-sorted) ha-svg-icon, + .mdc-data-table__header-cell.sortable:hover.not-sorted ha-svg-icon { left: 12px; } :host([dir="rtl"]) .mdc-data-table__header-cell.sortable:not(.not-sorted) - ha-icon, + ha-svg-icon, :host([dir="rtl"]) .mdc-data-table__header-cell.sortable:hover.not-sorted - ha-icon { + ha-svg-icon { left: auto; right: 12px; } diff --git a/src/components/ha-chip.ts b/src/components/ha-chip.ts index 0c8dce7edc..bbb934b981 100644 --- a/src/components/ha-chip.ts +++ b/src/components/ha-chip.ts @@ -9,7 +9,6 @@ import { unsafeCSS, } from "lit"; import { customElement, property } from "lit/decorators"; -import "./ha-icon"; declare global { // for fire event diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index b2963d6c3a..4993329563 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -4,7 +4,8 @@ import { css, CSSResultGroup, html, TemplateResult } from "lit"; import { customElement } from "lit/decorators"; import { computeRTLDirection } from "../common/util/compute_rtl"; import type { HomeAssistant } from "../types"; -import "./ha-icon-button"; +import "@material/mwc-icon-button/mwc-icon-button"; +import "./ha-svg-icon"; export const createCloseHeading = ( hass: HomeAssistant, diff --git a/src/components/ha-form/ha-form-multi_select.ts b/src/components/ha-form/ha-form-multi_select.ts index 8416b5af75..b947e5ac79 100644 --- a/src/components/ha-form/ha-form-multi_select.ts +++ b/src/components/ha-form/ha-form-multi_select.ts @@ -1,3 +1,4 @@ +import { mdiMenuDown } from "@mdi/js"; import "@polymer/paper-checkbox/paper-checkbox"; import "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-icon-item"; @@ -7,7 +8,7 @@ import "@polymer/paper-ripple/paper-ripple"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state, query } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; -import "../ha-icon"; +import "../ha-svg-icon"; import { HaFormElement, HaFormMultiSelectData, @@ -56,11 +57,11 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement { input-aria-haspopup="listbox" autocomplete="off" > - + >
` - : html``} + : ""} ` From 066a0771b37241af79bcdfad72a414441dff4688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 7 Oct 2021 11:02:52 +0200 Subject: [PATCH 09/17] Move functions to common-translation (#10180) --- src/common/translations/localize.ts | 2 +- src/fake_data/provide_hass.ts | 2 +- src/mixins/lit-localize-lite-mixin.ts | 2 +- src/state/connection-mixin.ts | 2 +- src/state/translations-mixin.ts | 6 +- src/util/common-translation.ts | 108 +++++++++++++++++++++++ src/util/hass-translation.ts | 118 -------------------------- 7 files changed, 117 insertions(+), 123 deletions(-) delete mode 100644 src/util/hass-translation.ts diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts index b88aae10f9..a094d48b3e 100644 --- a/src/common/translations/localize.ts +++ b/src/common/translations/localize.ts @@ -4,7 +4,7 @@ import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-rel import { shouldPolyfill as shouldPolyfillDateTime } from "@formatjs/intl-datetimeformat/lib/should-polyfill"; import IntlMessageFormat from "intl-messageformat"; import { Resources } from "../../types"; -import { getLocalLanguage } from "../../util/hass-translation"; +import { getLocalLanguage } from "../../util/common-translation"; export type LocalizeFunc = (key: string, ...args: any[]) => string; interface FormatType { diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts index e90665542c..1be324de95 100644 --- a/src/fake_data/provide_hass.ts +++ b/src/fake_data/provide_hass.ts @@ -8,7 +8,7 @@ import { DEFAULT_PANEL } from "../data/panel"; import { NumberFormat, TimeFormat } from "../data/translation"; import { translationMetadata } from "../resources/translations-metadata"; import { HomeAssistant } from "../types"; -import { getLocalLanguage, getTranslation } from "../util/hass-translation"; +import { getLocalLanguage, getTranslation } from "../util/common-translation"; import { demoConfig } from "./demo_config"; import { demoPanels } from "./demo_panels"; import { demoServices } from "./demo_services"; diff --git a/src/mixins/lit-localize-lite-mixin.ts b/src/mixins/lit-localize-lite-mixin.ts index c9751e1837..864bcd43e7 100644 --- a/src/mixins/lit-localize-lite-mixin.ts +++ b/src/mixins/lit-localize-lite-mixin.ts @@ -2,7 +2,7 @@ import { LitElement, PropertyValues } from "lit"; import { property } from "lit/decorators"; import { computeLocalize, LocalizeFunc } from "../common/translations/localize"; import { Constructor, Resources } from "../types"; -import { getLocalLanguage, getTranslation } from "../util/hass-translation"; +import { getLocalLanguage, getTranslation } from "../util/common-translation"; const empty = () => ""; diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts index 4de7103637..10dba5a617 100644 --- a/src/state/connection-mixin.ts +++ b/src/state/connection-mixin.ts @@ -22,7 +22,7 @@ import { Constructor, ServiceCallResponse } from "../types"; import { fetchWithAuth } from "../util/fetch-with-auth"; import { getState } from "../util/ha-pref-storage"; import hassCallApi from "../util/hass-call-api"; -import { getLocalLanguage } from "../util/hass-translation"; +import { getLocalLanguage } from "../util/common-translation"; import { HassBaseEl } from "./hass-base-mixin"; export const connectionMixin = >( diff --git a/src/state/translations-mixin.ts b/src/state/translations-mixin.ts index c7ec776b06..c26978ddc1 100644 --- a/src/state/translations-mixin.ts +++ b/src/state/translations-mixin.ts @@ -17,7 +17,7 @@ import { getLocalLanguage, getTranslation, getUserLocale, -} from "../util/hass-translation"; +} from "../util/common-translation"; import { HassBaseEl } from "./hass-base-mixin"; declare global { @@ -376,3 +376,7 @@ export default >(superClass: T) => } } }; + +// Load selected translation into memory immediately so it is ready when Polymer +// initializes. +getTranslation(null, getLocalLanguage()); diff --git a/src/util/common-translation.ts b/src/util/common-translation.ts index 7f7e99b3fa..ff7793d256 100644 --- a/src/util/common-translation.ts +++ b/src/util/common-translation.ts @@ -1,6 +1,12 @@ +import { + fetchTranslationPreferences, + FrontendLocaleData, +} from "../data/translation"; import { translationMetadata } from "../resources/translations-metadata"; +import { HomeAssistant } from "../types"; const DEFAULT_BASE_URL = "/static/translations"; +const STORAGE = window.localStorage || {}; // Store loaded translations in memory so translations are available immediately // when DOM is created in Polymer. Even a cache lookup creates noticeable latency. @@ -18,6 +24,108 @@ async function fetchTranslation(fingerprint: string, base_url: string) { return response.json(); } +// Chinese locales need map to Simplified or Traditional Chinese +const LOCALE_LOOKUP = { + "zh-cn": "zh-Hans", + "zh-sg": "zh-Hans", + "zh-my": "zh-Hans", + "zh-tw": "zh-Hant", + "zh-hk": "zh-Hant", + "zh-mo": "zh-Hant", + zh: "zh-Hant", // all other Chinese locales map to Traditional Chinese +}; + +/** + * Search for a matching translation from most specific to general + */ +export function findAvailableLanguage(language: string) { + // In most case, the language has the same format with our translation meta data + if (language in translationMetadata.translations) { + return language; + } + + // Perform case-insenstive comparison since browser isn't required to + // report languages with specific cases. + const langLower = language.toLowerCase(); + + if (langLower in LOCALE_LOOKUP) { + return LOCALE_LOOKUP[langLower]; + } + + const translation = Object.keys(translationMetadata.translations).find( + (lang) => lang.toLowerCase() === langLower + ); + if (translation) { + return translation; + } + + if (language.includes("-")) { + return findAvailableLanguage(language.split("-")[0]); + } + + return undefined; +} + +/** + * Get user selected locale data from backend + */ +export async function getUserLocale( + hass: HomeAssistant +): Promise> { + const result = await fetchTranslationPreferences(hass); + const language = result?.language; + const number_format = result?.number_format; + const time_format = result?.time_format; + if (language) { + const availableLanguage = findAvailableLanguage(language); + if (availableLanguage) { + return { + language: availableLanguage, + number_format, + time_format, + }; + } + } + return { + number_format, + time_format, + }; +} + +/** + * Get browser specific language + */ +export function getLocalLanguage() { + let language = null; + if (STORAGE.selectedLanguage) { + try { + const stored = JSON.parse(STORAGE.selectedLanguage); + if (stored) { + language = findAvailableLanguage(stored); + if (language) { + return language; + } + } + } catch (err: any) { + // Ignore parsing error. + } + } + if (navigator.languages) { + for (const locale of navigator.languages) { + language = findAvailableLanguage(locale); + if (language) { + return language; + } + } + } + language = findAvailableLanguage(navigator.language); + if (language) { + return language; + } + // Final fallback + return "en"; +} + export async function getTranslation( fragment: string | null, language: string, diff --git a/src/util/hass-translation.ts b/src/util/hass-translation.ts deleted file mode 100644 index 156c79fff7..0000000000 --- a/src/util/hass-translation.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { - fetchTranslationPreferences, - FrontendLocaleData, -} from "../data/translation"; -import { translationMetadata } from "../resources/translations-metadata"; -import { HomeAssistant } from "../types"; -import { getTranslation as commonGetTranslation } from "./common-translation"; - -const STORAGE = window.localStorage || {}; - -// Chinese locales need map to Simplified or Traditional Chinese -const LOCALE_LOOKUP = { - "zh-cn": "zh-Hans", - "zh-sg": "zh-Hans", - "zh-my": "zh-Hans", - "zh-tw": "zh-Hant", - "zh-hk": "zh-Hant", - "zh-mo": "zh-Hant", - zh: "zh-Hant", // all other Chinese locales map to Traditional Chinese -}; - -/** - * Search for a matching translation from most specific to general - */ -export function findAvailableLanguage(language: string) { - // In most case, the language has the same format with our translation meta data - if (language in translationMetadata.translations) { - return language; - } - - // Perform case-insenstive comparison since browser isn't required to - // report languages with specific cases. - const langLower = language.toLowerCase(); - - if (langLower in LOCALE_LOOKUP) { - return LOCALE_LOOKUP[langLower]; - } - - const translation = Object.keys(translationMetadata.translations).find( - (lang) => lang.toLowerCase() === langLower - ); - if (translation) { - return translation; - } - - if (language.includes("-")) { - return findAvailableLanguage(language.split("-")[0]); - } - - return undefined; -} - -/** - * Get user selected locale data from backend - */ -export async function getUserLocale( - hass: HomeAssistant -): Promise> { - const result = await fetchTranslationPreferences(hass); - const language = result?.language; - const number_format = result?.number_format; - const time_format = result?.time_format; - if (language) { - const availableLanguage = findAvailableLanguage(language); - if (availableLanguage) { - return { - language: availableLanguage, - number_format, - time_format, - }; - } - } - return { - number_format, - time_format, - }; -} - -/** - * Get browser specific language - */ -export function getLocalLanguage() { - let language = null; - if (STORAGE.selectedLanguage) { - try { - const stored = JSON.parse(STORAGE.selectedLanguage); - if (stored) { - language = findAvailableLanguage(stored); - if (language) { - return language; - } - } - } catch (err: any) { - // Ignore parsing error. - } - } - if (navigator.languages) { - for (const locale of navigator.languages) { - language = findAvailableLanguage(locale); - if (language) { - return language; - } - } - } - language = findAvailableLanguage(navigator.language); - if (language) { - return language; - } - // Final fallback - return "en"; -} - -export const getTranslation = (fragment: string | null, language: string) => - commonGetTranslation(fragment, language); - -// Load selected translation into memory immediately so it is ready when Polymer -// initializes. -commonGetTranslation(null, getLocalLanguage()); From d5ca7e1719ddc9d4e6c0965d6df0941b5321d9dd Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 12:25:15 +0200 Subject: [PATCH 10/17] Remove ha-icon from ha-label-badge (#10182) --- src/components/entity/ha-state-label-badge.ts | 27 ++++++++++---- src/components/ha-label-badge.ts | 35 +++++-------------- src/panels/lovelace/badges/hui-error-badge.ts | 10 +++--- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index 50bb6a137e..b7001ab861 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -1,3 +1,4 @@ +import { mdiAlert } from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; import { css, @@ -14,11 +15,12 @@ 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 { stateIcon } from "../../common/entity/state_icon"; -import { timerTimeRemaining } from "../../data/timer"; import { formatNumber } from "../../common/number/format_number"; import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; +import { timerTimeRemaining } from "../../data/timer"; import { HomeAssistant } from "../../types"; import "../ha-label-badge"; +import "../ha-icon"; @customElement("ha-state-label-badge") export class HaStateLabelBadge extends LitElement { @@ -58,16 +60,20 @@ export class HaStateLabelBadge extends LitElement { + > + + `; } const domain = computeStateDomain(entityState); + const value = this._computeValue(domain, entityState); + const icon = this.icon ? this.icon : this._computeIcon(domain, entityState); + return html` + > + ${icon ? html`` : ""} + ${value && (this.icon || !this.image) + ? html` 4 ? "big" : ""} + >${value}` + : ""} + `; } @@ -208,7 +219,9 @@ export class HaStateLabelBadge extends LitElement { :host { cursor: pointer; } - + .big { + font-size: 70%; + } ha-label-badge { --ha-label-badge-color: var(--label-badge-red, #df4c1e); } diff --git a/src/components/ha-label-badge.ts b/src/components/ha-label-badge.ts index 9312308687..7c1d633f43 100644 --- a/src/components/ha-label-badge.ts +++ b/src/components/ha-label-badge.ts @@ -8,13 +8,9 @@ import { } from "lit"; import { property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import "./ha-icon"; +import "./ha-svg-icon"; class HaLabelBadge extends LitElement { - @property() public value?: string; - - @property() public icon?: string; - @property() public label?: string; @property() public description?: string; @@ -25,20 +21,8 @@ class HaLabelBadge extends LitElement { return html`
-
4), - })} - > - - ${this.icon && !this.value && !this.image - ? html`` - : ""} - ${this.value && !this.image - ? html`${this.value}` - : ""} - +
+
${this.label ? html` @@ -54,7 +38,7 @@ class HaLabelBadge extends LitElement { : ""}
${this.description - ? html`
${this.description}
` + ? html`
${this.description}
` : ""}
`; @@ -87,14 +71,15 @@ class HaLabelBadge extends LitElement { background-size: cover; transition: border 0.3s ease-in-out; } + .label-badge .label.big span { + font-size: 90%; + padding: 10% 12% 7% 12%; /* push smaller text a bit down to center vertically */ + } .label-badge .value { font-size: 90%; overflow: hidden; text-overflow: ellipsis; } - .label-badge .value.big { - font-size: 70%; - } .label-badge .label { position: absolute; bottom: -1em; @@ -119,10 +104,6 @@ class HaLabelBadge extends LitElement { transition: background-color 0.3s ease-in-out; text-transform: var(--ha-label-badge-label-text-transform, uppercase); } - .label-badge .label.big span { - font-size: 90%; - padding: 10% 12% 7% 12%; /* push smaller text a bit down to center vertically */ - } .badge-container .title { margin-top: 1em; font-size: var(--ha-label-badge-title-font-size, 0.9em); diff --git a/src/panels/lovelace/badges/hui-error-badge.ts b/src/panels/lovelace/badges/hui-error-badge.ts index f15a5b0490..375cac75ab 100644 --- a/src/panels/lovelace/badges/hui-error-badge.ts +++ b/src/panels/lovelace/badges/hui-error-badge.ts @@ -1,6 +1,8 @@ +import { mdiAlert } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, state } from "lit/decorators"; import "../../../components/ha-label-badge"; +import "../../../components/ha-svg-icon"; import { HomeAssistant } from "../../../types"; import { LovelaceBadge } from "../types"; import { ErrorBadgeConfig } from "./types"; @@ -32,11 +34,9 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge { } return html` - + + + `; } From 8022bd28684ad50f62690626746783e9d2495c2d Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 12:31:47 +0200 Subject: [PATCH 11/17] Guard icon db check on hassio (#10181) --- .eslintrc.json | 1 + build-scripts/bundle.js | 4 ++++ src/components/ha-icon.ts | 5 ++++- src/types.ts | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 407ef663dd..8527576da2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -29,6 +29,7 @@ "__BUILD__": false, "__VERSION__": false, "__STATIC_PATH__": false, + "__SUPERVISOR__": false, "Polymer": true }, "env": { diff --git a/build-scripts/bundle.js b/build-scripts/bundle.js index 5402abc16f..277fa54bee 100644 --- a/build-scripts/bundle.js +++ b/build-scripts/bundle.js @@ -35,6 +35,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"), __VERSION__: JSON.stringify(env.version()), __DEMO__: false, + __SUPERVISOR__: false, __BACKWARDS_COMPAT__: false, __STATIC_PATH__: "/static/", "process.env.NODE_ENV": JSON.stringify( @@ -194,6 +195,9 @@ module.exports.config = { publicPath: publicPath(latestBuild, paths.hassio_publicPath), isProdBuild, latestBuild, + defineOverlay: { + __SUPERVISOR__: true, + }, }; }, diff --git a/src/components/ha-icon.ts b/src/components/ha-icon.ts index 59bb2488c8..2c829299b4 100644 --- a/src/components/ha-icon.ts +++ b/src/components/ha-icon.ts @@ -361,7 +361,10 @@ const mdiDeprecatedIcons: DeprecatedIcon = { const chunks: Chunks = {}; -checkCacheVersion(); +// Supervisor doesn't use icons, and should not update/downgrade the icon DB. +if (!__SUPERVISOR__) { + checkCacheVersion(); +} const debouncedWriteCache = debounce(() => writeCache(chunks), 2000); diff --git a/src/types.ts b/src/types.ts index 5d25b7bc8e..539dcb610d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,7 @@ declare global { var __VERSION__: string; var __STATIC_PATH__: string; var __BACKWARDS_COMPAT__: boolean; + var __SUPERVISOR__: boolean; /* eslint-enable no-var, no-redeclare */ interface Window { From 910cd98a3816cf25388f8d5978cecc2975a94309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 7 Oct 2021 12:53:22 +0200 Subject: [PATCH 12/17] Fix missing add-on rating (#10184) --- hassio/src/addon-view/info/hassio-addon-info.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 96aa19a02f..d1f6da3d08 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -297,10 +297,11 @@ class HassioAddonInfo extends LitElement { })} @click=${this._showMoreInfo} id="rating" - .value=${this.addon.rating} label="rating" description="" - > + > + ${this.addon.rating} + ${this.addon.host_network ? html` Date: Thu, 7 Oct 2021 15:26:41 +0200 Subject: [PATCH 13/17] Fix error in `reduceSumStatisticsByDay` (#10170) --- src/data/history.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/history.ts b/src/data/history.ts index 243e457960..a163ea4708 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -490,7 +490,7 @@ export const reduceSumStatisticsByDay = ( // add init value if the first value isn't end of previous period result.push({ ...values[0]!, - start: startOfMonth(addDays(new Date(values[0].start), -1)).toISOString(), + start: startOfDay(addDays(new Date(values[0].start), -1)).toISOString(), }); } let lastValue: StatisticValue; @@ -546,7 +546,7 @@ export const reduceSumStatisticsByMonth = ( prevMonth = month; } if (prevMonth !== month) { - // Last value of the day + // Last value of the month result.push({ ...lastValue!, start: startOfMonth(new Date(lastValue!.start)).toISOString(), From 2dc7c1afed1324d1cf54903c347f1b399ee2b89b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 21:05:45 +0200 Subject: [PATCH 14/17] Fix `unsupported_unit_metadata` text in stats dev tools (#10183) Co-authored-by: Philip Allgaier --- .../dialog-statistics-fix-unsupported-unit-meta.ts | 8 ++++---- src/translations/en.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts b/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts index fd7e7ae61f..c9f277f114 100644 --- a/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts +++ b/src/panels/developer-tools/statistics/dialog-statistics-fix-unsupported-unit-meta.ts @@ -37,10 +37,10 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { heading="Unsupported unit in recorded statistics" >

- The unit of the statistics in your database for this entity is not a - supported unit for the device class of the entity, - ${this._params.issue.data.device_class}. It should be - ${this._params.issue.data.supported_unit}. + The unit ${this._params.issue.data.metadata_unit} of the statistics in + your database for this entity is not a supported unit for the device + class of the entity, ${this._params.issue.data.device_class}. It + should be ${this._params.issue.data.supported_unit}.

Do you want to update the unit of the history statistics to diff --git a/src/translations/en.json b/src/translations/en.json index 3fb886faf8..25c2034d3e 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3733,7 +3733,7 @@ "issues": { "units_changed": "The unit of this entity changed from ''{metadata_unit}'' to ''{state_unit}''.", "unsupported_unit_state": "The unit (''{state_unit}'') of this entity doesn't match a unit of device class ''{device_class}''.", - "unsupported_unit_metadata": "The unit (''{state_unit}'') of the recorded statistics doesn't match a unit of device class ''{device_class}''.", + "unsupported_unit_metadata": "The unit (''{metadata_unit}'') of the recorded statistics doesn't match the supported unit ''{supported_unit}'' of device class ''{device_class}''.", "unsupported_state_class": "The state class ''{state_class}'' of this entity is not supported.", "entity_not_recorded": "This entity is excluded from being recorded." }, From db55be6d330aa358dd6cfd2ede485808d3f37494 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 21:06:18 +0200 Subject: [PATCH 15/17] Add start - end time to energy graph tooltip (#10185) --- .../cards/energy/hui-energy-gas-graph-card.ts | 19 ++++++++++++++++++- .../energy/hui-energy-solar-graph-card.ts | 19 ++++++++++++++++++- .../energy/hui-energy-usage-graph-card.ts | 19 ++++++++++++++++++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts index 582107472e..4175f55bee 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts @@ -10,7 +10,13 @@ import { ChartOptions, ScatterDataPoint, } from "chart.js"; -import { differenceInDays, endOfToday, isToday, startOfToday } from "date-fns"; +import { + addHours, + differenceInDays, + endOfToday, + isToday, + startOfToday, +} from "date-fns"; import { HomeAssistant } from "../../../../types"; import { LovelaceCard } from "../../types"; import { EnergyGasGraphCardConfig } from "../types"; @@ -39,6 +45,7 @@ import { reduceSumStatisticsByMonth, reduceSumStatisticsByDay, } from "../../../../data/history"; +import { formatTime } from "../../../../common/datetime/format_time"; @customElement("hui-energy-gas-graph-card") export class HuiEnergyGasGraphCard @@ -180,6 +187,16 @@ export class HuiEnergyGasGraphCard tooltip: { mode: "nearest", callbacks: { + title: (datasets) => { + if (dayDifference > 0) { + return datasets[0].label; + } + const date = new Date(datasets[0].parsed.x); + return `${formatTime(date, locale)} - ${formatTime( + addHours(date, 1), + locale + )}`; + }, label: (context) => `${context.dataset.label}: ${formatNumber( context.parsed.y, diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts index 54b272cfd4..fb595bf124 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts @@ -10,7 +10,13 @@ import { ChartOptions, ScatterDataPoint, } from "chart.js"; -import { differenceInDays, endOfToday, isToday, startOfToday } from "date-fns"; +import { + addHours, + differenceInDays, + endOfToday, + isToday, + startOfToday, +} from "date-fns"; import { HomeAssistant } from "../../../../types"; import { LovelaceCard } from "../../types"; import { EnergySolarGraphCardConfig } from "../types"; @@ -40,6 +46,7 @@ import { reduceSumStatisticsByMonth, reduceSumStatisticsByDay, } from "../../../../data/history"; +import { formatTime } from "../../../../common/datetime/format_time"; @customElement("hui-energy-solar-graph-card") export class HuiEnergySolarGraphCard @@ -173,6 +180,16 @@ export class HuiEnergySolarGraphCard tooltip: { mode: "nearest", callbacks: { + title: (datasets) => { + if (dayDifference > 0) { + return datasets[0].label; + } + const date = new Date(datasets[0].parsed.x); + return `${formatTime(date, locale)} - ${formatTime( + addHours(date, 1), + locale + )}`; + }, label: (context) => `${context.dataset.label}: ${formatNumber( context.parsed.y, diff --git a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index db2b8a0db6..54bb09d6ff 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -1,5 +1,11 @@ import { ChartData, ChartDataset, ChartOptions } from "chart.js"; -import { startOfToday, endOfToday, isToday, differenceInDays } from "date-fns"; +import { + startOfToday, + endOfToday, + isToday, + differenceInDays, + addHours, +} from "date-fns"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -13,6 +19,7 @@ import { } from "../../../../common/color/convert-color"; import { hexBlend } from "../../../../common/color/hex"; import { labDarken } from "../../../../common/color/lab"; +import { formatTime } from "../../../../common/datetime/format_time"; import { computeStateName } from "../../../../common/entity/compute_state_name"; import { formatNumber, @@ -168,6 +175,16 @@ export class HuiEnergyUsageGraphCard position: "nearest", filter: (val) => val.formattedValue !== "0", callbacks: { + title: (datasets) => { + if (dayDifference > 0) { + return datasets[0].label; + } + const date = new Date(datasets[0].parsed.x); + return `${formatTime(date, locale)} - ${formatTime( + addHours(date, 1), + locale + )}`; + }, label: (context) => `${context.dataset.label}: ${formatNumber( Math.abs(context.parsed.y), From 919ce2afb139f747c77201e834c96f9dc0b1444b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 21:06:59 +0200 Subject: [PATCH 16/17] Fix position of home circle in energy distribution on safari (#10186) --- .../lovelace/cards/energy/hui-energy-distribution-card.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts index 33a60ee16c..3a71b8d80b 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -763,6 +763,8 @@ class HuiEnergyDistrubutionCard stroke-width: 4px; width: 100%; height: 100%; + top: 0; + left: 0; } .gas path, .gas circle { From fa52442c1ccbeca5776248731441ba55c4eeb248 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 7 Oct 2021 21:07:37 +0200 Subject: [PATCH 17/17] Bumped version to 20211007.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9aefef5fe2..589990ec84 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20211006.0", + version="20211007.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/frontend", author="The Home Assistant Authors",