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:

3 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 %.

I get the following error:

ā€˜forecast_templateā€™ is an invalid option for ā€˜weather.templateā€™, check: forecast_template

I tried to fix the problem myself by using ā€œweather.get_forecast serviceā€. Unfortunately without success.

Does anyone have a solution?

Always refer to the docs when using code from an ā€œoldā€ post:

forecast_template is no longer a valid option as of Aug-23 (ref). You need to use one of these as appropriate, probably the hourly one:

image

The deprecation of forecast and the replacement with get_forecasts() only applies to integrations, not template weather entities you put together.

1 Like

Thank you so much! I think it works! :grinning: