Tomorrow.io hourly has stopped working

It appears that hourly forecast was deprecated.

We now have to have a template run something every hour to pull out the data in some new format.

I don’t know what was wrong with the old format. I don’t have time to fix mine so I have just removed it from all my dashboards. One less thing to go wrong.

A weather entity’s forecast attribute has been eliminated (as per the deprecation announcement made over 6 months ago).

To get the data that was contained in the forecast attribute, you must now request it using the weather.get_forecasts service call. Alternately, the Weather card displays it automatically.

1 Like

Solved. The problem was actually due to the deprecation of the forecast attribute for weather entities. Once I switched my template weather entity to the new forecast acquisition method hourly forecasts worked again.

ok, thanks. considering I have created custom sensors that utilise a mixture of data, this might not be so easy. Perhaps you have a link to this announcement?
edit: nevermind. I can see the chat about it all now… sigh… I guess I’ll wait until I have a spare day to pull my hair out… or wait until the future where chatgpt can tell me how to do it than trudge through forums for info :sweat_smile:

I’m in the same boat. I don’t have a spare day to fix.

@tinkererer and @VPC : in the topic for the Weatherman e-ink display I posted an example on how you can modify the template for the display to use the service. It actually isn’t that hard and most of the weather code looks very similar:

1 Like

My thing is I don’t want to have to run something every hour to get forecast data that already exists.

Thank you for the link, I appreciate it and will take a look.

As I understand it he service call should be returning cached data, not do an actual call. So you are just getting existing data another way. You can use the action in other places too, it does not need to be a time trigger afaik.

It’s not in the state machine so, whenever you want it, you’ll need to use the weather.get_forecasts service call.

How often you execute the service call is determined by your application’s requirements.

Thank you. It looks like it might be pretty straightforward but I have 2 different target entities weather.tomorrow_io_cottage_hourly' and weather.tomorrow_io_cottage_daily and I see a response_variable: hourly used in all the examples. I’m not sure how I would set mine up considering I’m calling on both hourly and daily?

Edit: Is it simply a case of using it twice such as:

service: weather.get_forecasts
target:
  entity_id:
    - weather.tomorrow_io_cottage
data:
  type: hourly
response_variable: hourly

and

service: weather.get_forecasts
target:
  entity_id:
    - weather.tomorrow_io_cottage
data:
  type: daily
response_variable: daily

?

My code:

sensor:
- name: Cottage Guests
  state: "OK"
  attributes:
    guest_message: >
      {% set num_guests = states('sensor.smoobu_guest_numbers') %}
      {% if num_guests == "1" %} ' ' {% elif num_guests == "2" %} and Guest {% elif num_guests > "2" %} and Guests {% endif %}
    guest_activity: > # 0=default  1=welcome  2=during  3=goodbye 4=same day guest arriving as one leaving
      {% set status = "0" %}
      {% if now().date().strftime('%Y-%m-%d') == states('sensor.smoobu_guest_arrival') %}
        {% set status = "1" %}
      {% elif now().date().strftime('%Y-%m-%d') > states('sensor.smoobu_guest_arrival') and now().date().strftime('%Y-%m-%d') < states('sensor.smoobu_guest_departure') %}
        {% set status = "2" %}
      {% elif now().date().strftime('%Y-%m-%d') == states('sensor.smoobu_guest_departure') and now().strftime('%H') | int < 12 %}
        {% set status = "3" %}
      {% elif now().date().strftime('%Y-%m-%d') == states('sensor.smoobu_second_guest_arrival') %}
        {% set status = "4" %}
      {% endif %}
      {{ status }}
      
      
- name: My Weather Data
  state: "OK"
  attributes:
    today_temp_low: >
      {% set temp = state_attr('weather.tomorrow_io_cottage_daily','forecast')[0].templow %}
      {% set lowest_temp = temp %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].temperature | round < lowest_temp %}
        {% set lowest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].temperature | round %} {% endif %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].temperature | round < lowest_temp %}
        {% set lowest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].temperature | round %} {% endif %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].temperature | round < lowest_temp %}
        {% set lowest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].temperature | round %} {% endif %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].temperature | round < lowest_temp %}
        {% set lowest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].temperature | round %} {% endif %}
      {{ lowest_temp }}
    today_temp_high: >
      {% set temp = state_attr('weather.tomorrow_io_cottage_daily','forecast')[0].temperature %}
      {% set highest_temp = temp %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].temperature | round > highest_temp %}
        {% set highest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].temperature | round %} {% endif %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].temperature | round > highest_temp %}
        {% set highest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].temperature | round %} {% endif %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].temperature | round > highest_temp %}
        {% set highest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].temperature | round %} {% endif %}
      {% if state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].temperature | round > highest_temp %}
        {% set highest_temp = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].temperature | round %} {% endif %}
      {{ highest_temp }}

