diff --git a/public/static/images/weather/cloudy.png b/public/static/images/weather/cloudy.png deleted file mode 100644 index 1277a7ad67..0000000000 Binary files a/public/static/images/weather/cloudy.png and /dev/null differ diff --git a/public/static/images/weather/lightning-rainy.png b/public/static/images/weather/lightning-rainy.png deleted file mode 100644 index 07f78b6c6f..0000000000 Binary files a/public/static/images/weather/lightning-rainy.png and /dev/null differ diff --git a/public/static/images/weather/lightning.png b/public/static/images/weather/lightning.png deleted file mode 100644 index a6aaed9a0c..0000000000 Binary files a/public/static/images/weather/lightning.png and /dev/null differ diff --git a/public/static/images/weather/night.png b/public/static/images/weather/night.png deleted file mode 100644 index 66f934624d..0000000000 Binary files a/public/static/images/weather/night.png and /dev/null differ diff --git a/public/static/images/weather/partly-cloudy.png b/public/static/images/weather/partly-cloudy.png deleted file mode 100644 index 6c59f6cc93..0000000000 Binary files a/public/static/images/weather/partly-cloudy.png and /dev/null differ diff --git a/public/static/images/weather/pouring.png b/public/static/images/weather/pouring.png deleted file mode 100644 index 487d5ac827..0000000000 Binary files a/public/static/images/weather/pouring.png and /dev/null differ diff --git a/public/static/images/weather/rainy.png b/public/static/images/weather/rainy.png deleted file mode 100644 index 0d429b11b0..0000000000 Binary files a/public/static/images/weather/rainy.png and /dev/null differ diff --git a/public/static/images/weather/snowy.png b/public/static/images/weather/snowy.png deleted file mode 100644 index e28b6e8274..0000000000 Binary files a/public/static/images/weather/snowy.png and /dev/null differ diff --git a/public/static/images/weather/sunny.png b/public/static/images/weather/sunny.png deleted file mode 100644 index 42016dfb46..0000000000 Binary files a/public/static/images/weather/sunny.png and /dev/null differ diff --git a/public/static/images/weather/windy.png b/public/static/images/weather/windy.png deleted file mode 100644 index 6db0122923..0000000000 Binary files a/public/static/images/weather/windy.png and /dev/null differ diff --git a/src/data/weather.ts b/src/data/weather.ts index 4fe0919191..199a5e68e9 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -1,26 +1,52 @@ -import { HomeAssistant, WeatherEntity } from "../types"; +import { SVGTemplateResult, svg, html, TemplateResult, css } from "lit-element"; +import { styleMap } from "lit-html/directives/style-map"; -export const weatherImages = { - "clear-night": "/static/images/weather/night.png", - cloudy: "/static/images/weather/cloudy.png", - fog: "/static/images/weather/cloudy.png", - lightning: "/static/images/weather/lightning.png", - "lightning-rainy": "/static/images/weather/lightning-rainy.png", - partlycloudy: "/static/images/weather/partly-cloudy.png", - pouring: "/static/images/weather/pouring.png", - rainy: "/static/images/weather/rainy.png", - hail: "/static/images/weather/rainy.png", - snowy: "/static/images/weather/snowy.png", - "snowy-rainy": "/static/images/weather/snowy.png", - sunny: "/static/images/weather/sunny.png", - windy: "/static/images/weather/windy.png", - "windy-variant": "/static/images/weather/windy.png", -}; +import type { HomeAssistant, WeatherEntity } from "../types"; + +export const weatherSVGs = new Set([ + "clear-night", + "cloudy", + "fog", + "lightning", + "lightning-rainy", + "partlycloudy", + "pouring", + "rainy", + "hail", + "snowy", + "snowy-rainy", + "sunny", + "windy", + "windy-variant", +]); export const weatherIcons = { exceptional: "hass:alert-circle-outline", }; +const cloudyStates = new Set([ + "partlycloudy", + "cloudy", + "fog", + "windy", + "windy-variant", + "hail", + "rainy", + "snowy", + "snowy-rainy", + "pouring", + "lightning", + "lightning-rainy", +]); + +const rainStates = new Set(["hail", "rainy", "pouring"]); + +const windyStates = new Set(["windy", "windy-variant"]); + +const snowyStates = new Set(["snowy", "snowy-rainy"]); + +const lightningStates = new Set(["lightning", "lightning-rainy"]); + export const cardinalDirections = [ "N", "NNE", @@ -164,3 +190,183 @@ const getWeatherExtrema = ( } `; }; + +export const weatherSVGStyles = css` + .rain { + fill: var(--weather-icon-rain-color, #30b3ff); + } + .sun { + fill: var(--weather-icon-sun-color, #fdd93c); + } + .moon { + fill: var(--weather-icon-moon-color, #fdf9cc); + } + .cloud-back { + fill: var(--weather-icon-cloud-back-color, #d4d4d4); + } + .cloud-front { + fill: var(--weather-icon-cloud-front-color, #f9f9f9); + } +`; + +export const getWeatherStateSVG = (state: string): SVGTemplateResult => { + return svg` + + ${ + state === "sunny" + ? svg` + + ` + : "" + } + ${ + state === "clear-night" + ? svg` + + ` + : "" + } + ${ + state === "partlycloudy" + ? svg` + + ` + : "" + } + ${ + cloudyStates.has(state) + ? svg` + + + ` + : "" + } + ${ + rainStates.has(state) + ? svg` + + + + + ` + : "" + } + ${ + state === "pouring" + ? svg` + + + ` + : "" + } + ${ + windyStates.has(state) + ? svg` + + + ` + : "" + } + ${ + snowyStates.has(state) + ? svg` + + + + ` + : "" + } + ${ + lightningStates.has(state) + ? svg` + + ` + : "" + } + `; +}; + +export const getWeatherStateIcon = ( + state: string, + element: HTMLElement +): TemplateResult | undefined => { + const userDefinedIcon = getComputedStyle(element).getPropertyValue( + `--weather-icon-${state}` + ); + + if (userDefinedIcon) { + return html` +
+ `; + } + + if (weatherSVGs.has(state)) { + return html`${getWeatherStateSVG(state)}`; + } + + if (state in weatherIcons) { + return html` + + `; + } + + return undefined; +}; diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts index 8fa9356417..3b5f7bc377 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts @@ -21,16 +21,16 @@ import { UNAVAILABLE } from "../../../data/entity"; import { getSecondaryWeatherAttribute, getWeatherUnit, - weatherIcons, - weatherImages, + getWeatherStateIcon, + weatherSVGStyles, } from "../../../data/weather"; -import { HomeAssistant, WeatherEntity } from "../../../types"; +import type { HomeAssistant, WeatherEntity } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entites"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-warning"; -import { LovelaceCard, LovelaceCardEditor } from "../types"; -import { WeatherForecastCardConfig } from "./types"; +import type { LovelaceCard, LovelaceCardEditor } from "../types"; +import type { WeatherForecastCardConfig } from "./types"; import { installResizeObserver } from "../common/install-resize-observer"; const DAY_IN_MILLISECONDS = 86400000; @@ -165,6 +165,8 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { hourly = timeDiff < DAY_IN_MILLISECONDS; } + const weatherStateIcon = getWeatherStateIcon(stateObj.state, this); + return html`
- ${stateObj.state in weatherImages - ? html` - - ` - : html` - - `} + ${weatherStateIcon || + html` + + `}
@@ -251,21 +247,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { ${item.condition !== undefined && item.condition !== null ? html`
- ${item.condition in weatherImages - ? html` - - ` - : item.condition in weatherIcons - ? html` - - ` - : ""} + ${getWeatherStateIcon(item.condition, this)}
` : ""} @@ -334,201 +316,205 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { } } - static get styles(): CSSResult { - return css` - :host { - display: block; - } + static get styles(): CSSResult[] { + return [ + weatherSVGStyles, + css` + :host { + display: block; + } - ha-card { - cursor: pointer; - padding: 16px; - } + ha-card { + cursor: pointer; + padding: 16px; + } - .content { - display: flex; - flex-wrap: nowrap; - justify-content: space-between; - align-items: center; - } + .content { + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + align-items: center; + } - .icon-image { - display: flex; - align-items: center; - min-width: 64px; - margin-right: 16px; - } + .icon-image { + display: flex; + align-items: center; + min-width: 64px; + margin-right: 16px; + } - .weather-image, - .weather-icon { - flex: 0 0 64px; - } + .icon-image > * { + flex: 0 0 64px; + height: 64px; + } - .weather-icon { - --mdc-icon-size: 64px; - } + .weather-icon { + --mdc-icon-size: 64px; + } - .info { - display: flex; - justify-content: space-between; - flex-grow: 1; - overflow: hidden; - } + .info { + display: flex; + justify-content: space-between; + flex-grow: 1; + overflow: hidden; + } - .temp-attribute { - text-align: right; - } + .temp-attribute { + text-align: right; + } - .temp-attribute .temp { - position: relative; - margin-right: 24px; - } + .temp-attribute .temp { + position: relative; + margin-right: 24px; + } - .temp-attribute .temp span { - position: absolute; - font-size: 24px; - top: 1px; - } + .temp-attribute .temp span { + position: absolute; + font-size: 24px; + top: 1px; + } - .state, - .temp-attribute .temp { - font-size: 28px; - line-height: 1.2; - } + .name, + .temp-attribute .temp { + font-size: 28px; + line-height: 1.2; + } - .name, - .attribute { - font-size: 14px; - line-height: 1; - } + .state, + .attribute { + font-size: 14px; + line-height: 1; + } - .name-state { - overflow: hidden; - padding-right: 12px; - width: 100%; - } + .name-state { + overflow: hidden; + padding-right: 12px; + width: 100%; + } - .name, - .state { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } + .name, + .state { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } - .attribute { - white-space: nowrap; - } + .attribute { + white-space: nowrap; + } - .forecast { - display: flex; - justify-content: space-around; - padding-top: 16px; - } + .forecast { + display: flex; + justify-content: space-around; + padding-top: 16px; + } - .forecast > div { - text-align: center; - } + .forecast > div { + text-align: center; + } - .forecast .icon, - .forecast .temp { - margin: 4px 0; - } + .forecast .icon, + .forecast .temp { + margin: 4px 0; + } - .forecast .temp { - font-size: 16px; - } + .forecast .temp { + font-size: 16px; + } - .forecast-image-icon { - padding-top: 4px; - padding-bottom: 4px; - } + .forecast-image-icon { + padding-top: 4px; + padding-bottom: 4px; + display: flex; + } - .forecast-image { - width: 40px; - } + .forecast-image-icon > * { + width: 40px; + } - .forecast-icon { - --mdc-icon-size: 40px; - } + .forecast-icon { + --mdc-icon-size: 40px; + } - .attribute, - .templow, - .name { - color: var(--secondary-text-color); - } + .attribute, + .templow, + .state { + color: var(--secondary-text-color); + } - .unavailable { - height: 100px; - display: flex; - justify-content: center; - align-items: center; - font-size: 16px; - padding: 10px 20px; - text-align: center; - } + .unavailable { + height: 100px; + display: flex; + justify-content: center; + align-items: center; + font-size: 16px; + padding: 10px 20px; + text-align: center; + } - /* ============= NARROW ============= */ + /* ============= NARROW ============= */ - :host([narrow]) .icon-image { - min-width: 52px; - } + :host([narrow]) .icon-image { + min-width: 52px; + } - :host([narrow]) .weather-image { - flex: 0 0 52px; - width: 52px; - } + :host([narrow]) .weather-image { + flex: 0 0 52px; + width: 52px; + } - :host([narrow]) .weather-icon { - --mdc-icon-size: 52px; - } + :host([narrow]) .weather-icon { + --mdc-icon-size: 52px; + } - :host([narrow]) .state, - :host([narrow]) .temp-attribute .temp { - font-size: 22px; - } + :host([narrow]) .name, + :host([narrow]) .temp-attribute .temp { + font-size: 22px; + } - :host([narrow]) .temp-attribute .temp { - margin-right: 16px; - } + :host([narrow]) .temp-attribute .temp { + margin-right: 16px; + } - :host([narrow]) .temp span { - top: 1px; - font-size: 16px; - } + :host([narrow]) .temp span { + top: 1px; + font-size: 16px; + } - /* ============= VERY NARROW ============= */ + /* ============= VERY NARROW ============= */ - :host([veryNarrow]) .name, - :host([veryNarrow]) .attribute { - display: none; - } + :host([veryNarrow]) .state, + :host([veryNarrow]) .attribute { + display: none; + } - :host([veryNarrow]) .info { - flex-direction: column; - align-items: flex-start; - } + :host([veryNarrow]) .info { + flex-direction: column; + align-items: flex-start; + } - :host([veryNarrow]) .name-state { - padding-right: 0; - } + :host([veryNarrow]) .name-state { + padding-right: 0; + } - /* ============= VERY VERY NARROW ============= */ + /* ============= VERY VERY NARROW ============= */ - :host([veryVeryNarrow]) .info { - padding-top: 4px; - align-items: center; - } + :host([veryVeryNarrow]) .info { + padding-top: 4px; + align-items: center; + } - :host([veryVeryNarrow]) .content { - flex-wrap: wrap; - justify-content: center; - flex-direction: column; - } + :host([veryVeryNarrow]) .content { + flex-wrap: wrap; + justify-content: center; + flex-direction: column; + } - :host([veryVeryNarrow]) .icon-image { - margin-right: 0; - } - `; + :host([veryVeryNarrow]) .icon-image { + margin-right: 0; + } + `, + ]; } } diff --git a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts index 2e04013bb1..d0ff194bc2 100644 --- a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts @@ -8,21 +8,32 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; +import { ifDefined } from "lit-html/directives/if-defined"; + import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import "../../../components/entity/state-badge"; import { UNAVAILABLE_STATES } from "../../../data/entity"; import { getSecondaryWeatherAttribute, getWeatherUnit, - weatherIcons, - weatherImages, + getWeatherStateIcon, + weatherSVGStyles, } from "../../../data/weather"; -import { HomeAssistant, WeatherEntity } from "../../../types"; -import { EntitiesCardEntityConfig } from "../cards/types"; +import type { HomeAssistant, WeatherEntity } from "../../../types"; +import type { EntitiesCardEntityConfig } from "../cards/types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; import "../components/hui-warning"; -import { LovelaceRow } from "./types"; +import type { LovelaceRow } from "./types"; +import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { actionHandler } from "../common/directives/action-handler-directive"; +import { hasAction } from "../common/has-action"; +import { computeStateName } from "../../../common/entity/compute_state_name"; +import { ActionHandlerEvent } from "../../../data/lovelace"; +import { handleAction } from "../common/handle-action"; +import { stateIcon } from "../../../common/entity/state_icon"; @customElement("hui-weather-entity-row") class HuiWeatherEntityRow extends LitElement implements LovelaceRow { @@ -61,48 +72,126 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow { `; } - const weatherRowConfig = { - ...this._config, - icon: weatherIcons[stateObj.state], - image: weatherImages[stateObj.state], - }; + const pointer = + (this._config.tap_action && this._config.tap_action.action !== "none") || + (this._config.entity && + !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(this._config.entity))); + + const weatherStateIcon = getWeatherStateIcon(stateObj.state, this); return html` - -
-
- ${UNAVAILABLE_STATES.includes(stateObj.state) - ? computeStateDisplay( - this.hass.localize, - stateObj, - this.hass.language - ) - : html` - ${stateObj.attributes.temperature} - ${getWeatherUnit(this.hass, "temperature")} - `} -
-
- ${getSecondaryWeatherAttribute(this.hass!, stateObj)} -
+
+ ${weatherStateIcon || + html` + + `} +
+
+ ${this._config.name || computeStateName(stateObj)} +
+
+
+ ${UNAVAILABLE_STATES.includes(stateObj.state) + ? computeStateDisplay( + this.hass.localize, + stateObj, + this.hass.language + ) + : html` + ${stateObj.attributes.temperature} + ${getWeatherUnit(this.hass, "temperature")} + `}
- +
+ ${getSecondaryWeatherAttribute(this.hass!, stateObj)} +
+
`; } - static get styles(): CSSResult { - return css` - .attributes { - display: flex; - flex-direction: column; - justify-content: center; - text-align: right; - } + private _handleAction(ev: ActionHandlerEvent) { + handleAction(this, this.hass!, this._config!, ev.detail.action!); + } - .secondary { - color: var(--secondary-text-color); - } - `; + static get styles(): CSSResult[] { + return [ + weatherSVGStyles, + css` + :host { + display: flex; + align-items: center; + flex-direction: row; + } + + .info { + margin-left: 16px; + 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; + } + + .weather-icon { + --iron-icon-width: 40px; + --iron-icon-height: 40px; + } + + :host([rtl]) .flex { + margin-left: 0; + margin-right: 16px; + } + + .pointer { + cursor: pointer; + } + + .attributes { + display: flex; + flex-direction: column; + justify-content: center; + text-align: right; + margin-left: 8px; + } + + .secondary { + color: var(--secondary-text-color); + } + `, + ]; } }