Creating Custom Weather Forecast Entity from Sensors

I am trying to create a Weather Forecast entity based on a JSON file which is created from a CSV file generated by a software package WXSIM which is running locally. This package generates an hourly forecast for my location.

I have successfully created a JSON file, and have that in HA using rest, and it appears as a sensor (sensor.wxsim). I can see the values in Developer Tools.

I am now trying to create a Forecast entity, initially to just populate a weather card.

I am using a third party integration to bring in current data from my weather station, which appears to work, but the forecast bit does not.

Below is what I have so far, but it doesn’t work.
Template in Developer Tools returns the error “TypeError: ‘NoneType’ object is not iterable”

Can someone point me in the right direction please?

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "{{ state_attr('sensor.wxsim', 'WX Type 1') }}"
    temperature_template: "{{ states('sensor.meteobridge_air_temperature') | float }}" 
    temperature_unit: "°C"
    humidity_template: "{{ states('sensor.meteobridge_relative_humidity') | float }}"  
    visibility_template: "{{ states('sensor.meteobridge_visibility') | float }}"
    forecast_hourly_template: |
      [
      {% for datetime_list in state_attr('sensor.wxsim', 'time') %}
        {% if loop.index > (now().hour|int)+1 %}
          {% set temperature_list = state_attr('sensor.wxsim', 'temperature')[loop.index0] %}
          {% set humidity_list = state_attr('sensor.wxsim', 'Rel.Hum.')[loop.index0] %}
          {% set precipitation_list = state_attr('sensor.wxsim', 'Tot.Prcp')[loop.index0] %}
          {% set wind_bearing_list = state_attr('sensor.wxsim', 'Wind Dir.')[loop.index0] %}
          {% set wind_speed_list = state_attr('sensor.wxsim', 'Wind Spd.')[loop.index0] %}
          {% set condition_list = state_attr('sensor.wxsim', 'WX Type 1')[loop.index0] %}
          
           "datetime": "{{datetime_list | as_datetime}}",
           "temperature": {{temperature_list | float}},
           "humidity": {{humidity_list | float}},
           "precipitation": {{precipitation_list | float}},
           "wind_bearing": {{wind_bearing_list | int}},
           "wind_speed": {{wind_speed_list | float}},
           "condition": "{{condition_list | float}},
        {% endif %}
      {% endfor %}
      ]

Use a namespace to extract an actual list of dictionaries out of the loop. Currently, you are just printing a “list-shaped” string.

Also, there are a few quote marks that do not belong…

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "{{ state_attr('sensor.wxsim', 'WX Type 1') }}"
    temperature_template: "{{ states('sensor.meteobridge_air_temperature') | float }}" 
    temperature_unit: "°C"
    humidity_template: "{{ states('sensor.meteobridge_relative_humidity') | float }}"  
    visibility_template: "{{ states('sensor.meteobridge_visibility') | float }}"
    forecast_hourly_template: |
      {% set ns = namespace(forecasts=[]) %}
      {% for datetime_list in state_attr('sensor.wxsim', 'time') %}
        {% if loop.index > now().hour + 1 %}
          {% set temperature_list = state_attr('sensor.wxsim', 'temperature')[loop.index0] %}
          {% set humidity_list = state_attr('sensor.wxsim', 'Rel.Hum.')[loop.index0] %}
          {% set precipitation_list = state_attr('sensor.wxsim', 'Tot.Prcp')[loop.index0] %}
          {% set wind_bearing_list = state_attr('sensor.wxsim', 'Wind Dir.')[loop.index0] %}
          {% set wind_speed_list = state_attr('sensor.wxsim', 'Wind Spd.')[loop.index0] %}
          {% set condition_list = state_attr('sensor.wxsim', 'WX Type 1')[loop.index0] %}
          
          {%  set ns.forecasts = ns.forecasts + 
            [ {"datetime": datetime_list | as_datetime,
            "temperature": temperature_list | float,
            "humidity": humidity_list | float,
            "precipitation": precipitation_list | float,
            "wind_bearing": wind_bearing_list | int,
            "wind_speed": wind_speed_list | float,
            "condition": condition_list | float} ] %}
        {% endif %}
      {% endfor %}
      {{ ns.forecasts }}

The extra quote marks doesn’t surprise me. I copied and pasted it and didn’t run it through a YAML validator to pick them up - so my error there.

Thank you for posting some corrected code, however your version of the code still doesn’t work.
Exactly the same as I what I had - the live data using Meteobridge sensors is being populated as expected, but the forecast data just says “Unknown”.

Should I expect it to work through the template testing tool in Developer Tools? If not, then I will stop trying it through that.

Looking at the source JSON file again, I notice that it is actually creating a forecast for 30 minute intervals, not hourly. Would that cause a problem?

Can I provide any more information to help troubleshoot why this isn’t working?

Yes, the template on it’s own should render in the Template Editor.

Are you sure all the attributes are actually returning lists?

For example, what do the following return:

{{state_attr('sensor.wxsim', 'time')}}

{{state_attr('sensor.wxsim', 'time')[0]}}

That’s definitely a possible issue for the weather entity itself, but it wouldn’t affect the template rendering in the Template editor.

This might sound like a basic question, but do I just paste that in to Developer Tools and should see a result?
As I don’t. I get “Result type: String” and then the text repeated.

I have no idea if the attribute is returning a list or not. I have just been reading various posts on the forums etc trying to build something. Lots of copy and pasting and then modifying the entities to match what I have.

Sorry, it needs the curly braces in the Template editor, corrected above.

I wasn’t returning anything until I noticed that it was case sensitive. When I changed it to Time, it returned the value - which at the moment is just “18” as that is what the JSON has in it.
I do have another value of datetime, which contains the full date and time.

Having corrected that case issue, I no longer get the error in Developer Tools, but I don’t get anything back in the forecast block. This is the result - with that large space below.

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "CLOUDY     "
    temperature_template: "13.7" 
    temperature_unit: "°C"
    humidity_template: "95.0"  
    visibility_template: "0.8"
    forecast_hourly_template: |
      
      
        
      
        
      
        
      
        
      
        
      
      []

Just to clarify, are you talking about {{ state_attr('sensor.wxsim', 'Time')[0] }}?

If so, please post the output for the other one as well. I think you going to have to use the attribute with the actual datetime so we can remove the mid-hour values.

Also, double check if the temperature attribute should be upper case too.

Yes, I was talking about the Time example you asked me to test.

The temperature attribute had to be upper case as well, so that one was corrected.

This is the current YAML I used to generate the above.

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "{{ state_attr('sensor.wxsim', 'WX Type 1') }}"
    temperature_template: "{{ states('sensor.meteobridge_air_temperature') | float }}" 
    temperature_unit: "°C"
    humidity_template: "{{ states('sensor.meteobridge_relative_humidity') | float }}"  
    visibility_template: "{{ states('sensor.meteobridge_visibility') | float }}"
    forecast_hourly_template: |
      {% set ns = namespace(forecasts=[]) %}
      {% for datetime_list in state_attr('sensor.wxsim', 'Time') %}
        {% if loop.index > now().hour + 1 %}
          {% set temperature_list = state_attr('sensor.wxsim', 'Temperature')[loop.index0] %}
          {% set humidity_list = state_attr('sensor.wxsim', 'Rel.Hum.')[loop.index0] %}
          {% set precipitation_list = state_attr('sensor.wxsim', 'Tot.Prcp')[loop.index0] %}
          {% set wind_bearing_list = state_attr('sensor.wxsim', 'Wind Dir.')[loop.index0] %}
          {% set wind_speed_list = state_attr('sensor.wxsim', 'Wind Spd.')[loop.index0] %}
          {% set condition_list = state_attr('sensor.wxsim', 'WX Type 1')[loop.index0] %}
          
           {%  set ns.forecasts = ns.forecasts + 
             [ {"datetime": datetime_list | as_datetime,
             "temperature": temperature_list | float,
             "humidity": humidity_list | float,
             "precipitation": precipitation_list | float,
             "wind_bearing": wind_bearing_list | int,
             "wind_speed": wind_speed_list | float,
             "condition": condition_list} ] %}
        {% endif %}
      {% endfor %}
      {{ ns.forecasts }}

I have also tested changing the Time attribute to use the datetime attribute that is also available. That just made the empty space much bigger!

This is a list of every attribute from the Sensor. What I have done is copy and paste the attribute label to ensure that I got the case, spacing and additional punctation correct.