- name: Weatherman Data
  state: "OK"
  attributes:
    weather_condition_0: >
      {% set cond0 = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].condition %}
      {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
      {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
      {% set cond0_time = as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].datetime) %}
      {% if ( cond0_time < next_rising and next_rising < next_setting ) or ( cond0_time > next_setting and cond0_time < next_rising ) %}
          {% if cond0 == 'sunny' or 'clear-night' %} night {% elif cond0 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond0 }} {% endif %}
      {% else %}
          {{ cond0 }}
      {% endif %}
    weather_temperature_0: >
      {{ state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].temperature | round }}
    weather_timestamp_0: >
      {{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[0].datetime) | timestamp_custom('%p') | lower}}

    weather_condition_1: >
      {% set cond1 = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].condition %}
      {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
      {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
      {% set cond1_time = as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].datetime) %}
      {% if ( cond1_time < next_rising and next_rising < next_setting ) or ( cond1_time > next_setting and cond1_time < next_rising ) %}
          {% if cond1 == 'sunny' or 'clear-night' %} night {% elif cond1 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond1 }} {% endif %}
      {% else %}
          {{ cond1 }}
      {% endif %}
    weather_temperature_1: >
      {{ state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].temperature | round }}
    weather_timestamp_1: >
      {{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[1].datetime) | timestamp_custom('%p') | lower}}

    weather_condition_2: >
      {% set cond2 = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].condition %}
      {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
      {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
      {% set cond2_time = as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].datetime) %}
      {% if ( cond2_time < next_rising and next_rising < next_setting ) or ( cond2_time > next_setting and cond2_time < next_rising ) %}
          {% if cond2 == 'sunny' or 'clear-night' %} night {% elif cond2 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond2 }} {% endif %}
      {% else %}
          {{ cond2 }}
      {% endif %}
    weather_temperature_2: >
      {{ state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].temperature | round }}
    weather_timestamp_2: >
      {{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[2].datetime) | timestamp_custom('%p') | lower}}

    weather_condition_3: >
      {% set cond3 = state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].condition %}
      {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
      {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
      {% set cond3_time = as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].datetime) %}
      {% if ( cond3_time < next_rising and next_rising < next_setting ) or ( cond3_time > next_setting and cond3_time < next_rising ) %}
          {% if cond3 == 'sunny' or 'clear-night' %} night {% elif cond3 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond3 }} {% endif %}
      {% else %}
          {{ cond3 }}
      {% endif %}
    weather_temperature_3: >
      {{ state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].temperature | round }}
    weather_timestamp_3: >
      {{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(state_attr('weather.tomorrow_io_cottage_hourly', 'forecast')[3].datetime) | timestamp_custom('%p') | lower}}

in regards to the edit above. I assume this will work by simply adding these new triggers to call the service? Currently dont have access to the display to check

I guess not

when you call get_forecasts and put the result in your response_variable, that variable only lives within the scope of the parent block (that - trigger in your specific case. so in your examples here, the hourly and daily response_varaibles are created then immediately discarded because that was the last line in that block.

here is the example from the documentation.

template:
  - trigger:
      - platform: time_pattern
        hours: /1
    action:
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: weather.home
        response_variable: hourly
    sensor:
      - name: Temperature forecast next hour
        unique_id: temperature_forecast_next_hour
        state: "{{ hourly['weather.home'].forecast[0].temperature }}"
        unit_of_measurement: °C

see how the hourly variable is used within the same - trigger block?

it looks to me like you’re trying to create a template sensor. which is what the doc’s example does. you should just tweak the doc example and keep it’s structure.

1 Like

In addition to the above comment above.

  • Please don’t use screenshots, post the code itself inside a code block, so we can quote/copy/modify.
  • Actions are a list, you can put multiple actions below one time trigger.
  • Judging from the errors you also need to change the calls to state_attr() with the forecast attribute, as in my axample. Look for all the places forecast is mentioned, they must use the hourly or daily variables and not the attribute.
1 Like

Thanks for the help. I’m almost there but still got some funky behavior.

My code is as such:

- trigger:
    - platform: time_pattern
      hours: /1
  action:
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: weather.tomorrow_io_home_hourly
        response_variable: hourly
      - service: weather.get_forecasts
        data:
          type: daily
        target:
          entity_id: weather.tomorrow_io_home_daily
        response_variable: daily
- name: Weatherman Data
      state: "OK"
      attributes:
        weather_condition_0: >
          {% set cond0 = hourly["weather.tomorrow_io_home_hourly"].forecast[0].condition %}
          {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
          {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
          {% set cond0_time = as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[0].datetime) %}
          {% if ( cond0_time < next_rising and next_rising < next_setting ) or ( cond0_time > next_setting and cond0_time < next_rising ) %}
              {% if cond0 == 'sunny' or 'clear-night' %} night {% elif cond0 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond0 }} {% endif %}
          {% else %}
              {{ cond0 }}
          {% endif %}
        weather_temperature_0: >
          {{ hourly["weather.tomorrow_io_home_hourly"].forecast[0].temperature | round }}
        weather_timestamp_0: >
          {{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[0].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[0].datetime) | timestamp_custom('%p') | lower}}
        weather_condition_1: >
          {% set cond1 = hourly["weather.tomorrow_io_home_hourly"].forecast[1].condition %}
          {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
          {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
          {% set cond1_time = as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[1].datetime) %}
          {% if ( cond1_time < next_rising and next_rising < next_setting ) or ( cond1_time > next_setting and cond1_time < next_rising ) %}
              {% if cond1 == 'sunny' or 'clear-night' %} night {% elif cond1 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond1 }} {% endif %}
          {% else %}
              {{ cond1 }}
          {% endif %}
        weather_temperature_1: >
          {{ hourly["weather.tomorrow_io_home_hourly"].forecast[1].temperature | round }}
        weather_timestamp_1: >
          {{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[1].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[1].datetime) | timestamp_custom('%p') | lower}}
        weather_condition_2: >
          {% set cond2 = hourly["weather.tomorrow_io_home_hourly"].forecast[2].condition %}
          {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
          {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
          {% set cond2_time = as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[2].datetime) %}
          {% if ( cond2_time < next_rising and next_rising < next_setting ) or ( cond2_time > next_setting and cond2_time < next_rising ) %}
              {% if cond2 == 'sunny' or 'clear-night' %} night {% elif cond2 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond2 }} {% endif %}
          {% else %}
              {{ cond2 }}
          {% endif %}
        weather_temperature_2: >
          {{ hourly["weather.tomorrow_io_home_hourly"].forecast[2].temperature | round }}
        weather_timestamp_2: >
          {{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[2].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[2].datetime) | timestamp_custom('%p') | lower}}
        weather_condition_3: >
          {% set cond3 = hourly["weather.tomorrow_io_home_hourly"].forecast[3].condition %}
          {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
          {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
          {% set cond3_time = as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[3].datetime) %}
          {% if ( cond3_time < next_rising and next_rising < next_setting ) or ( cond3_time > next_setting and cond3_time < next_rising ) %}
              {% if cond3 == 'sunny' or 'clear-night' %} night {% elif cond3 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond3 }} {% endif %}
          {% else %}
              {{ cond3 }}
          {% endif %}
        weather_temperature_3: >
          {{ hourly["weather.tomorrow_io_home_hourly"].forecast[3].temperature | round }}
        weather_timestamp_3: >
          {{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[3].datetime) | timestamp_custom('%I') | int }}{{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[3].datetime) | timestamp_custom('%p') | lower}}

I can confirm that is the correct service call:

but my display is showing these 4x 6am conditions which seems to be a daily forecast instead of hourly?

I don’t believe I need to modify my esphome file for the change as it should still be reading it correctly

Yeah… can confirm those are from the daily entity (check temps against the display) which is strange because I’m specifically targeting the hourly call and I dont see any errors otherwise

this is super weird

I’m clearly targeting hourly:

