import type { CSSResultGroup, PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { ifDefined } from "lit/directives/if-defined"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { isUnavailableState } from "../../../data/entity"; import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { ForecastEvent, WeatherEntity } from "../../../data/weather"; import { getDefaultForecastType, getForecast, getSecondaryWeatherAttribute, getWeatherStateIcon, subscribeForecast, weatherSVGStyles, } from "../../../data/weather"; import type { HomeAssistant } from "../../../types"; import type { EntitiesCardEntityConfig } from "../cards/types"; import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction, hasAnyAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { LovelaceRow } from "./types"; @customElement("hui-weather-entity-row") class HuiWeatherEntityRow extends LitElement implements LovelaceRow { @property({ attribute: false }) public hass?: HomeAssistant; @state() private _config?: EntitiesCardEntityConfig; @state() private _forecastEvent?: ForecastEvent; @state() private _subscribed?: Promise<() => void>; private _unsubscribeForecastEvents() { if (this._subscribed) { this._subscribed.then((unsub) => unsub()); this._subscribed = undefined; } } private async _subscribeForecastEvents() { this._unsubscribeForecastEvents(); if (!this.hass || !this._config || !this.isConnected) { return; } const stateObj = this.hass!.states[this._config!.entity]; const forecastType = getDefaultForecastType(stateObj); if (forecastType) { this._subscribed = subscribeForecast( this.hass!, stateObj.entity_id, forecastType, (event) => { this._forecastEvent = event; } ); } } public connectedCallback() { super.connectedCallback(); if (this.hasUpdated) { this._subscribeForecastEvents(); } } public disconnectedCallback(): void { super.disconnectedCallback(); this._unsubscribeForecastEvents(); } public setConfig(config: EntitiesCardEntityConfig): void { if (!config?.entity) { throw new Error("Entity must be specified"); } this._config = config; } protected shouldUpdate(changedProps: PropertyValues): boolean { return ( hasConfigOrEntityChanged(this, changedProps) || changedProps.size > 1 || !changedProps.has("hass") ); } protected updated(changedProps: PropertyValues): void { super.updated(changedProps); if (changedProps.has("_config") || !this._subscribed) { this._subscribeForecastEvents(); } } protected render() { if (!this.hass || !this._config) { return nothing; } const stateObj = this.hass.states[this._config.entity] as WeatherEntity; if (!stateObj) { return html` ${createEntityNotFoundWarning(this.hass, this._config.entity)} `; } const pointer = hasAnyAction(this._config); const hasSecondary = this._config.secondary_info; const weatherStateIcon = getWeatherStateIcon(stateObj.state, this); const forecastData = getForecast(stateObj.attributes, this._forecastEvent); const forecast = forecastData?.forecast; return html`
${weatherStateIcon || html` `}
${this._config.name || computeStateName(stateObj)} ${hasSecondary ? html`
${this._config.secondary_info === "entity-id" ? stateObj.entity_id : this._config.secondary_info === "last-changed" ? html` ` : this._config.secondary_info === "last-updated" ? html` ` : ""}
` : ""}
${isUnavailableState(stateObj.state) || stateObj.attributes.temperature === undefined || stateObj.attributes.temperature === null ? this.hass.formatEntityState(stateObj) : this.hass.formatEntityAttributeValue(stateObj, "temperature")}
${getSecondaryWeatherAttribute(this.hass!, stateObj, forecast!)}
`; } private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); } static get styles(): CSSResultGroup { return [ weatherSVGStyles, css` :host { display: flex; align-items: center; flex-direction: row; } .info { margin-left: 16px; margin-inline-start: 16px; margin-inline-end: initial; flex: 1 0 60px; } .info, .info > * { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .icon-image { display: flex; align-items: center; min-width: 40px; } .icon-image > * { flex: 0 0 40px; height: 40px; } .icon-image:focus { outline: none; background-color: var(--divider-color); border-radius: var(--ha-border-radius-circle); } .weather-icon { --mdc-icon-size: 40px; } .pointer { cursor: pointer; } .attributes { display: flex; flex-direction: column; justify-content: center; text-align: right; margin-left: 8px; margin-inline-start: 8px; margin-inline-end: initial; } .secondary { color: var(--secondary-text-color); } `, ]; } } declare global { interface HTMLElementTagNameMap { "hui-weather-entity-row": HuiWeatherEntityRow; } }