datetime: 2024-05-01 18:00:00
Month:  5  
Day:  1  
Time:  18  
WX Type 1: CLOUDY     
WX Type 2: DRIZZLE 
Temperature:  13.8  
Hi Temp:  13.8  
Low Temp:  13.8  
Rel.Hum.:  94.2  
Dew Pt.:  12.9  
Wet Bulb:  13.3  
Wind Spd.:  1.5  
Wind Dir.:  166  
Tot.Prcp:  0  
Snow Dpth:  0  
S.L.P.:  1013  
Stn.Pres.:  1005.2  
Wind Chl:  13.8  
Heat Ind:  13.8  
Vly Tmp:  14.4  
Hill Tmp:  13.2  
T_Lvl 1:  6.3  
T_850 mb: -2.5  
10-5 Thk:  5332.2  
LI: -1.1  
Sky Cov:  75  
SC L1:  75  
SC L2:  45  
SC L3:  0  
SC L4:  0  
SC L5:  0  
Vis Trans:  36.8  
VIS:  13.6  
Sun Alt:  21.6  
Solar Rad:  114.3  
UV Index:  0.2  
Freezing Level:  1176  
Snow Level:  1086  
Convection index:  0  
Severe index:  0  
1 min Gust:  1.5  
10 min Gust:  1.5  
1 hr Gust:  1.5  
6 hr Gust:  1.5  
FFDI:  0.8  
FWI:  4.1  
Average:  2.5  
WBGT:  15.7  
friendly_name: WXSIM

On further consideration, the mid-hour values are going to be an issue because of the if loop.index > now().hour + 1

I think you’re going to need to use the datetime, then try

{% set dt = datetime_list | as_datetime %} 
{% if dt > today_at( (now().hour + 1)|string~":00") and
dt.minute == 0 %}

Does that replace these lines?

      {% for datetime_list in state_attr('sensor.wxsim', 'Time') %}
        {% if loop.index > now().hour + 1 %}

The attribute in the for needs to be changed to datetime and the if line replaced:

    {% for datetime_list in state_attr('sensor.wxsim', 'datetime') %}
      {% set dt = datetime_list | as_datetime %} 
      {% if dt > today_at((now().hour + 1)|string~":00") and dt.minute == 0 %}

I changed it to this.

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "{{ state_attr('sensor.wxsim', 'WX Type 1') }}"
    temperature_template: "{{ states('sensor.meteobridge_air_temperature') | float }}" 
    temperature_unit: "°C"
    humidity_template: "{{ states('sensor.meteobridge_relative_humidity') | float }}"  
    visibility_template: "{{ states('sensor.meteobridge_visibility') | float }}"
    forecast_hourly_template: |
      {% set ns = namespace(forecasts=[]) %}
      {% for datetime_list in state_attr('sensor.wxsim', 'datetime') %}
        {% if loop.index > now().hour + 1 %}
          {% set temperature_list = state_attr('sensor.wxsim', 'Temperature')[loop.index0] %}
          {% set humidity_list = state_attr('sensor.wxsim', 'Rel.Hum.')[loop.index0] %}
          {% set precipitation_list = state_attr('sensor.wxsim', 'Tot.Prcp')[loop.index0] %}
          {% set wind_bearing_list = state_attr('sensor.wxsim', 'Wind Dir.')[loop.index0] %}
          {% set wind_speed_list = state_attr('sensor.wxsim', 'Wind Spd.')[loop.index0] %}
          {% set condition_list = state_attr('sensor.wxsim', 'WX Type 1')[loop.index0] %}
          
          {% set dt = datetime_list | as_datetime %} 
          {% if dt > today_at((now().hour + 1)|string~":00") and dt.minute == 0 %}
            {%  set ns.forecasts = ns.forecasts + 
               [{"datetime": dt,
               "temperature": temperature_list | float,
               "humidity": humidity_list | float,
               "precipitation": precipitation_list | float,
               "wind_bearing": wind_bearing_list | int,
               "wind_speed": wind_speed_list | float,
               "condition": condition_list}] %}
          {% endif %}
        {% endif %}
      {% endfor %}
      {{ ns.forecasts }}

And got the same result. Large block of empty space.
I am also having no luck eliminating the half hourly forecasts from the original source either!

