Here is an example of a Template Weather Provider fetching weather data from https://www.google.com/search?q=weather&hl=en-GB.
It uses the HA internal Scrape integration to extract the local weather data from the above link.
Performance
This implementation is actually efficient, as it executes only one API call to get all the data, and parses the response once. Then it uses fast CSS selectors to extract the needed data points.
The code
The code is long mostly because a Weather object has many data points.
scrape:
- resource: https://www.google.com/search
params:
q: "weather"
hl: "en-GB"
headers:
User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
sensor:
- name: "wdc_location_name"
select: ".BBwThe"
- name: "wdc_condition"
select: "#wob_dc"
- name: "wdc_temperature"
unit_of_measurement: "Ā°C"
select: "#wob_tm"
state_class: measurement
device_class: temperature
- name: "wdc_humidity"
unit_of_measurement: "%"
select: "#wob_hm"
state_class: measurement
device_class: humidity
value_template: '{{ value.split("%")[0] }}'
- name: "wdc_wind_speed"
unit_of_measurement: "km/h"
select: "#wob_ws"
state_class: measurement
device_class: speed
value_template: '{{ value.split(" ")[0] }}'
- name: "wdc_attribution"
select: "div.YfftMc a"
- name: "wdc_condition_day_0"
select: "#wob_dp div:nth-of-type(1) img"
attribute: alt
- name: "wdc_temp_day_0_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(1)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_0_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(1)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_1"
select: "#wob_dp div:nth-of-type(2) img"
attribute: alt
- name: "wdc_temp_day_1_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(2)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_1_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(2)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_2"
select: "#wob_dp div:nth-of-type(3) img"
attribute: alt
- name: "wdc_temp_day_2_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(3)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_2_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(3)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_3"
select: "#wob_dp div:nth-of-type(4) img"
attribute: alt
- name: "wdc_temp_day_3_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(4)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_3_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(4)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_4"
select: "#wob_dp div:nth-of-type(5) img"
attribute: alt
- name: "wdc_temp_day_4_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(5)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_4_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(5)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_5"
select: "#wob_dp div:nth-of-type(6) img"
attribute: alt
- name: "wdc_temp_day_5_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(6)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_5_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(6)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_6"
select: "#wob_dp div:nth-of-type(7) img"
attribute: alt
- name: "wdc_temp_day_6_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(7)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_6_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(7)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_condition_day_7"
select: "#wob_dp div:nth-of-type(8) img"
attribute: alt
- name: "wdc_temp_day_7_high"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(8)>div:nth-last-child(1)>div:nth-of-type(1)>span.wob_t"
state_class: measurement
device_class: temperature
- name: "wdc_temp_day_7_low"
unit_of_measurement: "Ā°C"
select: "#wob_dp div:nth-of-type(8)>div:nth-last-child(1)>div:nth-of-type(2)>span.wob_t"
state_class: measurement
device_class: temperature
weather:
- platform: template
name: My Weather
humidity_template: "{{ states('sensor.wdc_humidity') | float }}"
temperature_template: "{{ states('sensor.wdc_temperature') | float }}"
wind_speed_template: "{{ states('sensor.wdc_wind_speed') | float }}"
condition_template: "{{ states('sensor.wdc_condition') | lower | replace('mostly', '') | replace('scattered', '') | replace('showers', 'rainy') | replace(' ', '') }}"
attribution_template: "Weather at {{ states('sensor.wdc_location_name') }} provided by {{ states('sensor.wdc_attribution') }}"
forecast_template: >-
[
{% for day_number in range(8) %}
{
"datetime": "{{ (now() + timedelta( days = day_number )).strftime('%Y-%m-%dT00:00:00Z') }}",
"temperature": "{{ states('sensor.wdc_temp_day_' + '{0:d}'.format(day_number) + '_high' ) }}",
"templow": "{{ states('sensor.wdc_temp_day_' + '{0:d}'.format(day_number) + '_low') }}",
"condition": "{{ states('sensor.wdc_condition_day_' + '{0:d}'.format(day_number) ) | lower | replace('mostly', '') | replace('scattered', '') | replace('showers', 'rainy') | replace(' ', '') }}"
},
{% endfor %}
]
Installation
Just copy the above code into your configuration.yaml
file and restart HA. It will create a new weather.my_weather
entity.
Selecting a Location
By default, Google decides the geo-location for you, and it does it quite well. You can see the selected location in the sensor.wdc_location_name sensor.
You can manually select the location, by adding the location name in the āqā parameter of the scrape. For example, if you want to see the weather in Berlin, change the q
parameter from:
q: "weather"
to this:
q: "weather+Berlin"
UI
Any weather card can present the weather data:
Some articles suggest that Google brings weather data from the best sources, so this integration may actually provide better quality data than the standard HA Weather integrations.
Feedback
Iām pretty sure that there are better ways to write this. Feel free to extend, improve, and re-share.