Jinja experts: Better way to extract part of an MQTT topic name with template?

Hi all-

I’m very new to templates, so I’m hoping you can help.

I want to extract a portion of an MQTT topic name such as light_status_office/tele_ls/LWT or light_status_kitchen/tele_ls/LWT. I want to get the portion after light_status_ and before /tele_ls/LWT, for example, office or kitchen.

So far, I’m doing that as below, where topic3 = office. This works in dev_templates, but it seems kludgey.

{% set topic = "light_status_office/tele_ls/LWT" %}

{% set topic2 = topic.split("/")[0] %}
{% set topic3 = topic2.split("light_status_")[-1] %}
{{ topic3 }}

Is split the most appropriate filter to use for this? Can splits be nested, for example, so that there would be no need for topic3?

Overall, is there a better way to achieve the goal?

Thanks very much!


this will work:

{% set topic = (("light_status_office/tele_ls/LWT").split("/")[0]).split("light_status_")[-1] %}

sometimes “kludgey” is the best you can hope for. :slightly_smiling_face:

1 Like

Thanks! I’ll give it a try.

Actually, I reworked it a bit and I came up with this:

{% set topic = (("light_status_office/tele_ls/LWT").split("_")[2]).split("/")[0] %}

It still uses the same number of nested splits but it’s a bit cleaner.

1 Like

Thanks again. One thing I didn’t mention… Some of the room names I need to extract have an underscore in them, such as master_bedroom. (None have more than one underscore.)

So, while the first suggestion works, the second chops off the second word of the room name. Is there a way to control for that case?

This is a huge help in understanding how split works.

try this then:

{% set topic = ("light_status_master_bedroom/tele_ls/LWT").split("/")[0] %}
{% if topic.split("_")[3] is defined %}
  {% set topic_split = (topic.split("_")[2]) + " " +(topic.split("_")[3])%}
{% else %}
  {% set topic_split = (topic.split("_")[2])%}
{% endif %}

if the topic is “light_status_master_bedroom/…” you should end up with “master bedroom”. If it’s “light_status_office/…” you’ll end up with “office”.

1 Like

As ridiculous as this may seem, this should always get you the first found word between a _ and a /.

{{ trigger.topic | regex_findall_index('(?<=_)([a-z]+)(?=\/)', index=0) }}

So this bit of funky text here: '(?<=_)([a-z]+)(?=\/)' means find all words (without numbers) between _ and /. Pair that with index=0, and you get the first one that it finds.

EDIT: If you change the index to index=1, it will find the second word between those characters. If that wasn’t clear. In your case, that would be the letters ls.

1 Like

@petro Ridiculous :wink: , but great for learning.

@finity Went with your first suggestion in the end for simplicity. It works flawlessly in my automation:

 - alias: Light status processor via MQTT
    platform: mqtt
    topic: '+/tele_ls/LWT'
    - service: rest_command.light_status_updater
       endpoint: >-
         {{ (trigger.topic.split('/')[0]).split('light_status_')[-1] }}
       power_state: >-
         {% if trigger.payload|string == "Online" %}
         {% else %}
         {% endif %}