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)}
-
-
-
- [[localize('ui.card.water_heater.currently')]]: [[currentStatus]]
-
-
`;
}
- 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}
-
-
-
-
-
- [[stateObj.attributes.unit_of_measurement]]
-
-
- [[value]] [[stateObj.attributes.unit_of_measurement]]
-
-
- `;
- }
-
- 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);
}