Hi,
I have made my own custom Lovelace card to merge forecast and observed weather data. It’s not the prettiest code, but it does what I want it to do. The card updates from time to time, but not as often as I expected. I’ve noticed that the wind observation data on the card only updates infrequently - I think possibly only when the main temperature value changes. My wind observations update every 15 seconds, whereas temperature value only updates as quickly as the temperature is changing.
Can anyone tell me: what is the logic that determines when set hass(hass)
is called? Is it linked to certain entities that are configured against the card? Is there any way to force card refresh, or set a refresh interval? Or define which entities cause a refresh?
The card looks like this:
Here is the code:
class WeatherSummary extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
// set up DOM etc
setConfig(config) {
// check config
if (!config.temperature) {
throw new Error('Please define a temperature entity');
}
// setup root
const root = this.shadowRoot;
if (root.lastChild) root.removeChild(root.lastChild);
// get config
const cardConfig = Object.assign({}, config);
// create top element
const card = document.createElement('ha-card');
card.innerHTML = `
<div class="card-header">
<div class="name" id="title">${config.title}</div>
</div>
<div class="card-content">
<div class="content-area">
<div class="main">
<ha-icon id="weather-icon"></ha-icon>
<div id="main_temp"></div>
</div>
<div class="attributes">
<div id="temp_range"></div>
<div id="forecast_range"></div>
</div>
</div>
<div class="more">
<div class="more-detail" id="pressure-div">
<ha-icon icon="mdi:gauge"></ha-icon>
<div id="pressure"></div>
</div>
<div class="more-detail" id="humidity-div">
<ha-icon icon="mdi:water-percent"></ha-icon>
<div id="humidity"></div>
</div>
<div class="more-detail" id="rain-today-div" style="display:none;">
<ha-icon icon="mdi:water"></ha-icon>
<div id="rain-today"></div>
</div>
<div class="more-detail" id="wind-div">
<ha-icon icon="mdi:weather-windy"></ha-icon>
<div id="wind"></div>
</div>
</div>
<div id="detailed-forecast"></div>
</div>
`;
// set up custom css
const style = document.createElement('style');
style.textContent = `
card-header {
display: flex;
justify-content: space-between;
}
#value {
font-size: 40pt;
}
.content-area {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
padding-bottom: 10px;
}
.main {
display: flex;
align-items: center;
margin-right: 32px;
}
.main #main_temp {
font-size: 52px;
line-height: 1em;
position: relative;
}
.main #main_temp span {
font-size: 24px;
line-height: 1em;
position: absolute;
top: 4px;
}
.attributes {
color: var(--secondary-text-color);
line-height: 20px;
}
#weather-icon {
width: 52px;
height: 52px;
padding-right: 15px;
color: #44739e;
}
#detailed-forecast {
padding-top: 10px;
color: var(--secondary-text-color);
display: none;
}
.more {
display: flex;
flex-wrap: wrap;
}
.more-detail {
padding-right: 20px;
}
.more-detail div {
margin-top: 12px;
float: left;
}
.more ha-icon {
padding: 8px;
color: #44739e;
float: left;
}
`;
card.appendChild(style);
// make it so
root.appendChild(card);
this._config = cardConfig;
// attach click handlers
root.getElementById("title").addEventListener('click', event => {
this._showDetail();
});
root.getElementById("main_temp").addEventListener('click', event => {
this._fire('hass-more-info', { entityId: cardConfig.temperature });
});
root.getElementById("temp_range").addEventListener('click', event => {
this._fire('hass-more-info', { entityId: cardConfig.temperature });
});
root.getElementById("humidity-div").addEventListener('click', event => {
this._fire('hass-more-info', { entityId: cardConfig.humidity });
});
root.getElementById("rain-today-div").addEventListener('click', event => {
this._fire('hass-more-info', { entityId: cardConfig.rain_today });
});
root.getElementById("wind-div").addEventListener('click', event => {
this._fire('hass-more-info', { entityId: cardConfig.wind });
});
root.getElementById("pressure-div").addEventListener('click', event => {
this._fire('hass-more-info', { entityId: cardConfig.pressure });
});
root.getElementById("weather-icon").addEventListener('click', event => {
this._showDetail();
});
}
// click handler
_fire(type, detail, options) {
const node = this.shadowRoot;
options = options || {};
detail = (detail === null || detail === undefined) ? {} : detail;
const event = new Event(type, {
bubbles: options.bubbles === undefined ? true : options.bubbles,
cancelable: Boolean(options.cancelable),
composed: options.composed === undefined ? true : options.composed
});
event.detail = detail;
node.dispatchEvent(event);
return event;
}
// show forecast detail when weather-icon is clicked
_showDetail() {
const x = this.shadowRoot.getElementById("detailed-forecast")
x.style.display = (x.style.display === "block" ? "none" : "block");
}
// update card
set hass(hass) {
const config = this._config;
const root = this.shadowRoot;
const entityState = hass.states[config.temperature].state;
const measurement = hass.states[config.temperature].attributes.unit_of_measurement || "";
const tempMax = hass.states[config.temperature_maximum].state || "-";
const tempMin = hass.states[config.temperature_minimum].state || "-";
const forecastMax = hass.states[config.forecast_maximum].state || "-";
const forecastMin = hass.states[config.forecast_minimum].state || "-";
const humidity = hass.states[config.humidity].state || "-";
const weatherIcon = hass.states[config.icon].state;
const detailedForecast = hass.states[config.forecast].state || "=";
const rainToday = hass.states[config.rain_today].state;
const windToday = hass.states[config.wind].state;
const windGust = hass.states[config.wind_gust].state;
const windDir = hass.states[config.wind_direction].state;
const pressure = hass.states[config.pressure].state;
if (entityState !== this._entityState) {
root.getElementById("main_temp").innerHTML = `${entityState}<span>${measurement}</span>`;
root.getElementById("temp_range").innerHTML = `${tempMin}° to ${tempMax}° today`;
root.getElementById("forecast_range").innerHTML = `${forecastMin}° to ${forecastMax}° forecast`;
root.getElementById("humidity").innerHTML = `${humidity}%`;
root.getElementById("weather-icon").icon = `${weatherIcon}`;
root.getElementById("detailed-forecast").innerText = `${detailedForecast}`;
root.getElementById("rain-today").innerText = `${rainToday} mm`;
root.getElementById("wind").innerHTML = `${windToday} kt ${windDir}, ${windGust} kt gusts`;
root.getElementById("pressure").innerHTML = `${pressure} mb`;
if (rainToday > 0)
root.getElementById("rain-today-div").style.display = "flex";
this._entityState = entityState
}
root.lastChild.hass = hass;
}
// provide card size
getCardSize() {
return 3;
}
}
customElements.define('weather-summary', WeatherSummary);
And current UI config:
forecast: sensor.bom_hobart_detailed_summary_0
forecast_maximum: sensor.bom_max_temperature
forecast_minimum: sensor.bom_min_temperature
humidity: sensor.outside_humidity
icon: sensor.bom_hobart_icon_0
pressure: sensor.bom_pressure_mb
rain_today: sensor.bom_rain_today
temperature: sensor.outside_temperature
temperature_maximum: sensor.outside_temperature_maximum
temperature_minimum: sensor.outside_temperature_minimum
title: Weather
type: 'custom:weather-summary'
wind: sensor.wind_speed
wind_direction: sensor.bom_wind_direction
wind_gust: sensor.wind_gust