diff --git a/src/components/ha-climate-state.js b/src/components/ha-climate-state.js
deleted file mode 100644
index eb961cce4c..0000000000
--- a/src/components/ha-climate-state.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-/* eslint-plugin-disable lit */
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-import { CLIMATE_PRESET_NONE } from "../data/climate";
-import LocalizeMixin from "../mixins/localize-mixin";
-
-/*
- * @appliesMixin LocalizeMixin
- */
-class HaClimateState extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
-
-
-
-
-
- [[_localizeState(localize, stateObj)]]
-
- - [[_localizePreset(localize, stateObj.attributes.preset_mode)]]
-
-
-
-
[[computeTarget(hass, stateObj)]]
-
-
-
-
- [[localize('ui.card.climate.currently')]]:
-
[[currentStatus]]
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- stateObj: Object,
- currentStatus: {
- type: String,
- computed: "computeCurrentStatus(hass, stateObj)",
- },
- };
- }
-
- computeCurrentStatus(hass, stateObj) {
- if (!hass || !stateObj) return null;
- if (stateObj.attributes.current_temperature != null) {
- return `${stateObj.attributes.current_temperature} ${hass.config.unit_system.temperature}`;
- }
- if (stateObj.attributes.current_humidity != null) {
- return `${stateObj.attributes.current_humidity} %`;
- }
- return null;
- }
-
- computeTarget(hass, stateObj) {
- if (!hass || !stateObj) return null;
- // We're using "!= null" on purpose so that we match both null and undefined.
- if (
- stateObj.attributes.target_temp_low != null &&
- stateObj.attributes.target_temp_high != null
- ) {
- return `${stateObj.attributes.target_temp_low}-${stateObj.attributes.target_temp_high} ${hass.config.unit_system.temperature}`;
- }
- if (stateObj.attributes.temperature != null) {
- return `${stateObj.attributes.temperature} ${hass.config.unit_system.temperature}`;
- }
- if (
- stateObj.attributes.target_humidity_low != null &&
- stateObj.attributes.target_humidity_high != null
- ) {
- return `${stateObj.attributes.target_humidity_low}-${stateObj.attributes.target_humidity_high}%`;
- }
- if (stateObj.attributes.humidity != null) {
- return `${stateObj.attributes.humidity} %`;
- }
-
- return "";
- }
-
- _hasKnownState(state) {
- return state !== "unknown";
- }
-
- _localizeState(localize, stateObj) {
- const stateString = localize(`component.climate.state._.${stateObj.state}`);
- return stateObj.attributes.hvac_action
- ? `${localize(
- `state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
- )} (${stateString})`
- : stateString;
- }
-
- _localizePreset(localize, preset) {
- return localize(`state_attributes.climate.preset_mode.${preset}`) || preset;
- }
-
- _renderPreset(attributes) {
- return (
- attributes.preset_mode && attributes.preset_mode !== CLIMATE_PRESET_NONE
- );
- }
-}
-customElements.define("ha-climate-state", HaClimateState);
diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts
new file mode 100644
index 0000000000..39ece5ee43
--- /dev/null
+++ b/src/components/ha-climate-state.ts
@@ -0,0 +1,139 @@
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+import { HassEntity } from "home-assistant-js-websocket";
+
+import { CLIMATE_PRESET_NONE } from "../data/climate";
+import type { HomeAssistant } from "../types";
+
+@customElement("ha-climate-state")
+class HaClimateState extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public stateObj!: HassEntity;
+
+ protected render(): TemplateResult {
+ const currentStatus = this._computeCurrentStatus();
+
+ return html`
+ ${this.stateObj.state !== "unknown"
+ ? html`
+ ${this._localizeState()}
+ ${this.stateObj.attributes.preset_mode &&
+ this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
+ ? html`-
+ ${this.hass.localize(
+ `state_attributes.climate.preset_mode.${this.stateObj.attributes.preset_mode}`
+ ) || this.stateObj.attributes.preset_mode}`
+ : ""}
+ `
+ : ""}
+
${this._computeTarget()}
+
+
+ ${currentStatus
+ ? html`
+ ${this.hass.localize("ui.card.climate.currently")}:
+
${currentStatus}
+
`
+ : ""}`;
+ }
+
+ private _computeCurrentStatus(): string | undefined {
+ if (!this.hass || !this.stateObj) {
+ return undefined;
+ }
+
+ if (this.stateObj.attributes.current_temperature != null) {
+ return `${this.stateObj.attributes.current_temperature} ${this.hass.config.unit_system.temperature}`;
+ }
+
+ if (this.stateObj.attributes.current_humidity != null) {
+ return `${this.stateObj.attributes.current_humidity} %`;
+ }
+
+ return undefined;
+ }
+
+ private _computeTarget(): string {
+ if (!this.hass || !this.stateObj) {
+ return "";
+ }
+
+ if (
+ this.stateObj.attributes.target_temp_low != null &&
+ this.stateObj.attributes.target_temp_high != null
+ ) {
+ return `${this.stateObj.attributes.target_temp_low}-${this.stateObj.attributes.target_temp_high} ${this.hass.config.unit_system.temperature}`;
+ }
+
+ if (this.stateObj.attributes.temperature != null) {
+ return `${this.stateObj.attributes.temperature} ${this.hass.config.unit_system.temperature}`;
+ }
+ if (
+ this.stateObj.attributes.target_humidity_low != null &&
+ this.stateObj.attributes.target_humidity_high != null
+ ) {
+ return `${this.stateObj.attributes.target_humidity_low}-${this.stateObj.attributes.target_humidity_high}%`;
+ }
+
+ if (this.stateObj.attributes.humidity != null) {
+ return `${this.stateObj.attributes.humidity} %`;
+ }
+
+ return "";
+ }
+
+ private _localizeState(): string {
+ const stateString = this.hass.localize(
+ `component.climate.state._.${this.stateObj.state}`
+ );
+
+ return this.stateObj.attributes.hvac_action
+ ? `${this.hass.localize(
+ `state_attributes.climate.hvac_action.${this.stateObj.attributes.hvac_action}`
+ )} (${stateString})`
+ : stateString;
+ }
+
+ static get styles(): CSSResult {
+ return 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;
+ text-transform: capitalize;
+ }
+
+ .unit {
+ display: inline-block;
+ direction: ltr;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-climate-state": HaClimateState;
+ }
+}