Beginner's templating question

Hi

It’s my first time I’m playing around with templates so please be patient with me… :wink:

What I wanted to do, is to figure out the day’s min and max temperatures from the weather forecast (OWM).

What I’ve learned today is to extract timestamp and temperature from the entities attributes:

{% for i in state_attr('weather.openweathermap', 'forecast') -%}
  Timestamp: {{ (i.datetime//1000) | timestamp_local }} Temperature: {{ i.temperature }}
{% endfor %}

The result looks like this:

Timestamp: 2020-03-31 17:00:00 Temperature: 9.2
Timestamp: 2020-03-31 20:00:00 Temperature: 4.4
Timestamp: 2020-03-31 23:00:00 Temperature: 2.1
Timestamp: 2020-04-01 02:00:00 Temperature: 0.3
Timestamp: 2020-04-01 05:00:00 Temperature: -0.9
Timestamp: 2020-04-01 08:00:00 Temperature: 0.1
Timestamp: 2020-04-01 11:00:00 Temperature: 5.6
Timestamp: 2020-04-01 14:00:00 Temperature: 9.1
Timestamp: 2020-04-01 17:00:00 Temperature: 9.7
Timestamp: 2020-04-01 20:00:00 Temperature: 4.3
Timestamp: 2020-04-01 23:00:00 Temperature: 2.5
Timestamp: 2020-04-02 02:00:00 Temperature: 1.2
Timestamp: 2020-04-02 05:00:00 Temperature: 0.3
Timestamp: 2020-04-02 08:00:00 Temperature: 1.5
Timestamp: 2020-04-02 11:00:00 Temperature: 8.5
Timestamp: 2020-04-02 14:00:00 Temperature: 10.4
....

But now I’m stuck. How do I extract only the current day? And how do I find the min and max temperature of that day?

A little help which pushes me a bit forward would be very appreciated! Thx!

2 Likes

It’s a big step I’d say. Read this to understand a bit what’s going on.

Ok, first of all you’ll need to filter only today’s data - it can be done by comparing timestamps.
I decided to get today’s midnight from now() and calculate tomorrow by adding number of seconds in 24 hours.
Then you need to detect if your temperature is max or min and store it safely (the trouble here is Jinja discards everything when you leave the block of code so without some magic you won’t be able to preserve those min and max.

Here’s the code

{% set today = now().replace(hour=0,minute=0) | as_timestamp %}
{% set tomorrow = today + 86400 %}
{% set ns = namespace(min_temp=100, max_temp=-100) %}
{% for i in state_attr('weather.openweathermap', 'forecast') -%}
  {% if today <= i.datetime // 1000 < tomorrow %}
    {% if i.temperature < ns.min_temp %}
      {% set ns.min_temp = i.temperature %}
    {% endif %}
    {% if i.temperature > ns.max_temp %}
      {% set ns.max_temp = i.temperature %}
    {% endif %}
  {% endif %}
{% endfor %}
{{ns.min_temp}}
{{ns.max_temp}}

The question is how you’re going tol use it.

Well I wanted to make first attempts in templating. But if I see your code I should have chosen a simpler object for practice!

At the end I would like to have a template sensor for the day’s min and max temperature. That is the goal!

I’ll have a closer look at your code as soon as I have some time. Thx!

take a look here

that’s not your goal. your goal is something that you can see. so what is it? min and max temperatures?

Can you post the contents of the forecast attribute (as text, not a screenshot)? I’d like to see how it’s structured.

Why not just create a temperature sensor.
When it changes run 2 scripts one to check if it’s lower than ‘lowvalue’ and the other to check if it’s higher than ‘highvalue’ and if they are update them

it’s an array of records, each has timestamp, temp, precipitation and condition

[{'datetime': 1585785600000, 'temperature': 5.8, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585796400000, 'temperature': 4.5, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585807200000, 'temperature': 3.3, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585818000000, 'temperature': 6.4, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585828800000, 'temperature': 11.2, 'precipitation': 0.1, 'condition': 'rainy'}, {'datetime': 1585839600000, 'temperature': 11.2, 'precipitation': 0.1, 'condition': 'rainy'}, {'datetime': 1585850400000, 'temperature': 9.7, 'precipitation': 0.3, 'condition': 'rainy'}, {'datetime': 1585861200000, 'temperature': 7.5, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585872000000, 'temperature': 6.9, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585882800000, 'temperature': 5.4, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585893600000, 'temperature': 4.4, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585904400000, 'temperature': 7.3, 'precipitation': None, 'condition': 'partlycloudy'}, {'datetime': 1585915200000, 'temperature': 8.2, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585926000000, 'temperature': 8.7, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585936800000, 'temperature': 7.7, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585947600000, 'temperature': 6.3, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585958400000, 'temperature': 5.9, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585969200000, 'temperature': 6.1, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585980000000, 'temperature': 5.5, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1585990800000, 'temperature': 9.2, 'precipitation': None, 'condition': 'partlycloudy'}, {'datetime': 1586001600000, 'temperature': 11.5, 'precipitation': None, 'condition': 'partlycloudy'}, {'datetime': 1586012400000, 'temperature': 12.3, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1586023200000, 'temperature': 10.2, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1586034000000, 'temperature': 8.5, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586044800000, 'temperature': 8.1, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586055600000, 'temperature': 7.5, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586066400000, 'temperature': 6.9, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586077200000, 'temperature': 11.7, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586088000000, 'temperature': 15.3, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586098800000, 'temperature': 16.1, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586109600000, 'temperature': 14.9, 'precipitation': None, 'condition': 'sunny'}, {'datetime': 1586120400000, 'temperature': 13.6, 'precipitation': None, 'condition': 'partlycloudy'}, {'datetime': 1586131200000, 'temperature': 12.1, 'precipitation': 0.1, 'condition': 'rainy'}, {'datetime': 1586142000000, 'temperature': 11.3, 'precipitation': 2.3, 'condition': 'rainy'}, {'datetime': 1586152800000, 'temperature': 10.9, 'precipitation': 1.2, 'condition': 'rainy'}, {'datetime': 1586163600000, 'temperature': 12.4, 'precipitation': 0.6, 'condition': 'rainy'}, {'datetime': 1586174400000, 'temperature': 14.5, 'precipitation': None, 'condition': 'cloudy'}, {'datetime': 1586185200000, 'temperature': 14.6, 'precipitation': None, 'condition': 'partlycloudy'}, {'datetime': 1586196000000, 'temperature': 11.9, 'precipitation': None, 'condition': 'partlycloudy'}, {'datetime': 1586206800000, 'temperature': 9.2, 'precipitation': None, 'condition': 'partlycloudy'}]
1 Like

Yes. At the end I would like to have min/max sensors for temperature, wind speed, rain etc. to trigger certain actions. For example close shutter if temp max is > 21°C.

This is how it looks like:

temperature: 3
humidity: 64
pressure: 1028
wind_bearing: 50
wind_speed: 18.36
attribution: Data provided by OpenWeatherMap
forecast:
  - datetime: 1585645200000
    temperature: 5.5
    precipitation: null
    condition: cloudy
  - datetime: 1585656000000
    temperature: 8
    precipitation: null
    condition: partlycloudy
  - datetime: 1585666800000
    temperature: 8.4
    precipitation: null
    condition: sunny
  - datetime: 1585677600000
    temperature: 3.7
    precipitation: null
    condition: sunny
  - datetime: 1585688400000
    temperature: 1.3
    precipitation: null
    condition: sunny
  - datetime: 1585699200000
    temperature: -0.1
    precipitation: null
    condition: sunny
  - datetime: 1585710000000
    temperature: -1.1
    precipitation: null
    condition: sunny
  - datetime: 1585720800000
    temperature: -0.3
    precipitation: null
    condition: partlycloudy
  - datetime: 1585731600000
    temperature: 5.4
    precipitation: null
    condition: partlycloudy
  - datetime: 1585742400000
    temperature: 9
    precipitation: null
    condition: partlycloudy
  - datetime: 1585753200000
    temperature: 9.7
    precipitation: null
    condition: sunny
  - datetime: 1585764000000
    temperature: 4.2
    precipitation: null
    condition: sunny
  - datetime: 1585774800000
    temperature: 2.3
    precipitation: null
    condition: sunny
  - datetime: 1585785600000
    temperature: 1.2
    precipitation: null
    condition: sunny
  - datetime: 1585796400000
    temperature: 0.2
    precipitation: null
    condition: sunny
  - datetime: 1585807200000
    temperature: 1.4
    precipitation: null
    condition: sunny
  - datetime: 1585818000000
    temperature: 8.4
    precipitation: null
    condition: partlycloudy
  - datetime: 1585828800000
    temperature: 10.6
    precipitation: null
    condition: partlycloudy
  - datetime: 1585839600000
    temperature: 10.5
    precipitation: null
    condition: cloudy
  - datetime: 1585850400000
    temperature: 5.9
    precipitation: null
    condition: cloudy
  - datetime: 1585861200000
    temperature: 3.7
    precipitation: null
    condition: cloudy
  - datetime: 1585872000000
    temperature: 3
    precipitation: null
    condition: cloudy
  - datetime: 1585882800000
    temperature: 2
    precipitation: null
    condition: partlycloudy
  - datetime: 1585893600000
    temperature: 3.8
    precipitation: null
    condition: partlycloudy
  - datetime: 1585904400000
    temperature: 8.9
    precipitation: null
    condition: cloudy
  - datetime: 1585915200000
    temperature: 11.8
    precipitation: 0.3
    condition: rainy
  - datetime: 1585926000000
    temperature: 11.2
    precipitation: 0.2
    condition: rainy
  - datetime: 1585936800000
    temperature: 7.8
    precipitation: 0.1
    condition: rainy
  - datetime: 1585947600000
    temperature: 5.5
    precipitation: null
    condition: cloudy
  - datetime: 1585958400000
    temperature: 4.9
    precipitation: null
    condition: partlycloudy
  - datetime: 1585969200000
    temperature: 3
    precipitation: null
    condition: partlycloudy
  - datetime: 1585980000000
    temperature: 4
    precipitation: null
    condition: sunny
  - datetime: 1585990800000
    temperature: 10
    precipitation: null
    condition: sunny
  - datetime: 1586001600000
    temperature: 13.5
    precipitation: null
    condition: partlycloudy
  - datetime: 1586012400000
    temperature: 14.9
    precipitation: null
    condition: sunny
  - datetime: 1586023200000
    temperature: 10.3
    precipitation: null
    condition: partlycloudy
  - datetime: 1586034000000
    temperature: 8.1
    precipitation: null
    condition: sunny
  - datetime: 1586044800000
    temperature: 6.7
    precipitation: null
    condition: sunny
  - datetime: 1586055600000
    temperature: 5.7
    precipitation: null
    condition: sunny
  - datetime: 1586066400000
    temperature: 6.4
    precipitation: null
    condition: sunny
friendly_name: OpenWeatherMap
1 Like

Well I’m interested in the forecast. For example close shutters at 8 o’clock if the day max temp is above 21°C.

That’s why I asked. Before coding it’s great to describe your goal as clear as possible. Then choose how to achieve it.

In your case you’ll need more than one sensor so you don’t need one template that does everything.
One option is to create as many template sensors as you like and in each one use a shortened version that extracts only data that’s required - in case of Min temp you might remove all related to ns.max_temp from its template.

Yes, there will be a lot of similar code in your templates but there’s little you can do about it.

You can try to create one template sensor with additional attributes - it might be sensible if you don’t use your sensors/attributes directly but create binary template sensors like binary_sensor.max_temp_higher_than. The advantage is it’s like one sensor but it holds all relevant data so if you need to change/add something, it’s the same entity and you know where it is.

I’d suggest to start with separate template sensors and see if it works for you. When you have more experience and are more familiar with Jinja/tmplate sensors and how HA works in general, you may have a better idea and refactor your config accordingly.

Sorry, but you do know that you can just simply track a value and close the shutters if it goes above 21" and open them if if goes below 20.5" (say)
Or have blinds that come down to 50% if the sun is between 160" and 200" azimuth with (say) an elevation below 40"
Why do you need max and mins ?

Here the TS deals with weather forecast so I presume he’s getting ready for a sunny day or something.

Yes I know. But I am interested in the forecast. For example, I would like to close the shutters of the greenhouse at 8 o’clock in the morning if it’s getting more than 21°C that day. If I close them if it is already 21°C it’s kind of too late…



EDIT

See petro’s template in the next post. It uses a far more streamlined approach than what I presented below. I’ll leave this here for the curious but I recommend using petro’s template.



After a bit of experimentation (and discovering a few things you cannot do in Jinja2) I’ve created the following template to extract the current day’s minimum and maximum temperatures. Basically, it does the same thing AhmadK’s example did (iterates through the forecast data) but uses a different technique to determine the minimum and maximum values.

To experiment with it, go to Developer Tools > Templates and paste the following into the Template Editor:

{% set start = (now().replace(hour=0,minute=0,second=0).timestamp() * 1000) | int %}
{% set end = start + 86400000 %}
{% set ns = namespace(x='') %}

{% for d in state_attr('weather.openweathermap', 'forecast') if start <= d.datetime < end %}
  {% set ns.x = ns.x ~ d.temperature ~ ' ' %}
{% endfor %}
{% set temps = ns.x.split() | map("float") | list %}

Minimum: {{ temps | min }}
Maximum: {{ temps | max }}

I don’t have an OpenWeatherMap weather entity but I simulated it and the template produced today’s (April 2) minimum and maximum values using the sample data you provided.

To use it, create two Template Sensors, one for maximum and another for minimum. Here’s an example of a maximum sensor. The minimum version’s template would be 99% the same except the final filter would be min instead of max.

  - platform: template
    sensors:
      today_max_temp:
        friendly_name: 'Today Maximum Temperature'
        unit_of_measurement: '°C'
        value_template: >-
          {% set start = (now().replace(hour=0,minute=0,second=0).timestamp() * 1000) | int %}
          {% set end = start + 86400000 %}
          {% set ns = namespace(x='') %}
          {% for d in state_attr('weather.openweathermap', 'forecast') if start <= d.datetime < end %}
            {% set ns.x = ns.x ~ d.temperature ~ ' ' %}
          {% endfor %}
          {{ ns.x.split() | map("float") | list | max }}

How the template works:

  1. It creates two timestamps representing the start and end of the current day. It’s multiplied by 1000 so that it matches OpenWeathermap’s datetime format.
  2. It create a global string variable called ns.x. This string will collect all of today’s temperatures (delimited by a space).
  3. The forecast attribute contains a list. The for-loop iterates through that list but only for the items whose datetime falls between start and end. In other words, only for items containing today’s weather data.
  4. A matching item’s temperature value is appended to the ns.x string variable along with a space character (to serve as a delimiter).
  5. Finally, the ns.x string is converted to a list and the max filter is used to find the highest value in the list. The conversion is a multi-step process consisting of:
    • Splitting the string. This creates a list where each item within the list is a string.
    • Converting the list’s items from string to float (i.e. converting string values to numeric values).
    • Converting the mapped result back to a list.
    • Finally, finding the highest value in the list.
1 Like

you can do this with generators and without the namespace btw

{% set start = (now().replace(hour=0,minute=0,second=0).timestamp() * 1000) | int %}
{% set end = start + 86400000 %}
{{ state_attr('weather.openweathermap', 'forecast') | selectattr('datetime', '>', start) | selectattr('datetime','<=', end) | map(attribute='temperature') | list | max }}

not as easy to read but for learning purposes. Personally, I find generators easy to work with once you get the hang of them.

7 Likes

:man_facepalming:

:clap:

Noice!


EDIT
Frankly, I find your template easier to read than the one I created! A clean left-to-right progression of data running through a series of filters to produce the one thing we want. So neat. I didn’t think this one was possible without iterating though it.

This is amazing!!! I am deeply impressed!!!

I did not understand your code at first because I did not know, that it is possible to use python functions and syntax. For example, I have read about replace filter of Jinja. But I did not know, that it is perfectly fine to use Python’s “hour=0,minute=0,second=0” syntax. Same thing for the timestamp() python function. Where can I find the information on what Python functions I can use within Jinja templates?

I think I do understand the rest of your code now. But I would have never been able to write it myself!

If you have a good source of information about Jinja beside the official docs I would appreciate that!

Thank you very much for the code!

:+1:

There isn’t any. These objects are just passed up to jinja. If you know the object type you are dealing with, most properties and methods are available. Not all things work though. Kinda just have to guess and check.

the normal ansible documentation or the jinja2 website have pretty much everything jinja related. Not everything on the ansible page works though because we are not up to date.

Ok. Thx again. I will try to read and dig in a little deeper… :slightly_smiling_face: