diff --git a/build-scripts/bundle.js b/build-scripts/bundle.js index 3e83b592fb..655bbb278b 100644 --- a/build-scripts/bundle.js +++ b/build-scripts/bundle.js @@ -67,7 +67,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({ "@babel/preset-env", { useBuiltIns: "entry", - corejs: "3.15", + corejs: { version: "3.27", proposals: true }, bugfixes: true, }, ], diff --git a/demo/src/ha-demo.ts b/demo/src/ha-demo.ts index 070fea8b44..e911afce1f 100644 --- a/demo/src/ha-demo.ts +++ b/demo/src/ha-demo.ts @@ -1,4 +1,5 @@ // Compat needs to be first import +import "../../src/resources/compatibility"; import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; import { navigate } from "../../src/common/navigate"; import { @@ -6,7 +7,6 @@ import { provideHass, } from "../../src/fake_data/provide_hass"; import { HomeAssistantAppEl } from "../../src/layouts/home-assistant"; -import "../../src/resources/compatibility"; import { HomeAssistant } from "../../src/types"; import { selectedDemoConfig } from "./configs/demo-configs"; import { mockAuth } from "./stubs/auth"; diff --git a/package.json b/package.json index 3fe376b09a..fcfd48f9d4 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "author": "Paulus Schoutsen (http://paulusschoutsen.nl)", "license": "Apache-2.0", "dependencies": { - "@braintree/sanitize-url": "^6.0.0", + "@braintree/sanitize-url": "^6.0.2", "@codemirror/autocomplete": "^6.4.0", "@codemirror/commands": "^6.2.0", "@codemirror/language": "^6.4.0", @@ -32,10 +32,10 @@ "@codemirror/search": "^6.2.3", "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.7.1", - "@formatjs/intl-datetimeformat": "^4.2.5", + "@formatjs/intl-datetimeformat": "^6.4.3", "@formatjs/intl-getcanonicallocales": "^2.0.5", "@formatjs/intl-locale": "^3.0.11", - "@formatjs/intl-numberformat": "^7.2.5", + "@formatjs/intl-numberformat": "^8.3.3", "@formatjs/intl-pluralrules": "^5.1.8", "@formatjs/intl-relativetimeformat": "^11.1.8", "@fullcalendar/common": "^5.11.4", @@ -99,7 +99,7 @@ "app-datepicker": "^5.1.0", "chart.js": "^3.3.2", "comlink": "^4.3.1", - "core-js": "^3.15.2", + "core-js": "^3.27.2", "cropperjs": "^1.5.13", "date-fns": "^2.29.3", "date-fns-tz": "^1.3.7", @@ -121,7 +121,7 @@ "node-vibrant": "3.2.1-alpha.1", "proxy-polyfill": "^0.3.2", "punycode": "^2.3.0", - "qr-scanner": "^1.3.0", + "qr-scanner": "^1.4.2", "qrcode": "^1.5.1", "regenerator-runtime": "^0.13.11", "resize-observer-polyfill": "^1.5.1", @@ -228,12 +228,12 @@ "rollup-plugin-visualizer": "^5.9.0", "serve": "^11.3.2", "sinon": "^15.0.1", - "source-map-url": "^0.4.0", + "source-map-url": "^0.4.1", "systemjs": "^6.13.0", "tar": "^6.1.11", "terser-webpack-plugin": "^5.2.4", "ts-lit-plugin": "^1.2.1", - "typescript": "^4.9.4", + "typescript": "^4.9.5", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "webpack": "^5.55.1", @@ -245,8 +245,7 @@ }, "_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch", "resolutions": { - "@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch", - "@webcomponents/webcomponentsjs": "^2.2.10" + "@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch" }, "main": "src/home-assistant.js", "prettier": { diff --git a/pyproject.toml b/pyproject.toml index da9f762c40..9440b6a7d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20230201.0" +version = "20230202.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/common/datetime/first_weekday.ts b/src/common/datetime/first_weekday.ts index 3c34eb7c14..6316af6681 100644 --- a/src/common/datetime/first_weekday.ts +++ b/src/common/datetime/first_weekday.ts @@ -1,6 +1,12 @@ import { getWeekStartByLocale } from "weekstart"; import { FrontendLocaleData, FirstWeekday } from "../../data/translation"; +import { polyfillsLoaded } from "../translations/localize"; + +if (__BUILD__ === "latest" && polyfillsLoaded) { + await polyfillsLoaded; +} + export const weekdays = [ "sunday", "monday", diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts index 33d871e023..c79f50b4bf 100644 --- a/src/common/translations/localize.ts +++ b/src/common/translations/localize.ts @@ -65,19 +65,21 @@ export interface FormatsType { const loadedPolyfillLocale = new Set(); +const locale = getLocalLanguage(); + const polyfills: Promise[] = []; if (__BUILD__ === "latest") { if (shouldPolyfillLocale()) { - polyfills.push(import("@formatjs/intl-locale/polyfill")); + await import("@formatjs/intl-locale/polyfill"); } - if (shouldPolyfillPluralRules()) { + if (shouldPolyfillPluralRules(locale)) { polyfills.push(import("@formatjs/intl-pluralrules/polyfill")); polyfills.push(import("@formatjs/intl-pluralrules/locale-data/en")); } - if (shouldPolyfillRelativeTime()) { + if (shouldPolyfillRelativeTime(locale)) { polyfills.push(import("@formatjs/intl-relativetimeformat/polyfill")); } - if (shouldPolyfillDateTime()) { + if (shouldPolyfillDateTime(locale)) { polyfills.push(import("@formatjs/intl-datetimeformat/polyfill")); polyfills.push(import("@formatjs/intl-datetimeformat/add-all-tz")); } @@ -88,7 +90,7 @@ export const polyfillsLoaded = ? undefined : Promise.all(polyfills).then(() => // Load the default language - loadPolyfillLocales(getLocalLanguage()) + loadPolyfillLocales(locale) ); /** @@ -214,7 +216,7 @@ export const loadPolyfillLocales = async (language: string) => { // @ts-ignore Intl.DateTimeFormat.__addLocaleData(await result.json()); } - } catch (_e) { + } catch (e) { // Ignore } }; diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index 3f8553a21e..12b1fa9cd4 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -1,22 +1,33 @@ -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; import { customElement, property } from "lit/decorators"; import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../common/dom/fire_event"; import { HomeAssistant } from "../../types"; import "../ha-alert"; import "../ha-selector/ha-selector"; -import "./ha-form-boolean"; -import "./ha-form-constant"; -import "./ha-form-float"; -import "./ha-form-grid"; -import "./ha-form-expandable"; -import "./ha-form-integer"; -import "./ha-form-multi_select"; -import "./ha-form-positive_time_period_dict"; -import "./ha-form-select"; -import "./ha-form-string"; import { HaFormDataContainer, HaFormElement, HaFormSchema } from "./types"; +const LOAD_ELEMENTS = { + boolean: () => import("./ha-form-boolean"), + constant: () => import("./ha-form-constant"), + float: () => import("./ha-form-float"), + grid: () => import("./ha-form-grid"), + expandable: () => import("./ha-form-expandable"), + integer: () => import("./ha-form-integer"), + multi_select: () => import("./ha-form-multi_select"), + positive_time_period_dict: () => + import("./ha-form-positive_time_period_dict"), + select: () => import("./ha-form-select"), + string: () => import("./ha-form-string"), +}; + const getValue = (obj, item) => obj ? (!item.name ? obj : obj[item.name]) : null; @@ -58,6 +69,17 @@ export class HaForm extends LitElement implements HaFormElement { } } + protected willUpdate(changedProps: PropertyValues) { + if (changedProps.has("schema") && this.schema) { + this.schema.forEach((item) => { + if ("selector" in item) { + return; + } + LOAD_ELEMENTS[item.type]?.(); + }); + } + } + protected render(): TemplateResult { return html`
diff --git a/src/components/ha-selector/ha-selector-icon.ts b/src/components/ha-selector/ha-selector-icon.ts index e10969fbcb..1a5d10cf72 100644 --- a/src/components/ha-selector/ha-selector-icon.ts +++ b/src/components/ha-selector/ha-selector-icon.ts @@ -1,6 +1,8 @@ import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeDomain } from "../../common/entity/compute_domain"; +import { domainIcon } from "../../common/entity/domain_icon"; import { IconSelector } from "../../data/selector"; import { HomeAssistant } from "../../types"; import "../ha-icon-picker"; @@ -21,7 +23,22 @@ export class HaIconSelector extends LitElement { @property({ type: Boolean }) public required = true; + @property() public context?: { + icon_entity?: string; + }; + protected render() { + const iconEntity = this.context?.icon_entity; + + const stateObj = iconEntity ? this.hass.states[iconEntity] : undefined; + + const placeholder = + this.selector.icon?.placeholder || stateObj?.attributes.icon; + const fallbackPath = + !placeholder && stateObj + ? domainIcon(computeDomain(iconEntity!), stateObj) + : undefined; + return html` `; diff --git a/src/html/authorize.html.template b/src/html/authorize.html.template index 988170769f..66e0b1db17 100644 --- a/src/html/authorize.html.template +++ b/src/html/authorize.html.template @@ -43,6 +43,9 @@ <%= renderTemplate('_preload_roboto') %> diff --git a/src/html/index.html.template b/src/html/index.html.template index 3568135740..56bc8860a0 100644 --- a/src/html/index.html.template +++ b/src/html/index.html.template @@ -90,15 +90,15 @@ <%= renderTemplate('_preload_roboto') %> diff --git a/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts index da8436cf5b..5256fe846b 100644 --- a/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts @@ -1,11 +1,7 @@ -import type { HassEntity } from "home-assistant-js-websocket"; import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; import { assert, assign, boolean, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import { domainIcon } from "../../../../common/entity/domain_icon"; import { entityId } from "../../../../common/structs/is-entity-id"; import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; @@ -32,6 +28,52 @@ const cardConfigStruct = assign( }) ); +const SCHEMA = [ + { name: "entity", selector: { entity: {} } }, + { + name: "", + type: "grid", + schema: [ + { name: "name", selector: { text: {} } }, + { + name: "icon", + selector: { + icon: {}, + }, + context: { + icon_entity: "entity", + }, + }, + ], + }, + { + name: "", + type: "grid", + column_min_width: "100px", + schema: [ + { name: "show_name", selector: { boolean: {} } }, + { name: "show_state", selector: { boolean: {} } }, + { name: "show_icon", selector: { boolean: {} } }, + ], + }, + { + name: "", + type: "grid", + schema: [ + { name: "icon_height", selector: { text: { suffix: "px" } } }, + { name: "theme", selector: { theme: {} } }, + ], + }, + { + name: "tap_action", + selector: { "ui-action": {} }, + }, + { + name: "hold_action", + selector: { "ui-action": {} }, + }, +] as const; + @customElement("hui-button-card-editor") export class HuiButtonCardEditor extends LitElement @@ -46,76 +88,11 @@ export class HuiButtonCardEditor this._config = config; } - private _schema = memoizeOne( - (entity?: string, icon?: string, entityState?: HassEntity) => - [ - { name: "entity", selector: { entity: {} } }, - { - name: "", - type: "grid", - schema: [ - { name: "name", selector: { text: {} } }, - { - name: "icon", - selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && - !entityState?.attributes.icon && - entityState && - entity - ? domainIcon(computeDomain(entity), entityState) - : undefined, - }, - }, - }, - ], - }, - { - name: "", - type: "grid", - column_min_width: "100px", - schema: [ - { name: "show_name", selector: { boolean: {} } }, - { name: "show_state", selector: { boolean: {} } }, - { name: "show_icon", selector: { boolean: {} } }, - ], - }, - { - name: "", - type: "grid", - schema: [ - { name: "icon_height", selector: { text: { suffix: "px" } } }, - { name: "theme", selector: { theme: {} } }, - ], - }, - { - name: "tap_action", - selector: { "ui-action": {} }, - }, - { - name: "hold_action", - selector: { "ui-action": {} }, - }, - ] as const - ); - protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; } - const entityState = this._config.entity - ? this.hass.states[this._config.entity] - : undefined; - - const schema = this._schema( - this._config.entity, - this._config.icon, - entityState - ); - const data = { show_name: true, show_icon: true, @@ -130,7 +107,7 @@ export class HuiButtonCardEditor > - ) => { + private _computeHelperCallback = (schema: SchemaUnion) => { switch (schema.name) { case "tap_action": case "hold_action": @@ -162,9 +137,7 @@ export class HuiButtonCardEditor } }; - private _computeLabelCallback = ( - schema: SchemaUnion> - ) => { + private _computeLabelCallback = (schema: SchemaUnion) => { switch (schema.name) { case "theme": case "tap_action": diff --git a/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts index 37e3c34874..4b1df3306d 100644 --- a/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts @@ -1,11 +1,7 @@ -import type { HassEntity } from "home-assistant-js-websocket/dist/types"; import { html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; import { assert, assign, boolean, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import { domainIcon } from "../../../../common/entity/domain_icon"; import { entityId } from "../../../../common/structs/is-entity-id"; import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; @@ -29,6 +25,38 @@ const cardConfigStruct = assign( }) ); +const SCHEMA = [ + { name: "entity", required: true, selector: { entity: {} } }, + { + type: "grid", + name: "", + schema: [ + { name: "name", selector: { text: {} } }, + { + name: "icon", + selector: { + icon: {}, + }, + context: { + icon_entity: "entity", + }, + }, + { + name: "attribute", + selector: { + attribute: {}, + }, + context: { + filter_entity: "entity", + }, + }, + { name: "unit", selector: { text: {} } }, + { name: "theme", selector: { theme: {} } }, + { name: "state_color", selector: { boolean: {} } }, + ], + }, +] as const; + @customElement("hui-entity-card-editor") export class HuiEntityCardEditor extends LitElement @@ -43,58 +71,16 @@ export class HuiEntityCardEditor this._config = config; } - private _schema = memoizeOne( - (entity: string, icon: string, entityState: HassEntity) => - [ - { name: "entity", required: true, selector: { entity: {} } }, - { - type: "grid", - name: "", - schema: [ - { name: "name", selector: { text: {} } }, - { - name: "icon", - selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && !entityState?.attributes.icon && entityState - ? domainIcon(computeDomain(entity), entityState) - : undefined, - }, - }, - }, - - { - name: "attribute", - selector: { attribute: { entity_id: entity } }, - }, - { name: "unit", selector: { text: {} } }, - { name: "theme", selector: { theme: {} } }, - { name: "state_color", selector: { boolean: {} } }, - ], - }, - ] as const - ); - protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; } - const entityState = this.hass.states[this._config.entity]; - - const schema = this._schema( - this._config.entity, - this._config.icon, - entityState - ); - return html` @@ -107,9 +93,7 @@ export class HuiEntityCardEditor fireEvent(this, "config-changed", { config }); } - private _computeLabelCallback = ( - schema: SchemaUnion> - ) => { + private _computeLabelCallback = (schema: SchemaUnion) => { if (schema.name === "entity") { return this.hass!.localize( "ui.panel.lovelace.editor.card.generic.entity" 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 f02e3a8908..af2598f69d 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 @@ -1,13 +1,11 @@ -import "../../../../components/ha-form/ha-form"; -import type { HassEntity } from "home-assistant-js-websocket"; import { html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { assert } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { computeDomain } from "../../../../common/entity/compute_domain"; -import { domainIcon } from "../../../../common/entity/domain_icon"; import type { LocalizeFunc } from "../../../../common/translations/localize"; +import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { EntitiesCardEntityConfig } from "../../cards/types"; @@ -39,73 +37,56 @@ export class HuiGenericEntityRowEditor this._config = config; } - private _schema = memoizeOne( - ( - entity: string, - icon: string | undefined, - entityState: HassEntity, - localize: LocalizeFunc - ) => { - const domain = computeDomain(entity); + private _schema = memoizeOne((entity: string, localize: LocalizeFunc) => { + const domain = computeDomain(entity); - return [ - { name: "entity", required: true, selector: { entity: {} } }, - { - type: "grid", - name: "", - schema: [ - { name: "name", selector: { text: {} } }, - { - name: "icon", - selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && !entityState?.attributes.icon && entityState - ? domainIcon(domain, entityState) - : undefined, - }, - }, + return [ + { name: "entity", required: true, selector: { entity: {} } }, + { + type: "grid", + name: "", + schema: [ + { name: "name", selector: { text: {} } }, + { + name: "icon", + selector: { + icon: {}, }, - ], - }, - { - name: "secondary_info", - selector: { - select: { - options: ( - Object.keys(SecondaryInfoValues).filter( - (info) => - !("domains" in SecondaryInfoValues[info]) || - ("domains" in SecondaryInfoValues[info] && - SecondaryInfoValues[info].domains!.includes(domain)) - ) as Array - ).map((info) => ({ - value: info, - label: localize( - `ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}` - ), - })), + context: { + icon_entity: "entity", }, }, + ], + }, + { + name: "secondary_info", + selector: { + select: { + options: ( + Object.keys(SecondaryInfoValues).filter( + (info) => + !("domains" in SecondaryInfoValues[info]) || + ("domains" in SecondaryInfoValues[info] && + SecondaryInfoValues[info].domains!.includes(domain)) + ) as Array + ).map((info) => ({ + value: info, + label: localize( + `ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}` + ), + })), + }, }, - ] as const; - } - ); + }, + ] as const; + }); protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; } - const entityState = this.hass.states[this._config.entity]; - - const schema = this._schema( - this._config.entity, - this._config.icon, - entityState, - this.hass.localize - ); + const schema = this._schema(this._config.entity, this.hass.localize); return html` - [ - { - name: "entity", - required: true, - selector: { entity: { domain: "light" } }, - }, - { - type: "grid", - name: "", - schema: [ - { name: "name", selector: { text: {} } }, - { - name: "icon", - selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && !entityState?.attributes.icon && entityState - ? domainIcon(computeDomain(entity), entityState) - : undefined, - }, - }, - }, - ], - }, - { name: "theme", selector: { theme: {} } }, - { - name: "hold_action", - selector: { "ui-action": {} }, - }, - { - name: "double_tap_action", - selector: { "ui-action": {} }, - }, - ] as const - ); - protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; } - const entityState = this.hass.states[this._config.entity]; - const schema = this._schema( - this._config.entity, - this._config.icon, - entityState - ); - return html` @@ -108,9 +91,7 @@ export class HuiLightCardEditor fireEvent(this, "config-changed", { config: ev.detail.value }); } - private _computeLabelCallback = ( - schema: SchemaUnion> - ) => { + private _computeLabelCallback = (schema: SchemaUnion) => { switch (schema.name) { case "theme": case "hold_action": diff --git a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts index d9c6d314a7..7f8d0b8dc3 100644 --- a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts @@ -1,7 +1,5 @@ -import type { HassEntity } from "home-assistant-js-websocket"; import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; import { assert, assign, @@ -13,8 +11,6 @@ import { union, } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import { domainIcon } from "../../../../common/entity/domain_icon"; import { entityId } from "../../../../common/structs/is-entity-id"; import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; @@ -38,6 +34,55 @@ const cardConfigStruct = assign( }) ); +const SCHEMA = [ + { + name: "entity", + selector: { + entity: { domain: ["counter", "input_number", "number", "sensor"] }, + }, + }, + { name: "name", selector: { text: {} } }, + { + type: "grid", + name: "", + schema: [ + { + name: "icon", + selector: { + icon: {}, + }, + context: { + icon_entity: "entity", + }, + }, + { + name: "graph", + selector: { + select: { + options: [ + { + value: "none", + label: "None", + }, + { + value: "line", + label: "Line", + }, + ], + }, + }, + }, + { name: "unit", selector: { text: {} } }, + { name: "detail", selector: { boolean: {} } }, + { name: "theme", selector: { theme: {} } }, + { + name: "hours_to_show", + selector: { number: { min: 1, mode: "box" } }, + }, + ], + }, +] as const; + @customElement("hui-sensor-card-editor") export class HuiSensorCardEditor extends LitElement @@ -52,74 +97,11 @@ export class HuiSensorCardEditor this._config = config; } - private _schema = memoizeOne( - (entity: string, icon: string | undefined, entityState: HassEntity) => - [ - { - name: "entity", - selector: { - entity: { domain: ["counter", "input_number", "number", "sensor"] }, - }, - }, - { name: "name", selector: { text: {} } }, - { - type: "grid", - name: "", - schema: [ - { - name: "icon", - selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && !entityState?.attributes.icon && entityState - ? domainIcon(computeDomain(entity), entityState) - : undefined, - }, - }, - }, - { - name: "graph", - selector: { - select: { - options: [ - { - value: "none", - label: "None", - }, - { - value: "line", - label: "Line", - }, - ], - }, - }, - }, - { name: "unit", selector: { text: {} } }, - { name: "detail", selector: { boolean: {} } }, - { name: "theme", selector: { theme: {} } }, - { - name: "hours_to_show", - selector: { number: { min: 1, mode: "box" } }, - }, - ], - }, - ] as const - ); - protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; } - const entityState = this.hass.states[this._config.entity]; - - const schema = this._schema( - this._config.entity, - this._config.icon, - entityState - ); - const data = { hours_to_show: 24, graph: "none", @@ -131,7 +113,7 @@ export class HuiSensorCardEditor @@ -144,9 +126,7 @@ export class HuiSensorCardEditor fireEvent(this, "config-changed", { config }); } - private _computeLabelCallback = ( - schema: SchemaUnion> - ) => { + private _computeLabelCallback = (schema: SchemaUnion) => { switch (schema.name) { case "theme": return `${this.hass!.localize( diff --git a/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts index aa89edb420..03e58aecc0 100644 --- a/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-statistic-card-editor.ts @@ -1,11 +1,8 @@ -import type { HassEntity } from "home-assistant-js-websocket/dist/types"; import { html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { any, assert, assign, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import { domainIcon } from "../../../../common/entity/domain_icon"; import { LocalizeFunc } from "../../../../common/translations/localize"; import { deepEqual } from "../../../../common/util/deep-equal"; import "../../../../components/ha-form/ha-form"; @@ -100,10 +97,7 @@ export class HuiStatisticCardEditor private _schema = memoizeOne( ( - entity: string, - icon: string, selectedPeriodKey: string | undefined, - entityState: HassEntity, localize: LocalizeFunc, metadata?: StatisticsMetaData ) => @@ -155,13 +149,10 @@ export class HuiStatisticCardEditor { name: "icon", selector: { - icon: { - placeholder: icon || entityState?.attributes.icon, - fallbackPath: - !icon && !entityState?.attributes.icon && entityState - ? domainIcon(computeDomain(entity), entityState) - : undefined, - }, + icon: {}, + }, + context: { + icon_entity: "entity", }, }, { name: "unit", selector: { text: {} } }, @@ -176,15 +167,10 @@ export class HuiStatisticCardEditor return html``; } - const entityState = this.hass.states[this._config.entity]; - const data = this._data(this._config); const schema = this._schema( - this._config.entity, - this._config.icon, typeof data.period === "string" ? data.period : undefined, - entityState, this.hass.localize, this._metadata ); diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts index d62f0e071e..348ace3f33 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts @@ -14,9 +14,8 @@ import { string, } from "superstruct"; import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import { domainIcon } from "../../../../common/entity/domain_icon"; import { entityId } from "../../../../common/structs/is-entity-id"; +import { LocalizeFunc } from "../../../../common/translations/localize"; import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; @@ -65,16 +64,14 @@ export class HuiTileCardEditor } private _schema = memoizeOne( - (entity: string, icon?: string, stateObj?: HassEntity) => + (localize: LocalizeFunc) => [ { name: "entity", selector: { entity: {} } }, { name: "", type: "expandable", iconPath: mdiPalette, - title: this.hass!.localize( - `ui.panel.lovelace.editor.card.tile.appearance` - ), + title: localize(`ui.panel.lovelace.editor.card.tile.appearance`), schema: [ { name: "", @@ -84,14 +81,9 @@ export class HuiTileCardEditor { name: "icon", selector: { - icon: { - placeholder: icon || stateObj?.attributes.icon, - fallbackPath: - !icon && !stateObj?.attributes.icon && stateObj - ? domainIcon(computeDomain(entity), stateObj) - : undefined, - }, + icon: {}, }, + context: { icon_entity: "entity" }, }, { name: "color", @@ -118,9 +110,7 @@ export class HuiTileCardEditor { name: "", type: "expandable", - title: this.hass!.localize( - `ui.panel.lovelace.editor.card.tile.actions` - ), + title: localize(`ui.panel.lovelace.editor.card.tile.actions`), iconPath: mdiGestureTap, schema: [ { @@ -153,11 +143,7 @@ export class HuiTileCardEditor | HassEntity | undefined; - const schema = this._schema( - this._config.entity, - this._config.icon, - stateObj - ); + const schema = this._schema(this.hass!.localize); if (this._subElementEditorConfig) { return html` diff --git a/src/panels/lovelace/header-footer/hui-graph-header-footer.ts b/src/panels/lovelace/header-footer/hui-graph-header-footer.ts index 63ea87d580..5a0eab108a 100644 --- a/src/panels/lovelace/header-footer/hui-graph-header-footer.ts +++ b/src/panels/lovelace/header-footer/hui-graph-header-footer.ts @@ -131,7 +131,7 @@ export class HuiGraphHeaderFooter public connectedCallback() { super.connectedCallback(); - if (this.hasUpdated) { + if (this.hasUpdated && this._config) { this._subscribeHistory(); } } @@ -142,27 +142,31 @@ export class HuiGraphHeaderFooter } private _subscribeHistory() { - if (!isComponentLoaded(this.hass!, "history") || this._subscribed) { + if ( + !isComponentLoaded(this.hass!, "history") || + this._subscribed || + !this._config + ) { return; } this._subscribed = subscribeHistoryStatesTimeWindow( this.hass!, (combinedHistory) => { - if (!this._subscribed) { + if (!this._subscribed || !this._config) { // Message came in before we had a chance to unload return; } this._coordinates = coordinatesMinimalResponseCompressedState( - combinedHistory[this._config!.entity], - this._config!.hours_to_show!, + combinedHistory[this._config.entity], + this._config.hours_to_show!, 500, - this._config!.detail!, - this._config!.limits + this._config.detail!, + this._config.limits ) || []; }, - this._config!.hours_to_show!, - [this._config!.entity] + this._config.hours_to_show!, + [this._config.entity] ).catch((err) => { this._subscribed = undefined; this._error = err; diff --git a/src/resources/compatibility.ts b/src/resources/compatibility.ts index 07f80f0ff8..5e89e2f11a 100644 --- a/src/resources/compatibility.ts +++ b/src/resources/compatibility.ts @@ -20,6 +20,12 @@ import "@formatjs/intl-datetimeformat/add-all-tz"; import "proxy-polyfill"; import "unfetch/polyfill"; +import ResizeObserver from "resize-observer-polyfill"; + +if (!window.ResizeObserver) { + window.ResizeObserver = ResizeObserver; +} + // Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md (function (arr) { arr.forEach((item) => { diff --git a/yarn.lock b/yarn.lock index 6955556508..4257048348 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1360,7 +1360,7 @@ __metadata: languageName: node linkType: hard -"@braintree/sanitize-url@npm:^6.0.0": +"@braintree/sanitize-url@npm:^6.0.2": version: 6.0.2 resolution: "@braintree/sanitize-url@npm:6.0.2" checksum: 6a9dfd4081cc96516eeb281d1a83d3b5f1ad3d2837adf968fcc2ba18889ee833554f9c641b4083c36d3360a932e4504ddf25b0b51e9933c3742622df82cf7c9a @@ -1472,16 +1472,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/ecma402-abstract@npm:1.10.0": - version: 1.10.0 - resolution: "@formatjs/ecma402-abstract@npm:1.10.0" - dependencies: - "@formatjs/intl-localematcher": 0.2.21 - tslib: ^2.1.0 - checksum: 3ea000ba9e9e9ca21018a782d0cd26c0ef84ce7a242b3538b17f4450ff5eeac539a0d1b41bad629499bd8c7857119da5363690958e68bb4724bb01341f68559d - languageName: node - linkType: hard - "@formatjs/ecma402-abstract@npm:1.14.3": version: 1.14.3 resolution: "@formatjs/ecma402-abstract@npm:1.14.3" @@ -1522,14 +1512,14 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-datetimeformat@npm:^4.2.5": - version: 4.2.5 - resolution: "@formatjs/intl-datetimeformat@npm:4.2.5" +"@formatjs/intl-datetimeformat@npm:^6.4.3": + version: 6.4.3 + resolution: "@formatjs/intl-datetimeformat@npm:6.4.3" dependencies: - "@formatjs/ecma402-abstract": 1.10.0 - "@formatjs/intl-localematcher": 0.2.21 - tslib: ^2.1.0 - checksum: 6d796ba2dd98b49884f542ba902a0fc8b36464371c970673cfd2c9ce312df9eb0c9a41ceaf1cab23e36981e4686428ecc15feecc7f100eebc8be20e68ca36de5 + "@formatjs/ecma402-abstract": 1.14.3 + "@formatjs/intl-localematcher": 0.2.32 + tslib: ^2.4.0 + checksum: 5c77fd9e823a9596665abf45607cfcc3aba786836aeb0a79aeb67026b7991ef4963f0b10998994fc2b487bb1c777145d5ef2938d3e8dd44d4048fe36e779f038 languageName: node linkType: hard @@ -1553,15 +1543,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-localematcher@npm:0.2.21": - version: 0.2.21 - resolution: "@formatjs/intl-localematcher@npm:0.2.21" - dependencies: - tslib: ^2.1.0 - checksum: d766eb8ce8b2628d781fdb34fd0833a0a1b28f20e70a72dfabbca27cf02bd1b994a72c357b2b3d4888bc20c33b6b7cc7e10e92847ec228a40745a2e84d8d2e24 - languageName: node - linkType: hard - "@formatjs/intl-localematcher@npm:0.2.32": version: 0.2.32 resolution: "@formatjs/intl-localematcher@npm:0.2.32" @@ -1571,13 +1552,14 @@ __metadata: languageName: node linkType: hard -"@formatjs/intl-numberformat@npm:^7.2.5": - version: 7.2.5 - resolution: "@formatjs/intl-numberformat@npm:7.2.5" +"@formatjs/intl-numberformat@npm:^8.3.3": + version: 8.3.3 + resolution: "@formatjs/intl-numberformat@npm:8.3.3" dependencies: - "@formatjs/ecma402-abstract": 1.10.0 - tslib: ^2.1.0 - checksum: 1eda71418fbffbf7fc07eebbe04eca93cf84879b7d445cfc3fa130b5bf6a65b241d784fd7741b46fbb65d699aa5949fa8e613c51cb7833d7f67d8c4210ad67cc + "@formatjs/ecma402-abstract": 1.14.3 + "@formatjs/intl-localematcher": 0.2.32 + tslib: ^2.4.0 + checksum: 56fc8e25c445944f43295ee28a8f2dd059770e28e4a368b977787591157f3da357528ee8bcab5e00a54c9f850a879b1fedb92cc1934b93400a30699a3834c119 languageName: node linkType: hard @@ -4317,6 +4299,13 @@ __metadata: languageName: node linkType: hard +"@types/offscreencanvas@npm:^2019.6.4": + version: 2019.7.0 + resolution: "@types/offscreencanvas@npm:2019.7.0" + checksum: 018cfcd19e0c59c44d14ba61caaca7246f77fbb512839c7881654b7f2b6591dbdd5857362eccbf49f29cdc93724e71a4b37c8b6cf203388f9c04e913a53ea390 + languageName: node + linkType: hard + "@types/parse5@npm:^6.0.0, @types/parse5@npm:^6.0.1": version: 6.0.3 resolution: "@types/parse5@npm:6.0.3" @@ -7001,10 +6990,10 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^3.15.2": - version: 3.15.2 - resolution: "core-js@npm:3.15.2" - checksum: f8f61569c4c3bdf50679226f5a1045551192a2f4bc3fa46a873b6fa834cff6d1634ee138a6e4bae3eea99f1b1db2d588fa693de74640447476292b41f595a4c3 +"core-js@npm:^3.27.2": + version: 3.27.2 + resolution: "core-js@npm:3.27.2" + checksum: 718debd426f55a6b97cf9b757c936be258afd6d4f7052f89d0f96c982d7013e9000b0b006df42831a0cf32adad298e34d6a19052dce9ae1c7ab87162c0c665e0 languageName: node linkType: hard @@ -9378,7 +9367,7 @@ fsevents@^1.2.7: "@babel/plugin-syntax-top-level-await": ^7.14.5 "@babel/preset-env": ^7.20.2 "@babel/preset-typescript": ^7.18.6 - "@braintree/sanitize-url": ^6.0.0 + "@braintree/sanitize-url": ^6.0.2 "@codemirror/autocomplete": ^6.4.0 "@codemirror/commands": ^6.2.0 "@codemirror/language": ^6.4.0 @@ -9386,10 +9375,10 @@ fsevents@^1.2.7: "@codemirror/search": ^6.2.3 "@codemirror/state": ^6.2.0 "@codemirror/view": ^6.7.1 - "@formatjs/intl-datetimeformat": ^4.2.5 + "@formatjs/intl-datetimeformat": ^6.4.3 "@formatjs/intl-getcanonicallocales": ^2.0.5 "@formatjs/intl-locale": ^3.0.11 - "@formatjs/intl-numberformat": ^7.2.5 + "@formatjs/intl-numberformat": ^8.3.3 "@formatjs/intl-pluralrules": ^5.1.8 "@formatjs/intl-relativetimeformat": ^11.1.8 "@fullcalendar/common": ^5.11.4 @@ -9481,7 +9470,7 @@ fsevents@^1.2.7: chai: ^4.3.4 chart.js: ^3.3.2 comlink: ^4.3.1 - core-js: ^3.15.2 + core-js: ^3.27.2 cropperjs: ^1.5.13 date-fns: ^2.29.3 date-fns-tz: ^1.3.7 @@ -9538,7 +9527,7 @@ fsevents@^1.2.7: prettier: ^2.8.3 proxy-polyfill: ^0.3.2 punycode: ^2.3.0 - qr-scanner: ^1.3.0 + qr-scanner: ^1.4.2 qrcode: ^1.5.1 regenerator-runtime: ^0.13.11 require-dir: ^1.2.0 @@ -9552,7 +9541,7 @@ fsevents@^1.2.7: serve: ^11.3.2 sinon: ^15.0.1 sortablejs: ^1.14.0 - source-map-url: ^0.4.0 + source-map-url: ^0.4.1 superstruct: ^1.0.3 systemjs: ^6.13.0 tar: ^6.1.11 @@ -9560,7 +9549,7 @@ fsevents@^1.2.7: tinykeys: ^1.1.3 ts-lit-plugin: ^1.2.1 tsparticles: ^1.34.0 - typescript: ^4.9.4 + typescript: ^4.9.5 unfetch: ^4.1.0 vinyl-buffer: ^1.0.1 vinyl-source-stream: ^2.0.0 @@ -9655,9 +9644,9 @@ fsevents@^1.2.7: linkType: hard "http-cache-semantics@npm:^4.1.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 languageName: node linkType: hard @@ -13198,10 +13187,12 @@ fsevents@^1.2.7: languageName: node linkType: hard -"qr-scanner@npm:^1.3.0": - version: 1.3.0 - resolution: "qr-scanner@npm:1.3.0" - checksum: 421ff00626252d0f9e50550fb550a463166e4d0438baffb469c9450079f1f802f6df22784509bb571ef50ece81aecaadc00f91d442959f37655ad29710c81c8b +"qr-scanner@npm:^1.4.2": + version: 1.4.2 + resolution: "qr-scanner@npm:1.4.2" + dependencies: + "@types/offscreencanvas": ^2019.6.4 + checksum: 554855c1d61cc9f502162d43d76d0cd03d6df77b0ecbfcae2c0f936c31cbd1ac45e673857b91628dde1d3f5156eca6651c4973187fa061ae9ec7ec07a95f7738 languageName: node linkType: hard @@ -14413,10 +14404,10 @@ fsevents@^1.2.7: languageName: node linkType: hard -"source-map-url@npm:^0.4.0": - version: 0.4.0 - resolution: "source-map-url@npm:0.4.0" - checksum: 63ed54045fcd7b4ec7ca17513f48fdc23b573eef679326ecf1a31333e1aaecc0a9c085adaa7d118283b160e65b71cc72da9e1385f2de4ac5ed68294e3920d719 +"source-map-url@npm:^0.4.0, source-map-url@npm:^0.4.1": + version: 0.4.1 + resolution: "source-map-url@npm:0.4.1" + checksum: 64c5c2c77aff815a6e61a4120c309ae4cac01298d9bcbb3deb1b46a4dd4c46d4a1eaeda79ec9f684766ae80e8dc86367b89326ce9dd2b89947bd9291fc1ac08c languageName: node linkType: hard @@ -15380,13 +15371,13 @@ typescript@^3.8.3: languageName: node linkType: hard -"typescript@npm:^4.9.4": - version: 4.9.4 - resolution: "typescript@npm:4.9.4" +"typescript@npm:^4.9.5": + version: 4.9.5 + resolution: "typescript@npm:4.9.5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: e782fb9e0031cb258a80000f6c13530288c6d63f1177ed43f770533fdc15740d271554cdae86701c1dd2c83b082cea808b07e97fd68b38a172a83dbf9e0d0ef9 + checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db languageName: node linkType: hard @@ -15400,13 +15391,13 @@ typescript@^3.8.3: languageName: node linkType: hard -"typescript@patch:typescript@^4.9.4#~builtin": - version: 4.9.4 - resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=ad5954" +"typescript@patch:typescript@^4.9.5#~builtin": + version: 4.9.5 + resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=ad5954" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 1caaea6cb7f813e64345190fddc4e6c924d0b698ab81189b503763c4a18f7f5501c69362979d36e19c042d89d936443e768a78b0675690b35eb663d19e0eae71 + checksum: 8f6260acc86b56bfdda6004bc53f32ea548f543e8baef7071c8e34d29d292f3e375c8416556c8de10b24deef6933cd1c16a8233dc84a3dd43a13a13265d0faab languageName: node linkType: hard