diff --git a/src/components/entity/ha-state-label-badge.js b/src/components/entity/ha-state-label-badge.ts
similarity index 60%
rename from src/components/entity/ha-state-label-badge.js
rename to src/components/entity/ha-state-label-badge.ts
index bbd6f4dcac..c9308e48e6 100644
--- a/src/components/entity/ha-state-label-badge.js
+++ b/src/components/entity/ha-state-label-badge.ts
@@ -1,25 +1,206 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-
-import "../ha-label-badge";
+import {
+ LitElement,
+ html,
+ PropertyValues,
+ PropertyDeclarations,
+} from "@polymer/lit-element";
+import { TemplateResult } from "lit-html";
+import { HassEntity } from "home-assistant-js-websocket";
+import { classMap } from "lit-html/directives/classMap";
import computeStateDomain from "../../common/entity/compute_state_domain";
import computeStateName from "../../common/entity/compute_state_name";
import domainIcon from "../../common/entity/domain_icon";
import stateIcon from "../../common/entity/state_icon";
import timerTimeRemaining from "../../common/entity/timer_time_remaining";
-import attributeClassNames from "../../common/entity/attribute_class_names";
import secondsToDuration from "../../common/datetime/seconds_to_duration";
+import { fireEvent } from "../../common/dom/fire_event";
+import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
-import EventsMixin from "../../mixins/events-mixin";
-import LocalizeMixin from "../../mixins/localize-mixin";
+import "../ha-label-badge";
/*
* @appliesMixin LocalizeMixin
* @appliesMixin EventsMixin
*/
-class HaStateLabelBadge extends LocalizeMixin(EventsMixin(PolymerElement)) {
- static get template() {
+export class HaStateLabelBadge extends hassLocalizeLitMixin(LitElement) {
+ public state?: HassEntity;
+ private _connected?: boolean;
+ private _updateRemaining?: number;
+ private _timerTimeRemaining?: number;
+
+ public connectedCallback(): void {
+ super.connectedCallback();
+ this._connected = true;
+ this.startInterval(this.state);
+ }
+
+ public disconnectedCallback(): void {
+ super.disconnectedCallback();
+ this._connected = false;
+ this.clearInterval();
+ }
+
+ protected render(): TemplateResult {
+ const state = this.state;
+
+ if (!state) {
+ return html`
+ ${this.renderStyle()}
+
+ `;
+ }
+
+ const domain = computeStateDomain(state);
+
+ return html`
+ ${this.renderStyle()}
+
+ `;
+ }
+
+ static get properties(): PropertyDeclarations {
+ return {
+ hass: {},
+ state: {},
+ _timerTimeRemaining: {},
+ };
+ }
+
+ protected firstUpdated(changedProperties: PropertyValues): void {
+ super.firstUpdated(changedProperties);
+ this.addEventListener("click", (ev) => {
+ ev.stopPropagation();
+ fireEvent(this, "hass-more-info", { entityId: this.state!.entity_id });
+ });
+ }
+
+ protected updated(changedProperties: PropertyValues): void {
+ super.updated(changedProperties);
+
+ if (this._connected && changedProperties.has("state")) {
+ this.startInterval(this.state);
+ }
+ }
+
+ private _computeValue(domain: string, state: HassEntity) {
+ switch (domain) {
+ case "binary_sensor":
+ case "device_tracker":
+ case "updater":
+ case "sun":
+ case "alarm_control_panel":
+ case "timer":
+ return null;
+ case "sensor":
+ default:
+ return state.state === "unknown"
+ ? "-"
+ : this.localize(`component.${domain}.state.${state.state}`) ||
+ state.state;
+ }
+ }
+
+ private _computeIcon(domain: string, state: HassEntity) {
+ if (state.state === "unavailable") {
+ return null;
+ }
+ switch (domain) {
+ case "alarm_control_panel":
+ if (state.state === "pending") {
+ return "hass:clock-fast";
+ }
+ if (state.state === "armed_away") {
+ return "hass:nature";
+ }
+ if (state.state === "armed_home") {
+ return "hass:home-variant";
+ }
+ if (state.state === "armed_night") {
+ return "hass:weather-night";
+ }
+ if (state.state === "armed_custom_bypass") {
+ return "hass:security-home";
+ }
+ if (state.state === "triggered") {
+ return "hass:alert-circle";
+ }
+ // state == 'disarmed'
+ return domainIcon(domain, state.state);
+ case "binary_sensor":
+ case "device_tracker":
+ case "updater":
+ return stateIcon(state);
+ case "sun":
+ return state.state === "above_horizon"
+ ? domainIcon(domain)
+ : "hass:brightness-3";
+ case "timer":
+ return state.state === "active" ? "hass:timer" : "hass:timer-off";
+ default:
+ return null;
+ }
+ }
+
+ private _computeLabel(domain, state, _timerTimeRemaining) {
+ if (
+ state.state === "unavailable" ||
+ ["device_tracker", "alarm_control_panel"].includes(domain)
+ ) {
+ // Localize the state with a special state_badge namespace, which has variations of
+ // the state translations that are truncated to fit within the badge label. Translations
+ // are only added for device_tracker and alarm_control_panel.
+ return (
+ this.localize(`state_badge.${domain}.${state.state}`) ||
+ this.localize(`state_badge.default.${state.state}`) ||
+ state.state
+ );
+ }
+ if (domain === "timer") {
+ return secondsToDuration(_timerTimeRemaining);
+ }
+ return state.attributes.unit_of_measurement || null;
+ }
+
+ private clearInterval() {
+ if (this._updateRemaining) {
+ clearInterval(this._updateRemaining);
+ this._updateRemaining = undefined;
+ }
+ }
+
+ private startInterval(stateObj) {
+ this.clearInterval();
+ if (computeStateDomain(stateObj) === "timer") {
+ this.calculateTimerRemaining(stateObj);
+
+ if (stateObj.state === "active") {
+ this._updateRemaining = window.setInterval(
+ () => this.calculateTimerRemaining(this.state),
+ 1000
+ );
+ }
+ }
+ }
+
+ private calculateTimerRemaining(stateObj) {
+ this._timerTimeRemaining = timerTimeRemaining(stateObj);
+ }
+
+ private renderStyle(): TemplateResult {
return html`
-
-
`;
}
+}
- static get properties() {
- return {
- hass: Object,
- state: {
- type: Object,
- observer: "stateChanged",
- },
- _timerTimeRemaining: {
- type: Number,
- value: 0,
- },
- };
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.startInterval(this.state);
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- this.clearInterval();
- }
-
- ready() {
- super.ready();
- this.addEventListener("click", (ev) => this.badgeTap(ev));
- }
-
- badgeTap(ev) {
- ev.stopPropagation();
- this.fire("hass-more-info", { entityId: this.state.entity_id });
- }
-
- computeClassNames(state) {
- const classes = [computeStateDomain(state)];
- classes.push(attributeClassNames(state, ["unit_of_measurement"]));
- return classes.join(" ");
- }
-
- computeValue(localize, state) {
- const domain = computeStateDomain(state);
- switch (domain) {
- case "binary_sensor":
- case "device_tracker":
- case "updater":
- case "sun":
- case "alarm_control_panel":
- case "timer":
- return null;
- case "sensor":
- default:
- return state.state === "unknown"
- ? "-"
- : localize(`component.${domain}.state.${state.state}`) || state.state;
- }
- }
-
- computeIcon(state) {
- if (state.state === "unavailable") {
- return null;
- }
- const domain = computeStateDomain(state);
- switch (domain) {
- case "alarm_control_panel":
- if (state.state === "pending") {
- return "hass:clock-fast";
- }
- if (state.state === "armed_away") {
- return "hass:nature";
- }
- if (state.state === "armed_home") {
- return "hass:home-variant";
- }
- if (state.state === "armed_night") {
- return "hass:weather-night";
- }
- if (state.state === "armed_custom_bypass") {
- return "hass:security-home";
- }
- if (state.state === "triggered") {
- return "hass:alert-circle";
- }
- // state == 'disarmed'
- return domainIcon(domain, state.state);
- case "binary_sensor":
- case "device_tracker":
- case "updater":
- return stateIcon(state);
- case "sun":
- return state.state === "above_horizon"
- ? domainIcon(domain)
- : "hass:brightness-3";
- case "timer":
- return state.state === "active" ? "hass:timer" : "hass:timer-off";
- default:
- return null;
- }
- }
-
- computeImage(state) {
- return state.attributes.entity_picture || null;
- }
-
- computeLabel(localize, state, _timerTimeRemaining) {
- const domain = computeStateDomain(state);
- if (
- state.state === "unavailable" ||
- ["device_tracker", "alarm_control_panel"].includes(domain)
- ) {
- // Localize the state with a special state_badge namespace, which has variations of
- // the state translations that are truncated to fit within the badge label. Translations
- // are only added for device_tracker and alarm_control_panel.
- return (
- localize(`state_badge.${domain}.${state.state}`) ||
- localize(`state_badge.default.${state.state}`) ||
- state.state
- );
- }
- if (domain === "timer") {
- return secondsToDuration(_timerTimeRemaining);
- }
- return state.attributes.unit_of_measurement || null;
- }
-
- computeDescription(state) {
- return computeStateName(state);
- }
-
- stateChanged(stateObj) {
- this.updateStyles();
- this.startInterval(stateObj);
- }
-
- clearInterval() {
- if (this._updateRemaining) {
- clearInterval(this._updateRemaining);
- this._updateRemaining = null;
- }
- }
-
- startInterval(stateObj) {
- this.clearInterval();
- if (computeStateDomain(stateObj) === "timer") {
- this.calculateTimerRemaining(stateObj);
-
- if (stateObj.state === "active") {
- this._updateRemaining = setInterval(
- () => this.calculateTimerRemaining(this.state),
- 1000
- );
- }
- }
- }
-
- calculateTimerRemaining(stateObj) {
- this._timerTimeRemaining = timerTimeRemaining(stateObj);
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-state-label-badge": HaStateLabelBadge;
}
}
diff --git a/src/mixins/localize-base-mixin.ts b/src/mixins/localize-base-mixin.ts
index f7084902e8..a70821e6be 100644
--- a/src/mixins/localize-base-mixin.ts
+++ b/src/mixins/localize-base-mixin.ts
@@ -1,4 +1,5 @@
import IntlMessageFormat from "intl-messageformat/src/main";
+import { HomeAssistant } from "../types";
/**
* Adapted from Polymer app-localize-behavior.
@@ -32,6 +33,7 @@ export interface FormatsType {
export type LocalizeFunc = (key: string, ...args: any[]) => string;
export interface LocalizeMixin {
+ hass?: HomeAssistant;
localize: LocalizeFunc;
}
diff --git a/src/panels/lovelace/components/hui-theme-select-editor.ts b/src/panels/lovelace/components/hui-theme-select-editor.ts
index 206919b213..68c452c3d2 100644
--- a/src/panels/lovelace/components/hui-theme-select-editor.ts
+++ b/src/panels/lovelace/components/hui-theme-select-editor.ts
@@ -19,7 +19,7 @@ declare global {
export class HuiThemeSelectionEditor extends hassLocalizeLitMixin(LitElement) {
public value?: string;
- protected hass?: HomeAssistant;
+ public hass?: HomeAssistant;
static get properties(): PropertyDeclarations {
return {
diff --git a/src/panels/lovelace/editor/hui-dialog-save-config.ts b/src/panels/lovelace/editor/hui-dialog-save-config.ts
index 7e00454cfa..295d9ae87c 100644
--- a/src/panels/lovelace/editor/hui-dialog-save-config.ts
+++ b/src/panels/lovelace/editor/hui-dialog-save-config.ts
@@ -14,7 +14,7 @@ import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
import { SaveDialogParams } from "./show-save-config-dialog";
export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
- protected hass?: HomeAssistant;
+ public hass?: HomeAssistant;
private _params?: SaveDialogParams;
private _saving: boolean;
diff --git a/src/panels/lovelace/editor/view-editor/hui-edit-view.ts b/src/panels/lovelace/editor/view-editor/hui-edit-view.ts
index 733ec46a39..8b27068812 100644
--- a/src/panels/lovelace/editor/view-editor/hui-edit-view.ts
+++ b/src/panels/lovelace/editor/view-editor/hui-edit-view.ts
@@ -30,7 +30,7 @@ import { deleteView, addView, replaceView } from "../config-util";
export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
public lovelace?: Lovelace;
public viewIndex?: number;
- protected hass?: HomeAssistant;
+ public hass?: HomeAssistant;
private _config?: LovelaceViewConfig;
private _badges?: EntityConfig[];
private _cards?: LovelaceCardConfig[];
diff --git a/src/panels/lovelace/hui-view.ts b/src/panels/lovelace/hui-view.ts
index 52093225a1..8fc3cd6d01 100644
--- a/src/panels/lovelace/hui-view.ts
+++ b/src/panels/lovelace/hui-view.ts
@@ -5,9 +5,11 @@ import {
PropertyDeclarations,
} from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
-import { PolymerElement } from "@polymer/polymer";
import "../../components/entity/ha-state-label-badge";
+// This one is for types
+// tslint:disable-next-line
+import { HaStateLabelBadge } from "../../components/entity/ha-state-label-badge";
import applyThemesOnElement from "../../common/dom/apply_themes_on_element";
@@ -28,7 +30,7 @@ export class HUIView extends hassLocalizeLitMixin(LitElement) {
public columns?: number;
public index?: number;
private _cards: LovelaceCard[];
- private _badges: Array<{ element: PolymerElement; entityId: string }>;
+ private _badges: Array<{ element: HaStateLabelBadge; entityId: string }>;
static get properties(): PropertyDeclarations {
return {
@@ -158,10 +160,8 @@ export class HUIView extends hassLocalizeLitMixin(LitElement) {
} else if (changedProperties.has("hass")) {
this._badges.forEach((badge) => {
const { element, entityId } = badge;
- element.setProperties({
- hass: this.hass,
- state: this.hass!.states[entityId],
- });
+ element.hass = this.hass!;
+ element.state = this.hass!.states[entityId];
});
}
@@ -196,17 +196,9 @@ export class HUIView extends hassLocalizeLitMixin(LitElement) {
const elements: HUIView["_badges"] = [];
for (const entityId of config.badges) {
- if (!(entityId in this.hass!.states)) {
- continue;
- }
-
- const element = document.createElement(
- "ha-state-label-badge"
- ) as PolymerElement;
- element.setProperties({
- hass: this.hass,
- state: this.hass!.states[entityId],
- });
+ const element = document.createElement("ha-state-label-badge");
+ element.hass = this.hass;
+ element.state = this.hass!.states[entityId];
elements.push({ element, entityId });
root.appendChild(element);
}