Hi,
is there any way to add custom filters in the Jinja templating language used in ha?
Thank you
Hi,
is there any way to add custom filters in the Jinja templating language used in ha?
Thank you
Nope, you’d have to overwrite the helpers/template.py file. What are you trying to do? Chances are, there’s already a filter or a better way to do what you want, unless it’s extremely specific.
Hi petro,
thank you for your answer.
I’d like to have a custom version of “relative_time” filter,
with output in Italian and with always hours and minutes stated.
for example: my_timestamp | my_custom_relative_time would
output "3 hours and 15 minutes"
(in Italian).
Is there any smarter way to obtain this result?
Since I would use this in several templates, I do not want to calculate this message in each template.
Thank you
HI petro,
do you have any hint for me?
Thank you.
Matteo
Hello,
I’m also looking for a way to implement custom filters for a specific purpose. To calculate the absolute humidity from temperature and humidity sensors I need to use the math.exp function.
I found a HACS plugin where someone added their own filters, maybe this is something to build on.
I recently made some updates to this HACS integration (originally built by zvldz):
‣ Custom Filters for Jinja (Home Assistant)
The most recent updates fix all of the issues I was experiencing (e.g. it was not loading consistently on restart). It works flawlessly now (for me, at least).
You will have to write any additional custom filters in Python, but it comes with a handful of useful filters by default. Below is a list of the filters that come built-in with the integration, including documentation (description, usage, etc). Enjoy!
replace_all - Replace all provided values with replacement value(s) [e.g. `replace_all("A B C D", ["B", "D"], "?")` => "`A ? C ?`"]
is_defined - Check if a variable is defined by it's string name [e.g. `is_defined('varname')`]
get_type - Return the object type (as a string) [e.g. `get_type(['A','B'])` => "`list`"]
is_type - Check if a value is of given type [e.g. `is_type('str', 'float')` => `true`]
inflate - Compress with zlib
deflate - Decompress with zlib
decode_base64_and_inflate - Decode base64 content and decompress it
deflate_and_base64_encode - Compress content and base64 encode it
decode_valetudo_map - Decode Valetudo (https://github.com/Hypfer/Valetudo) map
urldecode - Decode URL characters (replace %xx escapes by their single-character equivalent)
strtolist - Turn a string into a list [e.g. `strtolist("['A','B','C']") | length` => `3`]
listify - Convert a string or non-list/dict into a list/dict (like JSON string)
get_index - Return the numeric index of a list or dict item [e.g. `get_index(['A','B','C'], 'A')` => `0`]
grab - Get a list/dict item by key, with optional fallback [e.g. `grab(['A','B','C'], 4, 'Z')` => "`Z`"]
reach - Get a dict item by full path of key(s), with optional fallback [e.g. `reach({"a": {"b": "c"}}, "a.b")` => "`c`"]
ternary - Returns one value if true, another if false, and third if null [e.g. `ternary(("a" == "b"), "Y", "N", "U")` => "`N`"]
shuffle - Randomize an existing list, giving a different order every invocation [e.g. `shuffle([1,2,3])` => `[2,1,3]`]
to_ascii_json - Convert string to ASCII JSON
This is working well for me, thanks. I was able to add my own filter as well.
That’s great to hear! Thanks for reporting back on this. Feel free to share any custom filters (or contribute on GitHub) if you think they’d be useful to others.
This is an obscure and narrow use case.
Thanks again.
@blizzrdof77 maybe you can help with this. I installed the add-on and added a filter. I can successfully use the filter in the template playground in the Developer tools as shown
flt_2_ieee
is not defined. Here’s the script:
modbus_brite_22_set_temp:
alias: Modbus Brite_22 set temp
sequence:
- service: modbus.write_register
data:
value: >
{{flt_2_ieee(temp)}}
hub: modbus_hub
unit: 2
address: 00
Can you think of reason it would work in one and not the other?
Hmm… I’d have to see your code. Have you tried using any of the other filters within the same context?
Why are you using this to create a function but not using it as a filter? If you’re going to use it as a function only, there’s no reason to use this custom library. Just make a macro.
From my testing, template macros cannot import. I need import struct
for this filter.
… why. There are HA built-in template functions/filters that encode/decode information. You shouldn’t need struct at all
Here the code (don’t laugh too hard - I’m new to python. Snippets around each mention of my filter. From __init__.py
import struct
from random import Random, SystemRandom, shuffle
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
_TemplateEnvironment = template.TemplateEnvironment
## Greg Martin ieee conversion
def flt_2_ieee(f):
"""converts float to ieee754 returned in two part hex"""
binary_string = ''.join(f'{b:08b}' for b in struct.pack('>f', f))
ieeehex =hex(int(binary_string, 2))
ieeelt = ieeehex[2:6]
ieeert = ieeehex[6:10]
retval = "[0x" + ieeelt + ",0x" + ieeert + "]"
return retval
...
def init(*args):
"""Initialize filters"""
env = _TemplateEnvironment(*args)
env.filters["flt_2_ieee"] = flt_2_ieee
env.filters["replace_all"] = replace_all
...
env.filters["shuffle"] = shuffle
env.filters["to_ascii_json"] = to_ascii_json
env.globals["flt_2_ieee"] = flt_2_ieee
env.globals["replace_all"] = replace_all
...
template.TemplateEnvironment = init
template._NO_HASS_ENV.filters["flt_2_ieee"] = flt_2_ieee
template._NO_HASS_ENV.filters["replace_all"] = replace_all
...
template._NO_HASS_ENV.filters["to_ascii_json"] = to_ascii_json
template._NO_HASS_ENV.globals["flt_2_ieee"] = flt_2_ieee
template._NO_HASS_ENV.globals["replace_all"] = replace_all
...
async def async_setup(hass, hass_config):
tpl = template.Template("", template._NO_HASS_ENV.hass)
tpl._env.globals = flt_2_ieee
tpl._env.globals = replace_all
I’m no python coder, but I couldn’t find a way to use pack
. Code posted. Happy to solve this with a macro. This code has to convert a float into a string of the two hex values for a ieee 754 representation of the float. The brackets are for passing the two valuse to a service call using a list
so 54.1 would return [0x4258,0x6666]
Here’s a curious thing (to me). If I call the filter as a function (as @petro pointed out), it works in the template playground. If I call is as a filter it fails with no filter named flt_2_ieee
.
And I get the same behavior with get_type
from the custom filter package. Looking at the docs, all the examples use the filters as functions, not pipeline filters.
yeah, that’s all built in…
{% set value = 54.1 %}
{% set a = '0x%X' % value | pack('>f') | unpack('>H') %}
{% set b = '0x%X' % value | pack('>f') | unpack('>H', offset=2) %}
[{{ a }}, {{ b }}]
In fact, most of the filters in that lib aren’t necessary and it can all be done in jinja
EDIT: if you want it as a proper macro
{%- macro flt_2_ieee(f) %}
{%- set a = f | pack('>f') | unpack('>H') %}
{%- set b = f | pack('>f') | unpack('>H', offset=2) %}
{{- "[ 0x%X, 0x%X ]" % (a, b) }}
{%- endmacro %}
Just keep in mind that the template editor will turn it into a list of int’s when you try to view the result.
Covered here in the documentation:
Wow - great work! Thanks so much.
The ints work as well. I’ll give this a shot.