From 4582c3ba0a43e5a4bf07fe24bc8e926a929bbac1 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:30:39 +0100 Subject: [PATCH] Migrate state cards from Polymer to Lit (#18257) --- gallery/src/components/demo-more-info.ts | 2 +- .../src/pages/more-info/input-text.markdown | 3 + gallery/src/pages/more-info/input-text.ts | 46 ++++ gallery/src/pages/more-info/lock.markdown | 3 + gallery/src/pages/more-info/lock.ts | 49 +++++ .../src/pages/more-info/media-player.markdown | 3 + gallery/src/pages/more-info/media-player.ts | 41 ++++ gallery/src/pages/more-info/number.markdown | 3 + gallery/src/pages/more-info/number.ts | 78 +++++++ gallery/src/pages/more-info/scene.markdown | 3 + gallery/src/pages/more-info/scene.ts | 49 +++++ gallery/src/pages/more-info/timer.markdown | 3 + gallery/src/pages/more-info/timer.ts | 46 ++++ gallery/src/pages/more-info/vacuum.markdown | 3 + gallery/src/pages/more-info/vacuum.ts | 50 +++++ src/common/dom/dynamic_content_updater.ts | 33 --- src/components/entity/state-info.ts | 2 +- src/components/ha-vacuum-state.js | 89 -------- src/components/ha-vacuum-state.ts | 111 ++++++++++ src/components/ha-water_heater-control.js | 135 ------------ ...ater-state.js => ha-water_heater-state.ts} | 108 +++++----- src/dialogs/more-info/ha-more-info-info.ts | 2 +- src/dialogs/more-info/more-info-content.ts | 68 ++---- src/state-summary/state-card-alert.ts | 8 +- src/state-summary/state-card-button.ts | 2 +- src/state-summary/state-card-climate.js | 55 ----- src/state-summary/state-card-climate.ts | 54 +++++ src/state-summary/state-card-configurator.js | 69 ------ src/state-summary/state-card-configurator.ts | 59 +++++ src/state-summary/state-card-content.js | 62 ------ src/state-summary/state-card-content.ts | 65 ++++++ src/state-summary/state-card-display.ts | 8 +- src/state-summary/state-card-event.ts | 2 +- src/state-summary/state-card-humidifier.js | 55 ----- src/state-summary/state-card-humidifier.ts | 56 +++++ src/state-summary/state-card-input_button.ts | 2 +- src/state-summary/state-card-input_text.js | 90 -------- src/state-summary/state-card-input_text.ts | 89 ++++++++ src/state-summary/state-card-lock.js | 95 --------- src/state-summary/state-card-lock.ts | 83 ++++++++ src/state-summary/state-card-media_player.js | 97 --------- src/state-summary/state-card-media_player.ts | 83 ++++++++ src/state-summary/state-card-number.js | 201 ------------------ src/state-summary/state-card-number.ts | 168 +++++++++++++++ src/state-summary/state-card-scene.js | 60 ------ src/state-summary/state-card-scene.ts | 56 +++++ src/state-summary/state-card-script.ts | 8 +- src/state-summary/state-card-timer.js | 97 --------- src/state-summary/state-card-timer.ts | 108 ++++++++++ src/state-summary/state-card-toggle.js | 50 ----- src/state-summary/state-card-toggle.ts | 51 +++++ src/state-summary/state-card-vacuum.js | 44 ---- src/state-summary/state-card-vacuum.ts | 44 ++++ src/state-summary/state-card-water_heater.js | 55 ----- src/state-summary/state-card-water_heater.ts | 55 +++++ ...er-model.js => hass-media-player-model.ts} | 13 +- 56 files changed, 1574 insertions(+), 1400 deletions(-) create mode 100644 gallery/src/pages/more-info/input-text.markdown create mode 100644 gallery/src/pages/more-info/input-text.ts create mode 100644 gallery/src/pages/more-info/lock.markdown create mode 100644 gallery/src/pages/more-info/lock.ts create mode 100644 gallery/src/pages/more-info/media-player.markdown create mode 100644 gallery/src/pages/more-info/media-player.ts create mode 100644 gallery/src/pages/more-info/number.markdown create mode 100644 gallery/src/pages/more-info/number.ts create mode 100644 gallery/src/pages/more-info/scene.markdown create mode 100644 gallery/src/pages/more-info/scene.ts create mode 100644 gallery/src/pages/more-info/timer.markdown create mode 100644 gallery/src/pages/more-info/timer.ts create mode 100644 gallery/src/pages/more-info/vacuum.markdown create mode 100644 gallery/src/pages/more-info/vacuum.ts delete mode 100644 src/common/dom/dynamic_content_updater.ts delete mode 100644 src/components/ha-vacuum-state.js create mode 100644 src/components/ha-vacuum-state.ts delete mode 100644 src/components/ha-water_heater-control.js rename src/components/{ha-water_heater-state.js => ha-water_heater-state.ts} (57%) delete mode 100644 src/state-summary/state-card-climate.js create mode 100644 src/state-summary/state-card-climate.ts delete mode 100644 src/state-summary/state-card-configurator.js create mode 100644 src/state-summary/state-card-configurator.ts delete mode 100644 src/state-summary/state-card-content.js create mode 100644 src/state-summary/state-card-content.ts delete mode 100644 src/state-summary/state-card-humidifier.js create mode 100644 src/state-summary/state-card-humidifier.ts delete mode 100644 src/state-summary/state-card-input_text.js create mode 100644 src/state-summary/state-card-input_text.ts delete mode 100644 src/state-summary/state-card-lock.js create mode 100644 src/state-summary/state-card-lock.ts delete mode 100644 src/state-summary/state-card-media_player.js create mode 100644 src/state-summary/state-card-media_player.ts delete mode 100644 src/state-summary/state-card-number.js create mode 100644 src/state-summary/state-card-number.ts delete mode 100644 src/state-summary/state-card-scene.js create mode 100644 src/state-summary/state-card-scene.ts delete mode 100644 src/state-summary/state-card-timer.js create mode 100644 src/state-summary/state-card-timer.ts delete mode 100644 src/state-summary/state-card-toggle.js create mode 100644 src/state-summary/state-card-toggle.ts delete mode 100644 src/state-summary/state-card-vacuum.js create mode 100644 src/state-summary/state-card-vacuum.ts delete mode 100644 src/state-summary/state-card-water_heater.js create mode 100644 src/state-summary/state-card-water_heater.ts rename src/util/{hass-media-player-model.js => hass-media-player-model.ts} (93%) diff --git a/gallery/src/components/demo-more-info.ts b/gallery/src/components/demo-more-info.ts index 2aa2ca1336..eeb5c92264 100644 --- a/gallery/src/components/demo-more-info.ts +++ b/gallery/src/components/demo-more-info.ts @@ -23,7 +23,7 @@ class DemoMoreInfo extends LitElement { ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-input-text": DemoMoreInfoInputText; + } +} diff --git a/gallery/src/pages/more-info/lock.markdown b/gallery/src/pages/more-info/lock.markdown new file mode 100644 index 0000000000..5d9d6020de --- /dev/null +++ b/gallery/src/pages/more-info/lock.markdown @@ -0,0 +1,3 @@ +--- +title: Lock +--- diff --git a/gallery/src/pages/more-info/lock.ts b/gallery/src/pages/more-info/lock.ts new file mode 100644 index 0000000000..4fe7e46563 --- /dev/null +++ b/gallery/src/pages/more-info/lock.ts @@ -0,0 +1,49 @@ +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/dialogs/more-info/more-info-content"; +import { getEntity } from "../../../../src/fake_data/entity"; +import { + MockHomeAssistant, + provideHass, +} from "../../../../src/fake_data/provide_hass"; +import "../../components/demo-more-infos"; + +const ENTITIES = [ + getEntity("lock", "lock", "locked", { + friendly_name: "Lock", + device_class: "lock", + }), + getEntity("lock", "unavailable", "unavailable", { + friendly_name: "Unavailable lock", + }), +]; + +@customElement("demo-more-info-lock") +class DemoMoreInfoLock extends LitElement { + @property() public hass!: MockHomeAssistant; + + @query("demo-more-infos") private _demoRoot!: HTMLElement; + + protected render(): TemplateResult { + return html` + ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-lock": DemoMoreInfoLock; + } +} diff --git a/gallery/src/pages/more-info/media-player.markdown b/gallery/src/pages/more-info/media-player.markdown new file mode 100644 index 0000000000..408f882d27 --- /dev/null +++ b/gallery/src/pages/more-info/media-player.markdown @@ -0,0 +1,3 @@ +--- +title: Media Player +--- diff --git a/gallery/src/pages/more-info/media-player.ts b/gallery/src/pages/more-info/media-player.ts new file mode 100644 index 0000000000..f02cc2ec5a --- /dev/null +++ b/gallery/src/pages/more-info/media-player.ts @@ -0,0 +1,41 @@ +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/dialogs/more-info/more-info-content"; +import { + MockHomeAssistant, + provideHass, +} from "../../../../src/fake_data/provide_hass"; +import "../../components/demo-more-infos"; +import { createMediaPlayerEntities } from "../../data/media_players"; + +const ENTITIES = createMediaPlayerEntities(); + +@customElement("demo-more-info-media-player") +class DemoMoreInfoMediaPlayer extends LitElement { + @property() public hass!: MockHomeAssistant; + + @query("demo-more-infos") private _demoRoot!: HTMLElement; + + protected render(): TemplateResult { + return html` + ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-media-player": DemoMoreInfoMediaPlayer; + } +} diff --git a/gallery/src/pages/more-info/number.markdown b/gallery/src/pages/more-info/number.markdown new file mode 100644 index 0000000000..7925e504ef --- /dev/null +++ b/gallery/src/pages/more-info/number.markdown @@ -0,0 +1,3 @@ +--- +title: Number +--- diff --git a/gallery/src/pages/more-info/number.ts b/gallery/src/pages/more-info/number.ts new file mode 100644 index 0000000000..c38693596a --- /dev/null +++ b/gallery/src/pages/more-info/number.ts @@ -0,0 +1,78 @@ +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/dialogs/more-info/more-info-content"; +import { getEntity } from "../../../../src/fake_data/entity"; +import { + MockHomeAssistant, + provideHass, +} from "../../../../src/fake_data/provide_hass"; +import "../../components/demo-more-infos"; + +const ENTITIES = [ + getEntity("number", "box1", 0, { + friendly_name: "Box1", + min: 0, + max: 100, + step: 1, + initial: 0, + mode: "box", + unit_of_measurement: "items", + }), + getEntity("number", "slider1", 0, { + friendly_name: "Slider1", + min: 0, + max: 100, + step: 1, + initial: 0, + mode: "slider", + unit_of_measurement: "items", + }), + getEntity("number", "auto1", 0, { + friendly_name: "Auto1", + min: 0, + max: 1000, + step: 1, + initial: 0, + mode: "auto", + unit_of_measurement: "items", + }), + getEntity("number", "auto2", 0, { + friendly_name: "Auto2", + min: 0, + max: 100, + step: 1, + initial: 0, + mode: "auto", + unit_of_measurement: "items", + }), +]; + +@customElement("demo-more-info-number") +class DemoMoreInfoNumber extends LitElement { + @property() public hass!: MockHomeAssistant; + + @query("demo-more-infos") private _demoRoot!: HTMLElement; + + protected render(): TemplateResult { + return html` + ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-number": DemoMoreInfoNumber; + } +} diff --git a/gallery/src/pages/more-info/scene.markdown b/gallery/src/pages/more-info/scene.markdown new file mode 100644 index 0000000000..0913623cd3 --- /dev/null +++ b/gallery/src/pages/more-info/scene.markdown @@ -0,0 +1,3 @@ +--- +title: Scene +--- diff --git a/gallery/src/pages/more-info/scene.ts b/gallery/src/pages/more-info/scene.ts new file mode 100644 index 0000000000..aaa1385e6b --- /dev/null +++ b/gallery/src/pages/more-info/scene.ts @@ -0,0 +1,49 @@ +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/dialogs/more-info/more-info-content"; +import { getEntity } from "../../../../src/fake_data/entity"; +import { + MockHomeAssistant, + provideHass, +} from "../../../../src/fake_data/provide_hass"; +import "../../components/demo-more-infos"; + +const ENTITIES = [ + getEntity("scene", "romantic_lights", "scening", { + entity_id: ["light.bed_light", "light.ceiling_lights"], + friendly_name: "Romantic Scene", + }), + getEntity("scene", "unavailable", "unavailable", { + friendly_name: "Romantic Scene", + }), +]; + +@customElement("demo-more-info-scene") +class DemoMoreInfoScene extends LitElement { + @property() public hass!: MockHomeAssistant; + + @query("demo-more-infos") private _demoRoot!: HTMLElement; + + protected render(): TemplateResult { + return html` + ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-scene": DemoMoreInfoScene; + } +} diff --git a/gallery/src/pages/more-info/timer.markdown b/gallery/src/pages/more-info/timer.markdown new file mode 100644 index 0000000000..d9c1b17f9c --- /dev/null +++ b/gallery/src/pages/more-info/timer.markdown @@ -0,0 +1,3 @@ +--- +title: Timer +--- diff --git a/gallery/src/pages/more-info/timer.ts b/gallery/src/pages/more-info/timer.ts new file mode 100644 index 0000000000..404b31df03 --- /dev/null +++ b/gallery/src/pages/more-info/timer.ts @@ -0,0 +1,46 @@ +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/dialogs/more-info/more-info-content"; +import { getEntity } from "../../../../src/fake_data/entity"; +import { + MockHomeAssistant, + provideHass, +} from "../../../../src/fake_data/provide_hass"; +import "../../components/demo-more-infos"; + +const ENTITIES = [ + getEntity("timer", "timer", "idle", { + friendly_name: "Timer", + duration: "0:05:00", + }), +]; + +@customElement("demo-more-info-timer") +class DemoMoreInfoTimer extends LitElement { + @property() public hass!: MockHomeAssistant; + + @query("demo-more-infos") private _demoRoot!: HTMLElement; + + protected render(): TemplateResult { + return html` + ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-timer": DemoMoreInfoTimer; + } +} diff --git a/gallery/src/pages/more-info/vacuum.markdown b/gallery/src/pages/more-info/vacuum.markdown new file mode 100644 index 0000000000..a617869ff5 --- /dev/null +++ b/gallery/src/pages/more-info/vacuum.markdown @@ -0,0 +1,3 @@ +--- +title: Vacuum +--- diff --git a/gallery/src/pages/more-info/vacuum.ts b/gallery/src/pages/more-info/vacuum.ts new file mode 100644 index 0000000000..e1a62f0a26 --- /dev/null +++ b/gallery/src/pages/more-info/vacuum.ts @@ -0,0 +1,50 @@ +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/dialogs/more-info/more-info-content"; +import { getEntity } from "../../../../src/fake_data/entity"; +import { + MockHomeAssistant, + provideHass, +} from "../../../../src/fake_data/provide_hass"; +import "../../components/demo-more-infos"; +import { VacuumEntityFeature } from "../../../../src/data/vacuum"; + +const ENTITIES = [ + getEntity("vacuum", "first_floor_vacuum", "docked", { + friendly_name: "First floor vacuum", + supported_features: + VacuumEntityFeature.START + + VacuumEntityFeature.STOP + + VacuumEntityFeature.RETURN_HOME, + }), +]; + +@customElement("demo-more-info-vacuum") +class DemoMoreInfoVacuum extends LitElement { + @property() public hass!: MockHomeAssistant; + + @query("demo-more-infos") private _demoRoot!: HTMLElement; + + protected render(): TemplateResult { + return html` + ent.entityId)} + > + `; + } + + protected firstUpdated(changedProperties: PropertyValues) { + super.firstUpdated(changedProperties); + const hass = provideHass(this._demoRoot); + hass.updateTranslations(null, "en"); + hass.addEntities(ENTITIES); + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-more-info-vacuum": DemoMoreInfoVacuum; + } +} diff --git a/src/common/dom/dynamic_content_updater.ts b/src/common/dom/dynamic_content_updater.ts deleted file mode 100644 index 3bdd2b0085..0000000000 --- a/src/common/dom/dynamic_content_updater.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Update root's child element to be newElementTag replacing another existing child if any. - * Copy attributes into the child element. - */ -export default function dynamicContentUpdater(root, newElementTag, attributes) { - const rootEl = root; - let customEl; - - if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) { - customEl = rootEl.lastChild; - } else { - if (rootEl.lastChild) { - rootEl.removeChild(rootEl.lastChild); - } - // Creating an element with upper case works fine in Chrome, but in FF it doesn't immediately - // become a defined Custom Element. Polymer does that in some later pass. - customEl = document.createElement(newElementTag.toLowerCase()); - } - - if (customEl.setProperties) { - customEl.setProperties(attributes); - } else { - // If custom element definition wasn't loaded yet - setProperties would be - // missing, but no harm in setting attributes one-by-one then. - Object.keys(attributes).forEach((key) => { - customEl[key] = attributes[key]; - }); - } - - if (customEl.parentNode === null) { - rootEl.appendChild(customEl); - } -} diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts index 2856239b11..5e1564ff4b 100644 --- a/src/components/entity/state-info.ts +++ b/src/components/entity/state-info.ts @@ -125,7 +125,7 @@ class StateInfo extends LitElement { text-overflow: ellipsis; } - .name[in-dialog], + .name[inDialog], :host([secondary-line]) .name { line-height: 20px; } diff --git a/src/components/ha-vacuum-state.js b/src/components/ha-vacuum-state.js deleted file mode 100644 index 371de7b4f5..0000000000 --- a/src/components/ha-vacuum-state.js +++ /dev/null @@ -1,89 +0,0 @@ -import "@material/mwc-button"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import LocalizeMixin from "../mixins/localize-mixin"; - -const STATES_INTERCEPTABLE = { - cleaning: { - action: "return_to_base", - service: "return_to_base", - }, - docked: { - action: "start_cleaning", - service: "start", - }, - idle: { - action: "start_cleaning", - service: "start", - }, - off: { - action: "turn_on", - service: "turn_on", - }, - on: { - action: "turn_off", - service: "turn_off", - }, - paused: { - action: "resume_cleaning", - service: "start", - }, -}; - -/* - * @appliesMixin LocalizeMixin - */ -class HaVacuumState extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - [[_computeLabel(stateObj.state, _interceptable)]] - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - _interceptable: { - type: Boolean, - computed: - "_computeInterceptable(stateObj.state, stateObj.attributes.supported_features)", - }, - }; - } - - _computeInterceptable(state, supportedFeatures) { - return state in STATES_INTERCEPTABLE && supportedFeatures !== 0; - } - - _computeLabel(state, interceptable) { - return interceptable - ? this.localize( - `ui.card.vacuum.actions.${STATES_INTERCEPTABLE[state].action}` - ) - : this.localize(`component.vacuum.entity_component._.state.${state}`); - } - - _callService(ev) { - ev.stopPropagation(); - const stateObj = this.stateObj; - const service = STATES_INTERCEPTABLE[stateObj.state].service; - this.hass.callService("vacuum", service, { entity_id: stateObj.entity_id }); - } -} -customElements.define("ha-vacuum-state", HaVacuumState); diff --git a/src/components/ha-vacuum-state.ts b/src/components/ha-vacuum-state.ts new file mode 100644 index 0000000000..0e033e8cde --- /dev/null +++ b/src/components/ha-vacuum-state.ts @@ -0,0 +1,111 @@ +import "@material/mwc-button"; +import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit"; +import { customElement, property } from "lit/decorators"; +import { HassEntity } from "home-assistant-js-websocket"; +import { haStyle } from "../resources/styles"; +import { HomeAssistant } from "../types"; + +const STATES_INTERCEPTABLE: { + [state: string]: { + action: + | "return_to_base" + | "start_cleaning" + | "turn_on" + | "turn_off" + | "resume_cleaning"; + service: string; + }; +} = { + cleaning: { + action: "return_to_base", + service: "return_to_base", + }, + docked: { + action: "start_cleaning", + service: "start", + }, + idle: { + action: "start_cleaning", + service: "start", + }, + off: { + action: "turn_on", + service: "turn_on", + }, + on: { + action: "turn_off", + service: "turn_off", + }, + paused: { + action: "resume_cleaning", + service: "start", + }, +}; + +@customElement("ha-vacuum-state") +export class HaVacuumState extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + protected render(): TemplateResult { + const interceptable = this._computeInterceptable( + this.stateObj.state, + this.stateObj.attributes.supported_features + ); + return html` + + ${this._computeLabel(this.stateObj.state, interceptable)} + + `; + } + + private _computeInterceptable( + state: string, + supportedFeatures: number | undefined + ) { + return state in STATES_INTERCEPTABLE && supportedFeatures !== 0; + } + + private _computeLabel(state: string, interceptable: boolean) { + return interceptable + ? this.hass.localize( + `ui.card.vacuum.actions.${STATES_INTERCEPTABLE[state].action}` + ) + : this.hass.localize( + `component.vacuum.entity_component._.state.${state}` + ); + } + + private async _callService(ev) { + ev.stopPropagation(); + const stateObj = this.stateObj; + const service = STATES_INTERCEPTABLE[stateObj.state].service; + await this.hass.callService("vacuum", service, { + entity_id: stateObj.entity_id, + }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + mwc-button { + top: 3px; + height: 37px; + margin-right: -0.57em; + } + mwc-button[disabled] { + background-color: transparent; + color: var(--secondary-text-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-vacuum-state": HaVacuumState; + } +} diff --git a/src/components/ha-water_heater-control.js b/src/components/ha-water_heater-control.js deleted file mode 100644 index a2521e5d8a..0000000000 --- a/src/components/ha-water_heater-control.js +++ /dev/null @@ -1,135 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { EventsMixin } from "../mixins/events-mixin"; -import "./ha-icon"; -import "./ha-icon-button"; - -/* - * @appliesMixin EventsMixin - */ -class HaWaterHeaterControl extends EventsMixin(PolymerElement) { - static get template() { - return html` - - - - -
[[value]] [[units]]
-
-
- - - -
-
- - - -
-
- `; - } - - static get properties() { - return { - value: { - type: Number, - observer: "valueChanged", - }, - units: { - type: String, - }, - min: { - type: Number, - }, - max: { - type: Number, - }, - step: { - type: Number, - value: 1, - }, - }; - } - - temperatureStateInFlux(inFlux) { - this.$.target_temperature.classList.toggle("in-flux", inFlux); - } - - incrementValue() { - const newval = this.value + this.step; - if (this.value < this.max) { - this.last_changed = Date.now(); - this.temperatureStateInFlux(true); - } - if (newval <= this.max) { - // If no initial target_temp - // this forces control to start - // from the min configured instead of 0 - if (newval <= this.min) { - this.value = this.min; - } else { - this.value = newval; - } - } else { - this.value = this.max; - } - } - - decrementValue() { - const newval = this.value - this.step; - if (this.value > this.min) { - this.last_changed = Date.now(); - this.temperatureStateInFlux(true); - } - if (newval >= this.min) { - this.value = newval; - } else { - this.value = this.min; - } - } - - valueChanged() { - // when the last_changed timestamp is changed, - // trigger a potential event fire in - // the future, as long as last changed is far enough in the - // past. - if (this.last_changed) { - window.setTimeout(() => { - const now = Date.now(); - if (now - this.last_changed >= 2000) { - this.fire("change"); - this.temperatureStateInFlux(false); - this.last_changed = null; - } - }, 2010); - } - } -} - -customElements.define("ha-water_heater-control", HaWaterHeaterControl); diff --git a/src/components/ha-water_heater-state.js b/src/components/ha-water_heater-state.ts similarity index 57% rename from src/components/ha-water_heater-state.js rename to src/components/ha-water_heater-state.ts index 2d768acfd2..2f47190147 100644 --- a/src/components/ha-water_heater-state.js +++ b/src/components/ha-water_heater-state.ts @@ -1,62 +1,30 @@ -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; +import { customElement, property } from "lit/decorators"; +import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit"; +import { HassEntity } from "home-assistant-js-websocket"; import { formatNumber } from "../common/number/format_number"; -import LocalizeMixin from "../mixins/localize-mixin"; +import { haStyle } from "../resources/styles"; +import { HomeAssistant } from "../types"; -/* - * @appliesMixin LocalizeMixin - */ -class HaWaterHeaterState extends LocalizeMixin(PolymerElement) { - static get template() { +@customElement("ha-water_heater-state") +export class HaWaterHeaterState extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + protected render(): TemplateResult { return html` - -
- [[_localizeState(stateObj)]] - [[computeTarget(hass, stateObj)]] + + ${this.hass.formatEntityState(this.stateObj)} + + ${this._computeTarget(this.hass, this.stateObj)}
- - `; } - static get properties() { - return { - hass: Object, - stateObj: Object, - }; - } - - computeTarget(hass, stateObj) { + private _computeTarget(hass: HomeAssistant, stateObj: HassEntity) { if (!hass || !stateObj) return null; // We're using "!= null" on purpose so that we match both null and undefined. @@ -85,5 +53,41 @@ class HaWaterHeaterState extends LocalizeMixin(PolymerElement) { _localizeState(stateObj) { return this.hass.formatEntityState(stateObj); } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + display: flex; + flex-direction: column; + justify-content: center; + white-space: nowrap; + } + + .target { + color: var(--primary-text-color); + } + + .current { + color: var(--secondary-text-color); + } + + .state-label { + font-weight: bold; + } + + .label { + direction: ltr; + display: inline-block; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-water_heater-state": HaWaterHeaterState; + } } -customElements.define("ha-water_heater-state", HaWaterHeaterState); diff --git a/src/dialogs/more-info/ha-more-info-info.ts b/src/dialogs/more-info/ha-more-info-info.ts index 1df9ef1401..ca05ec75d2 100644 --- a/src/dialogs/more-info/ha-more-info-info.ts +++ b/src/dialogs/more-info/ha-more-info-info.ts @@ -65,7 +65,7 @@ export class MoreInfoInfo extends LitElement { ? "" : html` diff --git a/src/dialogs/more-info/more-info-content.ts b/src/dialogs/more-info/more-info-content.ts index 3783851c91..171354def4 100644 --- a/src/dialogs/more-info/more-info-content.ts +++ b/src/dialogs/more-info/more-info-content.ts @@ -1,13 +1,14 @@ import { HassEntity } from "home-assistant-js-websocket"; -import { PropertyValues, ReactiveElement } from "lit"; -import { property } from "lit/decorators"; -import dynamicContentUpdater from "../../common/dom/dynamic_content_updater"; +import { LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; import { ExtEntityRegistryEntry } from "../../data/entity_registry"; import { importMoreInfoControl } from "../../panels/lovelace/custom-card-helpers"; import { HomeAssistant } from "../../types"; import { stateMoreInfoType } from "./state_more_info_control"; +import { dynamicElement } from "../../common/dom/dynamic-element-directive"; -class MoreInfoContent extends ReactiveElement { +@customElement("more-info-content") +class MoreInfoContent extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; @property({ attribute: false }) public stateObj?: HassEntity; @@ -16,62 +17,31 @@ class MoreInfoContent extends ReactiveElement { @property({ attribute: false }) public editMode?: boolean; - private _detachedChild?: ChildNode; - - protected createRenderRoot() { - return this; - } - - // This is not a lit element, but an updating element, so we implement update - protected update(changedProps: PropertyValues): void { - super.update(changedProps); - const stateObj = this.stateObj; - const entry = this.entry; - const hass = this.hass; - const editMode = this.editMode; - - if (!stateObj || !hass) { - if (this.lastChild) { - this._detachedChild = this.lastChild; - // Detach child to prevent it from doing work. - this.removeChild(this.lastChild); - } - return; - } - - if (this._detachedChild) { - this.appendChild(this._detachedChild); - this._detachedChild = undefined; - } - + protected render() { let moreInfoType: string | undefined; - if (stateObj.attributes && "custom_ui_more_info" in stateObj.attributes) { - moreInfoType = stateObj.attributes.custom_ui_more_info; + if (!this.stateObj || !this.hass) return nothing; + if ( + this.stateObj.attributes && + "custom_ui_more_info" in this.stateObj.attributes + ) { + moreInfoType = this.stateObj.attributes.custom_ui_more_info; } else { - const type = stateMoreInfoType(stateObj); + const type = stateMoreInfoType(this.stateObj); importMoreInfoControl(type); moreInfoType = type === "hidden" ? undefined : `more-info-${type}`; } - if (!moreInfoType) { - if (this.lastChild) { - this.removeChild(this.lastChild); - } - return; - } - - dynamicContentUpdater(this, moreInfoType.toUpperCase(), { - hass, - stateObj, - entry, - editMode, + if (!moreInfoType) return nothing; + return dynamicElement(moreInfoType, { + hass: this.hass, + stateObj: this.stateObj, + entry: this.entry, + editMode: this.editMode, }); } } -customElements.define("more-info-content", MoreInfoContent); - declare global { interface HTMLElementTagNameMap { "more-info-content": MoreInfoContent; diff --git a/src/state-summary/state-card-alert.ts b/src/state-summary/state-card-alert.ts index 70a4f1fd0a..2575bc0257 100755 --- a/src/state-summary/state-card-alert.ts +++ b/src/state-summary/state-card-alert.ts @@ -8,7 +8,7 @@ import { haStyle } from "../resources/styles"; import type { HomeAssistant } from "../types"; @customElement("state-card-alert") -export class StateCardAlert extends LitElement { +class StateCardAlert extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public stateObj!: HassEntity; @@ -78,3 +78,9 @@ export class StateCardAlert extends LitElement { ]; } } + +declare global { + interface HTMLElementTagNameMap { + "state-card-alert": StateCardAlert; + } +} diff --git a/src/state-summary/state-card-button.ts b/src/state-summary/state-card-button.ts index 3ce46e43ff..45b2d6a958 100644 --- a/src/state-summary/state-card-button.ts +++ b/src/state-summary/state-card-button.ts @@ -9,7 +9,7 @@ import { haStyle } from "../resources/styles"; import { HomeAssistant } from "../types"; @customElement("state-card-button") -export class StateCardButton extends LitElement { +class StateCardButton extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public stateObj!: HassEntity; diff --git a/src/state-summary/state-card-climate.js b/src/state-summary/state-card-climate.js deleted file mode 100644 index 16e2c17e6e..0000000000 --- a/src/state-summary/state-card-climate.js +++ /dev/null @@ -1,55 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import "../components/ha-climate-state"; - -class StateCardClimate extends PolymerElement { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } -} -customElements.define("state-card-climate", StateCardClimate); diff --git a/src/state-summary/state-card-climate.ts b/src/state-summary/state-card-climate.ts new file mode 100644 index 0000000000..828f422315 --- /dev/null +++ b/src/state-summary/state-card-climate.ts @@ -0,0 +1,54 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import "../components/entity/state-info"; +import "../components/ha-climate-state"; +import { customElement, property } from "lit/decorators"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-climate") +class StateCardClimate extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + return html` +
+ + +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + line-height: 1.5; + } + + ha-climate-state { + margin-left: 16px; + text-align: right; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-climate": StateCardClimate; + } +} diff --git a/src/state-summary/state-card-configurator.js b/src/state-summary/state-card-configurator.js deleted file mode 100644 index 4391445c00..0000000000 --- a/src/state-summary/state-card-configurator.js +++ /dev/null @@ -1,69 +0,0 @@ -import "@material/mwc-button"; -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { computeStateDisplay } from "../common/entity/compute_state_display"; -import "../components/entity/state-info"; -import LocalizeMixin from "../mixins/localize-mixin"; - -/* - * @appliesMixin LocalizeMixin - */ -class StateCardConfigurator extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - [[_localizeState(stateObj)]] -
- - - - `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } - - _localizeState(stateObj) { - return computeStateDisplay( - this.hass.localize, - stateObj, - this.hass.locale, - this.hass.entities - ); - } -} -customElements.define("state-card-configurator", StateCardConfigurator); diff --git a/src/state-summary/state-card-configurator.ts b/src/state-summary/state-card-configurator.ts new file mode 100644 index 0000000000..adc4ad23af --- /dev/null +++ b/src/state-summary/state-card-configurator.ts @@ -0,0 +1,59 @@ +import "@material/mwc-button"; +import { HassEntity } from "home-assistant-js-websocket"; +import "../components/entity/state-info"; +import { customElement, property } from "lit/decorators"; +import { + CSSResultGroup, + LitElement, + TemplateResult, + css, + html, + nothing, +} from "lit"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-configurator") +class StateCardConfigurator extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + return html` +
+ + ${this.inDialog + ? html`${this.hass.formatEntityState(this.stateObj)}` + : nothing} +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + mwc-button { + top: 3px; + height: 37px; + margin-right: -0.57em; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-configurator": StateCardConfigurator; + } +} diff --git a/src/state-summary/state-card-content.js b/src/state-summary/state-card-content.js deleted file mode 100644 index a0f3b450d6..0000000000 --- a/src/state-summary/state-card-content.js +++ /dev/null @@ -1,62 +0,0 @@ -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import dynamicContentUpdater from "../common/dom/dynamic_content_updater"; -import { stateCardType } from "../common/entity/state_card_type"; -import "./state-card-alert"; -import "./state-card-button"; -import "./state-card-climate"; -import "./state-card-configurator"; -import "./state-card-cover"; -import "./state-card-display"; -import "./state-card-event"; -import "./state-card-humidifier"; -import "./state-card-input_button"; -import "./state-card-input_number"; -import "./state-card-input_select"; -import "./state-card-input_text"; -import "./state-card-lawn_mower"; -import "./state-card-lock"; -import "./state-card-media_player"; -import "./state-card-number"; -import "./state-card-scene"; -import "./state-card-script"; -import "./state-card-select"; -import "./state-card-text"; -import "./state-card-timer"; -import "./state-card-toggle"; -import "./state-card-update"; -import "./state-card-vacuum"; -import "./state-card-water_heater"; - -class StateCardContent extends PolymerElement { - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } - - static get observers() { - return ["inputChanged(hass, inDialog, stateObj)"]; - } - - inputChanged(hass, inDialog, stateObj) { - let stateCard; - if (!stateObj || !hass) return; - if (stateObj.attributes && "custom_ui_state_card" in stateObj.attributes) { - stateCard = stateObj.attributes.custom_ui_state_card; - } else { - stateCard = "state-card-" + stateCardType(hass, stateObj); - } - dynamicContentUpdater(this, stateCard.toUpperCase(), { - hass: hass, - stateObj: stateObj, - inDialog: inDialog, - }); - } -} -customElements.define("state-card-content", StateCardContent); diff --git a/src/state-summary/state-card-content.ts b/src/state-summary/state-card-content.ts new file mode 100644 index 0000000000..433e2c071a --- /dev/null +++ b/src/state-summary/state-card-content.ts @@ -0,0 +1,65 @@ +import { LitElement, nothing } from "lit"; +import { HassEntity } from "home-assistant-js-websocket"; +import { customElement, property } from "lit/decorators"; +import { HomeAssistant } from "../types"; +import { dynamicElement } from "../common/dom/dynamic-element-directive"; +import { stateCardType } from "../common/entity/state_card_type"; +import "./state-card-alert"; +import "./state-card-button"; +import "./state-card-climate"; +import "./state-card-configurator"; +import "./state-card-cover"; +import "./state-card-display"; +import "./state-card-event"; +import "./state-card-humidifier"; +import "./state-card-input_button"; +import "./state-card-input_number"; +import "./state-card-input_select"; +import "./state-card-input_text"; +import "./state-card-lawn_mower"; +import "./state-card-lock"; +import "./state-card-media_player"; +import "./state-card-number"; +import "./state-card-scene"; +import "./state-card-script"; +import "./state-card-select"; +import "./state-card-text"; +import "./state-card-timer"; +import "./state-card-toggle"; +import "./state-card-update"; +import "./state-card-vacuum"; +import "./state-card-water_heater"; + +@customElement("state-card-content") +class StateCardContent extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render() { + let stateCard: string; + if (!this.stateObj || !this.hass) return nothing; + if ( + this.stateObj.attributes && + "custom_ui_state_card" in this.stateObj.attributes + ) { + stateCard = this.stateObj.attributes.custom_ui_state_card; + } else { + stateCard = "state-card-" + stateCardType(this.hass, this.stateObj); + } + + return dynamicElement(stateCard, { + hass: this.hass, + stateObj: this.stateObj, + inDialog: this.inDialog, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-content": StateCardContent; + } +} diff --git a/src/state-summary/state-card-display.ts b/src/state-summary/state-card-display.ts index 6f106a79b2..7263ab30bd 100755 --- a/src/state-summary/state-card-display.ts +++ b/src/state-summary/state-card-display.ts @@ -12,7 +12,7 @@ import { haStyle } from "../resources/styles"; import type { HomeAssistant } from "../types"; @customElement("state-card-display") -export class StateCardDisplay extends LitElement { +class StateCardDisplay extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public stateObj!: HassEntity; @@ -93,3 +93,9 @@ export class StateCardDisplay extends LitElement { ]; } } + +declare global { + interface HTMLElementTagNameMap { + "state-card-display": StateCardDisplay; + } +} diff --git a/src/state-summary/state-card-event.ts b/src/state-summary/state-card-event.ts index 7ec097a594..e80985d95a 100644 --- a/src/state-summary/state-card-event.ts +++ b/src/state-summary/state-card-event.ts @@ -7,7 +7,7 @@ import { haStyle } from "../resources/styles"; import { HomeAssistant } from "../types"; @customElement("state-card-event") -export class StateCardEvent extends LitElement { +class StateCardEvent extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public stateObj!: HassEntity; diff --git a/src/state-summary/state-card-humidifier.js b/src/state-summary/state-card-humidifier.js deleted file mode 100644 index a99b6a539d..0000000000 --- a/src/state-summary/state-card-humidifier.js +++ /dev/null @@ -1,55 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import "../components/ha-humidifier-state"; - -class StateCardHumidifier extends PolymerElement { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } -} -customElements.define("state-card-humidifier", StateCardHumidifier); diff --git a/src/state-summary/state-card-humidifier.ts b/src/state-summary/state-card-humidifier.ts new file mode 100644 index 0000000000..f1d24b94b7 --- /dev/null +++ b/src/state-summary/state-card-humidifier.ts @@ -0,0 +1,56 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; + +import "../components/entity/state-info"; +import "../components/ha-humidifier-state"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-humidifier") +class StateCardHumidifier extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + return html` +
+ + + +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + line-height: 1.5; + } + + ha-humidifier-state { + margin-left: 16px; + text-align: right; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-humidifier": StateCardHumidifier; + } +} diff --git a/src/state-summary/state-card-input_button.ts b/src/state-summary/state-card-input_button.ts index 9e6437bf4d..2de153cb54 100644 --- a/src/state-summary/state-card-input_button.ts +++ b/src/state-summary/state-card-input_button.ts @@ -9,7 +9,7 @@ import { haStyle } from "../resources/styles"; import { HomeAssistant } from "../types"; @customElement("state-card-input_button") -export class StateCardInputButton extends LitElement { +class StateCardInputButton extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public stateObj!: HassEntity; diff --git a/src/state-summary/state-card-input_text.js b/src/state-summary/state-card-input_text.js deleted file mode 100644 index 99abe1f2f3..0000000000 --- a/src/state-summary/state-card-input_text.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-plugin-disable lit */ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import "../components/ha-textfield"; - -class StateCardInputText extends PolymerElement { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - - inDialog: { - type: Boolean, - value: false, - }, - - stateObj: { - type: Object, - observer: "stateObjectChanged", - }, - - pattern: String, - value: String, - }; - } - - stateObjectChanged(newVal) { - this.value = newVal.state; - } - - onInput(ev) { - this.value = ev.target.value; - } - - selectedValueChanged() { - if (this.value === this.stateObj.state) { - return; - } - this.hass.callService("input_text", "set_value", { - value: this.value, - entity_id: this.stateObj.entity_id, - }); - } - - stopPropagation(ev) { - ev.stopPropagation(); - } -} - -customElements.define("state-card-input_text", StateCardInputText); diff --git a/src/state-summary/state-card-input_text.ts b/src/state-summary/state-card-input_text.ts new file mode 100644 index 0000000000..1a6e6ef451 --- /dev/null +++ b/src/state-summary/state-card-input_text.ts @@ -0,0 +1,89 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "../components/entity/state-info"; +import "../components/ha-textfield"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; +import { stopPropagation } from "../common/dom/stop_propagation"; + +@customElement("state-card-input_text") +class StateCardInputText extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + @state() public value: string = ""; + + protected render(): TemplateResult { + return html` +
+ + +
+ `; + } + + protected willUpdate(changedProp: PropertyValues): void { + super.willUpdate(changedProp); + if (changedProp.has("stateObj")) { + this.value = this.stateObj.state; + } + } + + private _onInput(ev) { + this.value = ev.target.value; + } + + private async _selectedValueChanged() { + if (this.value === this.stateObj.state) { + return; + } + await this.hass.callService("input_text", "set_value", { + value: this.value, + entity_id: this.stateObj.entity_id, + }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + ha-textfield { + margin-left: 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-input_text": StateCardInputText; + } +} diff --git a/src/state-summary/state-card-lock.js b/src/state-summary/state-card-lock.js deleted file mode 100644 index 94ee6e27aa..0000000000 --- a/src/state-summary/state-card-lock.js +++ /dev/null @@ -1,95 +0,0 @@ -import "@material/mwc-button"; -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { supportsFeature } from "../common/entity/supports-feature"; -import "../components/entity/state-info"; -import LocalizeMixin from "../mixins/localize-mixin"; -import { LockEntityFeature } from "../data/lock"; - -/* - * @appliesMixin LocalizeMixin - */ -class StateCardLock extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - [[localize('ui.card.lock.open')]] - [[localize('ui.card.lock.unlock')]] - [[localize('ui.card.lock.lock')]] -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: { - type: Object, - observer: "_stateObjChanged", - }, - inDialog: { - type: Boolean, - value: false, - }, - isLocked: Boolean, - supportsOpen: Boolean, - }; - } - - _stateObjChanged(newVal) { - if (newVal) { - this.isLocked = newVal.state === "locked"; - this.supportsOpen = supportsFeature(newVal, LockEntityFeature.OPEN); - } - } - - _callService(ev) { - ev.stopPropagation(); - const service = ev.target.dataset.service; - const data = { - entity_id: this.stateObj.entity_id, - }; - this.hass.callService("lock", service, data); - } -} -customElements.define("state-card-lock", StateCardLock); diff --git a/src/state-summary/state-card-lock.ts b/src/state-summary/state-card-lock.ts new file mode 100644 index 0000000000..bb19c6c64c --- /dev/null +++ b/src/state-summary/state-card-lock.ts @@ -0,0 +1,83 @@ +import "@material/mwc-button"; +import type { HassEntity } from "home-assistant-js-websocket"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + TemplateResult, +} from "lit"; +import { customElement, property } from "lit/decorators"; +import { supportsFeature } from "../common/entity/supports-feature"; +import "../components/entity/state-info"; +import { LockEntityFeature } from "../data/lock"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-lock") +class StateCardLock extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + const isLocked = this.stateObj.state === "locked"; + const supportsOpen = supportsFeature(this.stateObj, LockEntityFeature.OPEN); + + return html` +
+ + ${!supportsOpen + ? html`${this.hass.localize("ui.card.lock.open")}` + : nothing} + ${isLocked + ? html` ${this.hass.localize("ui.card.lock.unlock")}` + : nothing} + ${!isLocked + ? html`${this.hass.localize("ui.card.lock.lock")}` + : nothing} +
+ `; + } + + private async _callService(ev) { + ev.stopPropagation(); + const service = ev.target.dataset.service; + const data = { + entity_id: this.stateObj.entity_id, + }; + await this.hass.callService("lock", service, data); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + mwc-button { + top: 3px; + height: 37px; + margin-right: -0.57em; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-lock": StateCardLock; + } +} diff --git a/src/state-summary/state-card-media_player.js b/src/state-summary/state-card-media_player.js deleted file mode 100644 index 6f5e243639..0000000000 --- a/src/state-summary/state-card-media_player.js +++ /dev/null @@ -1,97 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { computeStateDisplay } from "../common/entity/compute_state_display"; -import "../components/entity/state-info"; -import LocalizeMixin from "../mixins/localize-mixin"; -import HassMediaPlayerEntity from "../util/hass-media-player-model"; - -/* - * @appliesMixin LocalizeMixin - */ -class StateCardMediaPlayer extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} -
-
- [[computePrimaryText(localize, playerObj)]] -
-
[[playerObj.secondaryTitle]]
-
-
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - playerObj: { - type: Object, - computed: "computePlayerObj(hass, stateObj)", - }, - }; - } - - computePlayerObj(hass, stateObj) { - return new HassMediaPlayerEntity(hass, stateObj); - } - - computePrimaryText(localize, playerObj) { - return ( - playerObj.primaryTitle || - computeStateDisplay( - localize, - playerObj.stateObj, - this.hass.locale, - this.hass.entities - ) - ); - } -} -customElements.define("state-card-media_player", StateCardMediaPlayer); diff --git a/src/state-summary/state-card-media_player.ts b/src/state-summary/state-card-media_player.ts new file mode 100644 index 0000000000..17d66084c5 --- /dev/null +++ b/src/state-summary/state-card-media_player.ts @@ -0,0 +1,83 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { computeStateDisplay } from "../common/entity/compute_state_display"; +import "../components/entity/state-info"; +import HassMediaPlayerEntity from "../util/hass-media-player-model"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-media_player") +class StateCardMediaPlayer extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + const playerObj = new HassMediaPlayerEntity(this.hass, this.stateObj); + return html` +
+ +
+
+ ${this._computePrimaryText(this.hass.localize, playerObj)} +
+
${playerObj.secondaryTitle}
+
+
+ `; + } + + private _computePrimaryText(localize, playerObj) { + return ( + playerObj.primaryTitle || + computeStateDisplay( + localize, + playerObj.stateObj, + this.hass.locale, + this.hass.config, + this.hass.entities + ) + ); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + line-height: 1.5; + } + + .state { + margin-left: 16px; + text-align: right; + } + + .main-text { + color: var(--primary-text-color); + } + + .main-text[take-height] { + line-height: 40px; + } + + .secondary-text { + color: var(--secondary-text-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-media_player": StateCardMediaPlayer; + } +} diff --git a/src/state-summary/state-card-number.js b/src/state-summary/state-card-number.js deleted file mode 100644 index dc59854da2..0000000000 --- a/src/state-summary/state-card-number.js +++ /dev/null @@ -1,201 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior"; -import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import "../components/ha-slider"; -import "../components/ha-textfield"; - -class StateCardNumber extends mixinBehaviors( - [IronResizableBehavior], - PolymerElement -) { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - - - - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - ready() { - super.ready(); - if (typeof ResizeObserver === "function") { - const ro = new ResizeObserver((entries) => { - entries.forEach(() => { - this.hiddenState(); - }); - }); - ro.observe(this.$.number_card); - } else { - this.addEventListener("iron-resize", () => this.hiddenState()); - } - } - - static get properties() { - return { - hass: Object, - hiddenbox: { - type: Boolean, - value: true, - }, - hiddenslider: { - type: Boolean, - value: true, - }, - inDialog: { - type: Boolean, - value: false, - }, - stateObj: { - type: Object, - observer: "stateObjectChanged", - }, - min: { - type: Number, - value: 0, - }, - max: { - type: Number, - value: 100, - }, - maxlength: { - type: Number, - value: 3, - }, - step: Number, - value: Number, - mode: String, - }; - } - - hiddenState() { - if (this.mode !== "slider") return; - const sliderwidth = this.$.slider.offsetWidth; - if (sliderwidth < 100) { - this.$.sliderstate.hidden = true; - } else if (sliderwidth >= 145) { - this.$.sliderstate.hidden = false; - } - } - - stateObjectChanged(newVal) { - const prevMode = this.mode; - const min = Number(newVal.attributes.min); - const max = Number(newVal.attributes.max); - const step = Number(newVal.attributes.step); - const range = (max - min) / step; - - this.setProperties({ - min: min, - max: max, - step: step, - value: Number(newVal.state), - mode: String(newVal.attributes.mode), - maxlength: String(newVal.attributes.max).length, - hiddenbox: - newVal.attributes.mode === "slider" || - (newVal.attributes.mode === "auto" && range <= 256), - hiddenslider: - newVal.attributes.mode === "box" || - (newVal.attributes.mode === "auto" && range > 256), - }); - if (this.mode === "slider" && prevMode !== "slider") { - this.hiddenState(); - } - } - - onInput(ev) { - this.value = ev.target.value; - } - - selectedValueChanged(ev) { - if (ev.target.value === Number(this.stateObj.state)) { - return; - } - this.hass.callService("number", "set_value", { - value: ev.target.value, - entity_id: this.stateObj.entity_id, - }); - } - - stopPropagation(ev) { - ev.stopPropagation(); - } -} - -customElements.define("state-card-number", StateCardNumber); diff --git a/src/state-summary/state-card-number.ts b/src/state-summary/state-card-number.ts new file mode 100644 index 0000000000..d07d3e45a0 --- /dev/null +++ b/src/state-summary/state-card-number.ts @@ -0,0 +1,168 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../components/entity/state-info"; +import "../components/ha-slider"; +import "../components/ha-textfield"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; +import { loadPolyfillIfNeeded } from "../resources/resize-observer.polyfill"; +import { computeRTLDirection } from "../common/util/compute_rtl"; +import { isUnavailableState } from "../data/entity"; +import { debounce } from "../common/util/debounce"; + +@customElement("state-card-number") +class StateCardNumber extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + private _loaded?: boolean; + + private _updated?: boolean; + + private _resizeObserver?: ResizeObserver; + + public connectedCallback(): void { + super.connectedCallback(); + if (this._updated && !this._loaded) { + this._initialLoad(); + } + this._attachObserver(); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + this._resizeObserver?.disconnect(); + } + + protected firstUpdated(): void { + this._updated = true; + if (this.isConnected && !this._loaded) { + this._initialLoad(); + } + this._attachObserver(); + } + + protected render() { + const range = this.stateObj.attributes.max - this.stateObj.attributes.min; + + return html` + + ${this.stateObj.attributes.mode === "slider" || + (this.stateObj.attributes.mode === "auto" && range <= 256) + ? html` +
+ + + + ${this.hass.formatEntityState(this.stateObj)} + +
+ ` + : html`
+ + +
`} + `; + } + + private async _initialLoad(): Promise { + this._loaded = true; + await this.updateComplete; + this._measureCard(); + } + + private _measureCard() { + if (!this.isConnected) { + return; + } + const element = this.shadowRoot!.querySelector(".state") as HTMLElement; + if (!element) { + return; + } + element.hidden = this.clientWidth <= 300; + } + + private async _attachObserver(): Promise { + if (!this._resizeObserver) { + await loadPolyfillIfNeeded(); + this._resizeObserver = new ResizeObserver( + debounce(() => this._measureCard(), 250, false) + ); + } + if (this.isConnected) { + this._resizeObserver.observe(this); + } + } + + private async _selectedValueChanged(ev: Event) { + const value = (ev.target as HTMLInputElement).value; + if (value === this.stateObj.state) { + return; + } + await this.hass.callService("number", "set_value", { + value: value, + entity_id: this.stateObj.entity_id, + }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + display: flex; + } + .flex { + display: flex; + align-items: center; + justify-content: flex-end; + flex-grow: 2; + } + .state { + min-width: 45px; + text-align: end; + } + ha-textfield { + text-align: end; + } + ha-slider { + width: 100%; + max-width: 200px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-number": StateCardNumber; + } +} diff --git a/src/state-summary/state-card-scene.js b/src/state-summary/state-card-scene.js deleted file mode 100644 index c7ece92453..0000000000 --- a/src/state-summary/state-card-scene.js +++ /dev/null @@ -1,60 +0,0 @@ -import "@material/mwc-button"; -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import { activateScene } from "../data/scene"; -import LocalizeMixin from "../mixins/localize-mixin"; - -/* - * @appliesMixin LocalizeMixin - */ -class StateCardScene extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - [[localize('ui.card.scene.activate')]] -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } - - _activateScene(ev) { - ev.stopPropagation(); - activateScene(this.hass, this.stateObj.entity_id); - } -} -customElements.define("state-card-scene", StateCardScene); diff --git a/src/state-summary/state-card-scene.ts b/src/state-summary/state-card-scene.ts new file mode 100644 index 0000000000..40e5f4522d --- /dev/null +++ b/src/state-summary/state-card-scene.ts @@ -0,0 +1,56 @@ +import "@material/mwc-button"; +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../components/entity/state-info"; +import { activateScene } from "../data/scene"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-scene") +class StateCardScene extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render() { + return html` +
+ + ${this.hass.localize("ui.card.scene.activate")} +
+ `; + } + + private _activateScene(ev) { + ev.stopPropagation(); + activateScene(this.hass, this.stateObj.entity_id); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + mwc-button { + top: 3px; + height: 37px; + margin-right: -0.57em; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-scene": StateCardScene; + } +} diff --git a/src/state-summary/state-card-script.ts b/src/state-summary/state-card-script.ts index e01573d5a1..2a0f65af82 100644 --- a/src/state-summary/state-card-script.ts +++ b/src/state-summary/state-card-script.ts @@ -10,7 +10,7 @@ import { haStyle } from "../resources/styles"; import { HomeAssistant } from "../types"; @customElement("state-card-script") -export class StateCardScript extends LitElement { +class StateCardScript extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public stateObj!: HassEntity; @@ -69,3 +69,9 @@ export class StateCardScript extends LitElement { return haStyle; } } + +declare global { + interface HTMLElementTagNameMap { + "state-card-script": StateCardScript; + } +} diff --git a/src/state-summary/state-card-timer.js b/src/state-summary/state-card-timer.js deleted file mode 100644 index a517a52da1..0000000000 --- a/src/state-summary/state-card-timer.js +++ /dev/null @@ -1,97 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import { computeDisplayTimer, timerTimeRemaining } from "../data/timer"; - -class StateCardTimer extends PolymerElement { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} -
[[_displayState(timeRemaining, stateObj)]]
-
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: { - type: Object, - observer: "stateObjChanged", - }, - timeRemaining: Number, - inDialog: { - type: Boolean, - value: false, - }, - }; - } - - connectedCallback() { - super.connectedCallback(); - this.startInterval(this.stateObj); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.clearInterval(); - } - - stateObjChanged(stateObj) { - this.startInterval(stateObj); - } - - clearInterval() { - if (this._updateRemaining) { - clearInterval(this._updateRemaining); - this._updateRemaining = null; - } - } - - startInterval(stateObj) { - this.clearInterval(); - this.calculateRemaining(stateObj); - - if (stateObj.state === "active") { - this._updateRemaining = setInterval( - () => this.calculateRemaining(this.stateObj), - 1000 - ); - } - } - - calculateRemaining(stateObj) { - this.timeRemaining = timerTimeRemaining(stateObj); - } - - _displayState(timeRemaining, stateObj) { - return computeDisplayTimer(this.hass, stateObj, timeRemaining); - } -} -customElements.define("state-card-timer", StateCardTimer); diff --git a/src/state-summary/state-card-timer.ts b/src/state-summary/state-card-timer.ts new file mode 100644 index 0000000000..2279d20cd8 --- /dev/null +++ b/src/state-summary/state-card-timer.ts @@ -0,0 +1,108 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property } from "lit/decorators"; +import "../components/entity/state-info"; +import { computeDisplayTimer, timerTimeRemaining } from "../data/timer"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-timer") +class StateCardTimer extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + @property() public timeRemaining?: number; + + private _updateRemaining: any; + + protected render(): TemplateResult { + return html` +
+ +
+ ${this._displayState(this.timeRemaining, this.stateObj)} +
+
+ `; + } + + connectedCallback() { + super.connectedCallback(); + this._startInterval(this.stateObj); + } + + disconnectedCallback() { + super.disconnectedCallback(); + this._clearInterval(); + } + + protected willUpdate(changedProp: PropertyValues): void { + super.willUpdate(changedProp); + if (changedProp.has("stateObj")) { + this._startInterval(this.stateObj); + } + } + + private _clearInterval() { + if (this._updateRemaining) { + clearInterval(this._updateRemaining); + this._updateRemaining = null; + } + } + + private _startInterval(stateObj) { + this._clearInterval(); + this._calculateRemaining(stateObj); + + if (stateObj.state === "active") { + this._updateRemaining = setInterval( + () => this._calculateRemaining(this.stateObj), + 1000 + ); + } + } + + private _calculateRemaining(stateObj) { + this.timeRemaining = timerTimeRemaining(stateObj); + } + + private _displayState(timeRemaining, stateObj) { + return computeDisplayTimer(this.hass, stateObj, timeRemaining); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .state { + color: var(--primary-text-color); + + margin-left: 16px; + text-align: right; + line-height: 40px; + white-space: nowrap; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-timer": StateCardTimer; + } +} diff --git a/src/state-summary/state-card-toggle.js b/src/state-summary/state-card-toggle.js deleted file mode 100644 index e7136c5776..0000000000 --- a/src/state-summary/state-card-toggle.js +++ /dev/null @@ -1,50 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/ha-entity-toggle"; -import "../components/entity/state-info"; - -class StateCardToggle extends PolymerElement { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } -} -customElements.define("state-card-toggle", StateCardToggle); diff --git a/src/state-summary/state-card-toggle.ts b/src/state-summary/state-card-toggle.ts new file mode 100644 index 0000000000..5972b85f0c --- /dev/null +++ b/src/state-summary/state-card-toggle.ts @@ -0,0 +1,51 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../components/entity/ha-entity-toggle"; +import "../components/entity/state-info"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-toggle") +class StateCardToggle extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + return html` +
+ + + +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + ha-entity-toggle { + margin: -4px -16px -4px 0; + padding: 4px 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-toggle": StateCardToggle; + } +} diff --git a/src/state-summary/state-card-vacuum.js b/src/state-summary/state-card-vacuum.js deleted file mode 100644 index 228ccef009..0000000000 --- a/src/state-summary/state-card-vacuum.js +++ /dev/null @@ -1,44 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import "../components/ha-vacuum-state"; - -class StateCardVacuum extends PolymerElement { - static get template() { - return html` - - -
- ${this.stateInfoTemplate} - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } -} -customElements.define("state-card-vacuum", StateCardVacuum); diff --git a/src/state-summary/state-card-vacuum.ts b/src/state-summary/state-card-vacuum.ts new file mode 100644 index 0000000000..293d27f1e7 --- /dev/null +++ b/src/state-summary/state-card-vacuum.ts @@ -0,0 +1,44 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../components/entity/state-info"; +import "../components/ha-vacuum-state"; +import { HomeAssistant } from "../types"; +import { haStyle } from "../resources/styles"; + +@customElement("state-card-vacuum") +class StateCardVacuum extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + return html` +
+ + + + +
+ `; + } + + static get styles(): CSSResultGroup { + return [haStyle]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-vacuum": StateCardVacuum; + } +} diff --git a/src/state-summary/state-card-water_heater.js b/src/state-summary/state-card-water_heater.js deleted file mode 100644 index 672972b9c3..0000000000 --- a/src/state-summary/state-card-water_heater.js +++ /dev/null @@ -1,55 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../components/entity/state-info"; -import "../components/ha-water_heater-state"; - -class StateCardWaterHeater extends PolymerElement { - static get template() { - return html` - - - -
- ${this.stateInfoTemplate} - -
- `; - } - - static get stateInfoTemplate() { - return html` - - `; - } - - static get properties() { - return { - hass: Object, - stateObj: Object, - inDialog: { - type: Boolean, - value: false, - }, - }; - } -} -customElements.define("state-card-water_heater", StateCardWaterHeater); diff --git a/src/state-summary/state-card-water_heater.ts b/src/state-summary/state-card-water_heater.ts new file mode 100644 index 0000000000..2ca34e5f2a --- /dev/null +++ b/src/state-summary/state-card-water_heater.ts @@ -0,0 +1,55 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../components/entity/state-info"; +import "../components/ha-water_heater-state"; +import { haStyle } from "../resources/styles"; +import { HomeAssistant } from "../types"; + +@customElement("state-card-water_heater") +class StateCardWaterHeater extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: HassEntity; + + @property({ type: Boolean }) public inDialog = false; + + protected render(): TemplateResult { + return html` +
+ + + +
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + line-height: 1.5; + } + + ha-water_heater-state { + margin-left: 16px; + text-align: right; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-water_heater": StateCardWaterHeater; + } +} diff --git a/src/util/hass-media-player-model.js b/src/util/hass-media-player-model.ts similarity index 93% rename from src/util/hass-media-player-model.js rename to src/util/hass-media-player-model.ts index fd71465750..f31a417e90 100644 --- a/src/util/hass-media-player-model.js +++ b/src/util/hass-media-player-model.ts @@ -1,12 +1,19 @@ +import { HassEntity } from "home-assistant-js-websocket"; import { supportsFeature } from "../common/entity/supports-feature"; import { cleanupMediaTitle } from "../data/media-player"; +import { HomeAssistant } from "../types"; export default class MediaPlayerEntity { - constructor(hass, stateObj) { + public hass: HomeAssistant; + + public stateObj: HassEntity; + + private _attr: { [key: string]: any }; + + constructor(hass: HomeAssistant, stateObj: HassEntity) { this.hass = hass; this.stateObj = stateObj; this._attr = stateObj.attributes; - this._feat = this._attr.supported_features; } get isOff() { @@ -219,7 +226,7 @@ export default class MediaPlayerEntity { // helper method - callService(service, data = {}) { + callService(service, data: any = {}) { data.entity_id = this.stateObj.entity_id; this.hass.callService("media_player", service, data); }