You have to get rid of the index-based if.

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "{{ state_attr('sensor.wxsim', 'WX Type 1') }}"
    temperature_template: "{{ states('sensor.meteobridge_air_temperature') | float }}" 
    temperature_unit: "°C"
    humidity_template: "{{ states('sensor.meteobridge_relative_humidity') | float }}"  
    visibility_template: "{{ states('sensor.meteobridge_visibility') | float }}"
    forecast_hourly_template: |
      {% set ns = namespace(forecasts=[]) %}
      {% set wxsim_attr = states.sensor.wxsim.attributes %}
      {% for datetime in wxsim_attr['datetime'] | map('as_datetime') | map('as_local') | list %}
        {% if datetime > today_at((now().hour)|string~":00") and datetime.minute == 0 %}          
          {%  set ns.forecasts = ns.forecasts + 
            [{"datetime": datetime,
            "temperature": wxsim_attr['Temperature'][loop.index0] | float,
            "humidity": wxsim_attr['Rel.Hum.'][loop.index0] | float,
            "precipitation": wxsim_attr['Tot.Prcp'][loop.index0] | float,
            "wind_bearing": wxsim_attr['Wind Dir.'][loop.index0] | int,
            "wind_speed": wxsim_attr['Wind Spd.'][loop.index0] | float,
            "condition": wxsim_attr['WX Type 1'][loop.index0] } ] %}
        {% endif %}
      {% endfor %}
      {{ ns.forecasts }}

I thought that was it, but it shows an error.

AttributeError: ‘NoneType’ object has no attribute ‘tzinfo’

I presume that is where you have changed the time formatting section around line 12.

I found an old thread which you contributed on but I cannot translate your response to my template.

Paste this is the Template editor and let us know what it returns:

{{ state_attr('sensor.wxsim', 'datetime')[0] + timedelta(hours=1) }}

This is what comes back.

TypeError: can only concatenate str (not “datetime.timedelta”) to str

That’s what I expected, but it means that I’m nearly stumped. From the earlier error it seems that there are some values in the datetime attribute’s list that are returning none, so we could try filtering those out as well in the if

weather:
  - platform: template
    name: "WXSIM"
    condition_template: "{{ state_attr('sensor.wxsim', 'WX Type 1') }}"
    temperature_template: "{{ states('sensor.meteobridge_air_temperature') | float }}" 
    temperature_unit: "°C"
    humidity_template: "{{ states('sensor.meteobridge_relative_humidity') | float }}"  
    visibility_template: "{{ states('sensor.meteobridge_visibility') | float }}"
    forecast_hourly_template: |
      {% set ns = namespace(forecasts=[]) %}
      {% set wxsim_attr = states.sensor.wxsim.attributes %}
      {% for datetime in wxsim_attr['datetime'] %}
        {% if (datetime is not none) and 
        (datetime|as_datetime|as_local > today_at((now().hour)|string~":00")) and 
        ((datetime|as_datetime|as_local).minute == 0) %}          
          {%  set ns.forecasts = ns.forecasts + 
            [{"datetime": datetime,
            "temperature": wxsim_attr['Temperature'][loop.index0] | float,
            "humidity": wxsim_attr['Rel.Hum.'][loop.index0] | float,
            "precipitation": wxsim_attr['Tot.Prcp'][loop.index0] | float,
            "wind_bearing": wxsim_attr['Wind Dir.'][loop.index0] | int,
            "wind_speed": wxsim_attr['Wind Spd.'][loop.index0] | float,
            "condition": wxsim_attr['WX Type 1'][loop.index0] } ] %}
        {% endif %}
      {% endfor %}
      {{ ns.forecasts }}

Same result with that updated code unfortunately.

I have been able to remove the 30 minute forecast from the source, so it is now just hourly.

With regards to the timestamp information, that might be my fault.
The original source as generated by the app is a CSV file. I then convert that to JSON. The source CSV doesn’t have a complete timestamp - it is all separate fields: Year ,Month ,Day ,Time - where time is just the two digit time in 24 hour version (13,14, 15 etc). When I started on this process I read that the timestamp was important and it had to be in a certain format, so made my conversion script create the timestamp. As such, it could be the formatting I am creating. Is there a preferred format?

I hadn’t considered that… you may have cracked it.

It looks like format used for datetime in other weather forecasts is ISO 8601 format i.e. 2024-05-02T00:00:00+00:00 (always in UTC). Can you get the datetime attributes’ list items in that format or will it need to be done through templating?