I thought I'd share this tip, since I just found it a few weeks ago, and I thought maybe newer people might have overlooked the value of it. You can create a custom macro template for things that you use more than once.
For example, I use the sports tracker integration to track three teams. Then, I have conditional cards that start to appear the day before a game and stay visible through 16 hours after the game starts (i.e., showing the final score for a few hours into the next day).
This is a fairly complex example, but the complexity also demonstrates why you'd want to extract this out to a macro. Imagine having this repeated in 3 places (in my case), then realizing there was a bug, or you could improve it by just changing one little thing...With the macro, edit it in one place, reload the macro template, and boom, the change shows up everywhere you use the macro!
In /homeassistant/custom_templates/sports.jinja (I believe I had to create that directory, and the file), I have this code:
{% macro format_date(entity_id, interval) %}
{% if is_state(entity_id, "POST") %}
{{ state_attr(entity_id, "clock") }}
{% else %}
{% if is_state(entity_id, "PRE") %}
{% if as_timestamp(state_attr(entity_id, 'date')) | timestamp_custom("%d") == as_timestamp(now()) | timestamp_custom("%d") %}
{{ as_timestamp(state_attr(entity_id, 'date')) | timestamp_custom("Today at %-I:%M %p") }}
{% elif as_timestamp(state_attr(entity_id, 'date')) | timestamp_custom("%d") == as_timestamp(now()+ timedelta(days = 1)) | timestamp_custom("%d") %}
{{ as_timestamp(state_attr(entity_id, 'date')) | timestamp_custom("Tomorrow at %-I:%M %p") }}
{% else %}
{{ as_timestamp(state_attr(entity_id, 'date')) | timestamp_custom("%A at %-I:%M %p") }}
{% endif %}
{% else %}
{{ state_attr(entity_id, "clock") }} {{ interval }}
{% endif %}
{% endif %}
{% endmacro %}
The macro line has two variables specified - the entity to use (in my case, sensor.capitals from the integration), and the "interval", which is only used to fill in whether it's a "period" or a "quarter" for the game. (I could have if/thens to figure it out from the attribute data, but just passing it in is really easy.)
There's a lot going on in this macro, mostly specific to the sports integration, but here's an overview:
- If it's postgame, then just put up the "clock" attribute, which will have something like "Final" or "Final/OT", etc.
- If it's pregame, then it checks if the game is today, and if so, it says "Today at 7:00 pm" or whatever. If it's tomorrow, it says, "Tomorrow at 7". If it's beyond tomorrow, it gives the day of the week and the time.
- If it's during the game, which is the only other option, put up the current clock info and whether it's a period or quarter.
Next, let's look at how to use it. Here's the yaml for the hockey team card:
- type: custom:mushroom-template-card
primary: >-
{{ state_attr("sensor.capitals", "team_abbr") }} {{
state_attr("sensor.capitals", "team_score") }} - {{
state_attr("sensor.capitals", "opponent_abbr") }} {{
state_attr("sensor.capitals", "opponent_score") }}
icon: mdi:hockey-puck
features_position: bottom
entity: sensor.template_away_team
visibility:
- condition: or
conditions:
- condition: state
entity: sensor.capitals
state: IN
- condition: and
conditions:
- condition: state
entity: sensor.capitals
state: POST
- condition: numeric_state
entity: sensor.caps_hours_until_next_game
above: -16
- condition: and
conditions:
- condition: state
entity: sensor.capitals
state: PRE
- condition: numeric_state
entity: sensor.caps_hours_until_next_game
below: 49
secondary: |-
{% from 'sports.jinja' import format_date %}
{{ format_date('sensor.capitals', 'period') }}
picture: "{{ state_attr(\"sensor.capitals\", \"team_logo\") }}"
The primary shows the score and the teams, and you can see the visibility that I hopefully described correctly before. But, look at the secondary spec: The first line tells it what file you saved that jinja code in and the macro you want to use, and the second line calls the macro.
That's it!
And if I want to change it, I only have to change it in one place, and then remember to re-load the custom templates by invoking the "homeassistant.reload_custom_templates" action (note this doesn't show up on the YAML page on the developer tools - you have to trigger it on the Actions page).
Note that I'm using it in the secondary info, but it could be used anywhere that takes a template - such as the primary in this card. For example, I could write another macro to format the teams such that the home team is always second...and I probably will at some point.
This info is in the Reusing Templates section of the Templating page on HA's website, but I overlooked it for a long time. Hopefully this tip helps someone else.
Where it would have been really helpful is that I have quite a few sensors that measure how many days since I changed water filters, cleaned the pet's recirculating water bowl, did flea and tick treatment on the pets, exercised the generator, drained and repressurized the pressure tank, changed the batteries in my pinball machine, etc. etc. etc.
So, I have quite a few templates that use a helper for the last date I did something, then count how many days since then...and they're all coded independently in my configuration.yaml (these were done before the UI helper interface was as useful as it is now). And even though all of those do essentially the same thing, I coded some of them differently for various reasons along the way. It's a mess, no question. I could recreate all of them with one short macro.
Had I noticed this macro ability a few years ago, when I first started, I could have saved myself a lot of headache!