import { html, LitElement, PropertyDeclarations, PropertyValues, } from "@polymer/lit-element"; import { classMap } from "lit-html/directives/classMap"; import { TemplateResult } from "lit-html"; import { jQuery } from "../../../resources/jquery"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; import computeStateName from "../../../common/entity/compute_state_name"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { roundSliderStyle } from "../../../resources/jquery.roundslider"; import { HomeAssistant, ClimateEntity } from "../../../types"; import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; import { LovelaceCard, LovelaceConfig } from "../types"; import "../../../components/ha-card"; import "../../../components/ha-icon"; const thermostatConfig = { radius: 150, step: 1, circleShape: "pie", startAngle: 315, width: 5, lineCap: "round", handleSize: "+10", showTooltip: false, }; const modeIcons = { auto: "hass:autorenew", heat: "hass:fire", cool: "hass:snowflake", off: "hass:power", }; interface Config extends LovelaceConfig { entity: string; theme?: string; } function formatTemp(temps: string[]): string { return temps.filter(Boolean).join("-"); } export class HuiThermostatCard extends hassLocalizeLitMixin(LitElement) implements LovelaceCard { public hass?: HomeAssistant; private _config?: Config; static get properties(): PropertyDeclarations { return { hass: {}, _config: {}, }; } public getCardSize(): number { return 4; } public setConfig(config: Config): void { if (!config.entity || config.entity.split(".")[0] !== "climate") { throw new Error("Specify an entity from within the climate domain."); } this._config = { theme: "default", ...config }; } protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; } const stateObj = this.hass.states[this._config.entity] as ClimateEntity; const broadCard = this.clientWidth > 390; const mode = modeIcons[stateObj.attributes.operation_mode || ""] ? stateObj.attributes.operation_mode! : "unknown-mode"; return html` ${this.renderStyle()}
${computeStateName(stateObj)}
${ stateObj.attributes.current_temperature } ${ this.hass.config.unit_system.temperature }
${this.localize( `state.climate.${stateObj.state}` )}
${(stateObj.attributes.operation_list || []).map((modeItem) => this._renderIcon(modeItem, mode) )}
`; } protected shouldUpdate(changedProps: PropertyValues): boolean { return hasConfigOrEntityChanged(this, changedProps); } protected firstUpdated(): void { const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity; const _sliderType = stateObj.attributes.target_temp_low && stateObj.attributes.target_temp_high ? "range" : "min-range"; jQuery("#thermostat", this.shadowRoot).roundSlider({ ...thermostatConfig, radius: this.clientWidth / 3, min: stateObj.attributes.min_temp, max: stateObj.attributes.max_temp, sliderType: _sliderType, change: (value) => this._setTemperature(value), drag: (value) => this._dragEvent(value), }); } protected updated(changedProps: PropertyValues): void { if (!this._config || !this.hass) { return; } const stateObj = this.hass.states[this._config.entity] as ClimateEntity; let sliderValue; let uiValue; if ( stateObj.attributes.target_temp_low && stateObj.attributes.target_temp_high ) { sliderValue = `${stateObj.attributes.target_temp_low}, ${ stateObj.attributes.target_temp_high }`; uiValue = formatTemp([ String(stateObj.attributes.target_temp_low), String(stateObj.attributes.target_temp_high), ]); } else { sliderValue = uiValue = stateObj.attributes.temperature; } jQuery("#thermostat", this.shadowRoot).roundSlider({ value: sliderValue, }); this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = uiValue; const oldHass = changedProps.get("hass") as HomeAssistant | undefined; if (!oldHass || oldHass.themes !== this.hass.themes) { applyThemesOnElement(this, this.hass.themes, this._config.theme); } } private renderStyle(): TemplateResult { return html` ${roundSliderStyle} `; } private _dragEvent(e): void { this.shadowRoot!.querySelector("#set-temperature")!.innerHTML = formatTemp( String(e.value).split(",") ); } private _setTemperature(e): void { const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity; if ( stateObj.attributes.target_temp_low && stateObj.attributes.target_temp_high ) { if (e.handle.index === 1) { this.hass!.callService("climate", "set_temperature", { entity_id: this._config!.entity, target_temp_low: e.handle.value, target_temp_high: stateObj.attributes.target_temp_high, }); } else { this.hass!.callService("climate", "set_temperature", { entity_id: this._config!.entity, target_temp_low: stateObj.attributes.target_temp_low, target_temp_high: e.handle.value, }); } } else { this.hass!.callService("climate", "set_temperature", { entity_id: this._config!.entity, temperature: e.value, }); } } private _renderIcon(mode: string, currentMode: string): TemplateResult { if (!modeIcons[mode]) { return html``; } return html``; } private _handleModeClick(e: MouseEvent): void { this.hass!.callService("climate", "set_operation_mode", { entity_id: this._config!.entity, operation_mode: (e.currentTarget as any).mode, }); } } declare global { interface HTMLElementTagNameMap { "hui-thermostat-card": HuiThermostatCard; } } customElements.define("hui-thermostat-card", HuiThermostatCard);