Template Weather Provider with weather data from Google

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:

image

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. :slight_smile:

2 Likes

Hi. It seems the icons are not being recognized for all. Any idea how to fix this??

2023-06-17_11-50-49

forecast:

temperature: 30
temperature_unit: °C
humidity: 43
pressure_unit: hPa
wind_speed: 2
wind_speed_unit: km/h
visibility_unit: km
precipitation_unit: mm
forecast: 
- datetime: '2023-06-17T00:00:00Z'
  temperature: 32
  templow: 18
  condition: clear
- datetime: '2023-06-18T00:00:00Z'
  temperature: 33
  templow: 21
  condition: clear
- datetime: '2023-06-19T00:00:00Z'
  temperature: 26
  templow: 18
  condition: cloudy
- datetime: '2023-06-20T00:00:00Z'
  temperature: 28
  templow: 16
  condition: clearwithperiodicclouds
- datetime: '2023-06-21T00:00:00Z'
  temperature: 28
  templow: 17
  condition: clearwithperiodicclouds
- datetime: '2023-06-22T00:00:00Z'
  temperature: 26
  templow: 17
  condition: cloudywithbriefrain
- datetime: '2023-06-23T00:00:00Z'
  temperature: 28
  templow: 20
  condition: cloudywithbriefrain
- datetime: '2023-06-24T00:00:00Z'
  temperature: 29
  templow: 21
  condition: cloudy

Yes, sorry. The mapping between the names of the conditions between Weather.com and HA is not complete.

For HA the list of conditions names is shown here: Weather - Home Assistant under “Condition Mapping”.

I don’t have the complete list of condition names for Weather.com, so I did some code that is not perfect:

            "condition": "{{ states('sensor.wdc_condition_day_' + '{0:d}'.format(day_number) ) | lower | replace('mostly', '') | replace('scattered', '') | replace('showers', 'rainy') | replace(' ', '') }}"

You can enrich this code if you want by adding more conditions. For example, the condition you just experienced can be handled like this:

            "condition": "{{ states('sensor.wdc_condition_day_' + '{0:d}'.format(day_number) ) | lower | replace('mostly', '') | replace('scattered', '') | replace('showers', 'rainy')  | replace('with periodic clouds', '')  | replace('with brief rain', '') | replace(' ', '') }}"

Note that there is another new HA integration with Weather.com by jaydeethree. It can be found here: Weather.com (The Weather Channel) Custom Integration

It is good to have choices and alternatives. :+1: :slight_smile:

Thanks for the reply. I did saw that custom integration when he posted.

How often does the scrape happens?? I am currently using the weatherbit . io integration and their api calls reduced drastically for the free version. Also, when I restart HA, it errors saying I have reached it’s limits of calls. So, I lose my weather for about 30 minutes after a restart.

By default, HA runs scrapes every 10 minutes. You can add a scan_interval parameter to control the rate as described here: https://www.home-assistant.io/integrations/scrape/#scan_interval

I believe that there is no rate limit for this integration that is based on scraping, as it brings the data from Google’s public service.

I noticed there is a precipitation. Is it possible to grab those data? I think there is current and 3 days out chance of precipitation in %.