Jinja2 macro in template

Hello, I am playing with template and Jinja2 but I’m a really beginner and I’m try to learn taking some ideas and code example around.
I wrote this (incomplete) piece of code which works perfectly:

- platform: template
  sensors:
    forecast_1:
      friendly_name_template: >
        {%- set date = as_timestamp(now()) + (1 * 86400 ) -%}
        {% set day = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] %}
        {% set month = ["January", "February", "Marsh", "April", "May", "june", "July", "August", "September", "October", "November", "December"] %}
        {% set m_ok = date | timestamp_custom("%m") | int %}
        {% set g_ok = date | timestamp_custom("%w") | int %}
        {{day[g_ok] + ' ' + date | timestamp_custom("%d") + ' ' + month[m_ok - 1]}}
      value_template: "{{states.sensor.dark_sky_icon_1.state}}"
      icon_template: >-
        {% if states.sensor.dark_sky_icon_1.state == "clear-day" %}
          mdi:weather-sunny
        {% elif states.sensor.dark_sky_icon_1.state == "partly-cloudy-day" or states.sensor.dark_sky_icon_1.state == "partly-cloudy-night"%}
          mdi:weather-partlycloudy
        {% elif states.sensor.dark_sky_icon_1.state == "snow" %}
          mdi:weather-snowy
        {% elif states.sensor.dark_sky_icon_1.state == "fog" %}
          mdi:weather-fog
        {% else %}
          mdi:help-circle
        {%- endif %}

Because I have more forecast_n sensor I have to repeat that code for each sensor especially for the icon_template section. I was wondering if I could use a Jinja2 macro but I have no idea how to place it in the code.

Thank You for any suggestion

unfortunately you have to repeat the same section of code many times if they aren’t in the same attribute. Macro’s don’t transfer between them because they are in their own environment. It’s super annoying.

But just to let you know, there are much better ways to do what you want to do. First, you can use strfttime or even custom timestamp to get your days and months. The only reason people use a list for days and months is when they aren’t dealing with english.

%A gives day
%B gives month

All these attributes are documented in python here, you just need to scroll to the grid at the bottom of the page.

Also, if you want to shorten up the number of locations that you are changing 1 to 2, you can make a dictionary that that maps values to other values instead of using if statements. This will make it so you only need to change the number in the fewest amount of places.

- platform: template
  sensors:
    forecast_1:
      friendly_name_template: >
        {{ (as_timestamp(now()) + (1 * 86400)) | timestamp_custom('%A %d %B') }}
      value_template: >
        {{ states('sensor.dark_sky_icon_1') }}
      icon_template: >
        {%- set current = states('sensor.dark_sky_icon_1') %}
        {%- set mapper = {'clear-day':'sunny', 'partly-cloudy-day':'partlycloudy', 'partly-cloudy-night':'partlycloudy', 'snow':'snowy', 'fog':'fog'} %}
        {{ 'mdi:weather-'+mapper[current] if current in mapper else 'mdi:help-circle' %}

EDIT: changed friendly_name_template based on conversation below

So, you’d just copy and paste the code and replace the 2 dark_sky sensor names. Also, if you know python, you can just make a string format and have it write the code for you.

If you have questions about what is being done, i’m glad to answer them. Some of the code in this is advanced.

2 Likes

Thank You for your reply.
I got the shortcut given by using the dictionary mapper

For the first part of the code

    friendly_name_template: >
            {{ as_timestamp(now()) + (1 * 86400) | timestamp_custom('%A %d %B') }}

I remember to have tried something similar but I gave up because I got some error similiar to the error I get using the code you wrote:

2019-01-24 19:21:10 INFO (MainThread) [homeassistant.core] Starting Home Assistant
2019-01-24 19:21:10 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.forecast_1 fails
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 221, in async_update_ha_state
    await self.async_device_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 347, in async_device_update
    await self.async_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/sensor/template.py", line 215, in async_update
    setattr(self, property_name, template.async_render())
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/template.py", line 138, in async_render
    return self._compiled.render(kwargs).strip()
  File "/usr/local/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
TypeError: unsupported operand type(s) for +: 'float' and 'str'

If I have to be honest, I don’t know python a lot but my best difficulty is understand how Python and Jinja2 ‘intersect’ home-assistant configuration code. :sweat:

Thats odd, there aren’t any floats here.

Put this template in the template designer

        {%- set current = states('sensor.dark_sky_icon_1') %}
        {%- set mapper = {'clear-day':'sunny', 'partly-cloudy-day':'partlycloudy', 'partly-cloudy-night':'partlycloudy', 'snow':'snowy', 'fog':'fog'} %}
        {{ 'mdi:weather-'+mapper[current] if current in mapper else 'mdi:help-circle' %}

That works fine (except for the missing ‘}’ instead of ‘%’ at the end ot the last line).
The error I got comes from the first one:

{{ as_timestamp(now()) + (1 * 86400) | timestamp_custom('%A %d %B') }}

bah, the filter isn’t getting applied to the entire function.

This shoudl work

{{ ( as_timestamp(now()) + (1 * 86400) ) | timestamp_custom('%A %d %B') }}
1 Like

Yes, it works perfectly!
Thank You a lot.