- name: Weatherman Data
      state: "OK"
      attributes:
        weather_condition_0: >
          {% set cond0 = hourly["weather.tomorrow_io_home_hourly"].forecast[0].condition %}
          {% set next_setting = as_timestamp(state_attr('sun.sun', 'next_setting')) %}
          {% set next_rising = as_timestamp(state_attr('sun.sun', 'next_rising')) %}
          {% set cond0_time = as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[0].datetime) %}
          {% if ( cond0_time < next_rising and next_rising < next_setting ) or ( cond0_time > next_setting and cond0_time < next_rising ) %}
              {% if cond0 == 'sunny' or 'clear-night' %} night {% elif cond0 == 'partlycloudy' %} night-partly-cloudy {% else %} {{ cond0 }} {% endif %}
          {% else %}
              {{ cond0 }}
          {% endif %}
        weather_temperature_0: >
          {{ hourly["weather.tomorrow_io_home_hourly"].forecast[0].temperature | round }}
        weather_timestamp_0: >
          {{ as_timestamp(hourly["weather.tomorrow_io_home_hourly"].forecast[0].datetime) | timestamp_custom('%I')

It is weird indeed. I do not see anything wrong with your code. What if you swap the actions and do hourly last and dayly first? If that changes things (it shouldn’t) then it would point to a bug where the second service calls internally replaces the data from the first call.

swapping the order didnt change anything. But neither did commenting out the daily service, then the hourly and then both altogether. :thinking:

And now I’m not receiving anything at all which leads me to believe the attributes were somehow pulled elsewhere from a wonky file config or something.
image

Will update if I figure it out!

Do yourself a favor and reduce your overhead by using variables. Also, You don’t need to get the daily information every hour. Separate that out.

e.g.

- trigger:
    - platform: time_pattern
      hours: /1
  action:
      - variables:
          entity: weather.tomorrow_io_home_hourly
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: "{{ entity }}"
        response_variable: hourly
      - variables:
          mapped:
            sunny: night
            clear-night: night
            partlycloudy: night-partly-cloudy
          info: >
            {%- set ns = namespace(items=[]) %}
            {%- set sunrise = state_attr('sun.sun', 'next_setting') %}
            {%- set sunset = state_attr('sun.sun', 'next_rising') %}
            {%- for hour in hourly[entity] if loop.index < 4 %}
              {%- set cond = hour.condition %}
              {%- set t = hour.datetime | as_datetime | as_local %}
              {%- set tod = t.strftime('%I%p') %}
              {%- set temp = hour.temperature | round %}
              {%- set ret = mapped.get(cond, cond) if (t < sunrise and sunrise < sunset) or (sunset < t < sunrise) else cond %}
              {%- set ns.items = ns.items + [dict(cond=ret, tod=tod, temp=temp)] %}
            {%- endfor %}
            {{ ns.items }}
  sensor:
    - name: Weatherman Data
      device_class: timestamp
      state: "{{ now() }}"
      attributes:
        weather_condition_0: "{{ info[0].cond }}"
        weather_temperature_0: "{{ info[0].tod }}"
        weather_timestamp_0: "{{ info[0].temp }}"
        weather_condition_1: "{{ info[1].cond }}"
        weather_temperature_1: "{{ info[1].tod }}"
        weather_timestamp_1: "{{ info[1].temp }}"
        weather_condition_2: "{{ info[2].cond }}"
        weather_temperature_2: "{{ info[2].tod }}"
        weather_timestamp_2: "{{ info[2].temp }}"
        weather_condition_3: "{{ info[3].cond }}"
        weather_temperature_3: "{{ info[3].tod }}"
        weather_timestamp_3: "{{ info[3].temp }}"

You can also turn this into a script to debug the output in the scripts trace.

get_weather_info:
  sequence:
      - variables:
          entity: weather.tomorrow_io_home_hourly
      - service: weather.get_forecasts
        data:
          type: hourly
        target:
          entity_id: "{{ entity }}"
        response_variable: hourly
      - variables:
          mapped:
            sunny: night
            clear-night: night
            partlycloudy: night-partly-cloudy
          info: >
            {%- set ns = namespace(items=[]) %}
            {%- set sunrise = state_attr('sun.sun', 'next_setting') %}
            {%- set sunset = state_attr('sun.sun', 'next_rising') %}
            {%- for hour in hourly[entity] if loop.index < 4 %}
              {%- set cond = hour.condition %}
              {%- set t = hour.datetime | as_datetime | as_local %}
              {%- set tod = t.strftime('%I%p') %}
              {%- set temp = hour.temperature | round %}
              {%- set ret = mapped.get(cond, cond) if (t < sunrise and sunrise < sunset) or (sunset < t < sunrise) else cond %}
              {%- set ns.items = ns.items + [dict(cond=ret, tod=tod, temp=temp)] %}
            {%- endfor %}
            {{ ns.items }}
      - stop: End the script
        response_variable: info

Then using it in your template entity…

- trigger:
    - platform: time_pattern
      hours: /1
  action:
      - service: script.get_weather_info
        response_variable: info
  sensor:
    - name: Weatherman Data
      device_class: timestamp
      state: "{{ now() }}"
      attributes:
        weather_condition_0: "{{ info[0].cond }}"
        weather_temperature_0: "{{ info[0].tod }}"
        weather_timestamp_0: "{{ info[0].temp }}"
        
        weather_condition_1: "{{ info[1].cond }}"
        weather_temperature_1: "{{ info[1].tod }}"
        weather_timestamp_1: "{{ info[1].temp }}"
        
        weather_condition_2: "{{ info[2].cond }}"
        weather_temperature_2: "{{ info[2].tod }}"
        weather_timestamp_2: "{{ info[2].temp }}"
        
        weather_condition_3: "{{ info[3].cond }}"
        weather_temperature_3: "{{ info[3].tod }}"
        weather_timestamp_3: "{{ info[3].temp }}"
1 Like