Saving a list of urls within a template sensor as a string

Hi I want to use the platform template to store in a template sensor a simple string with a list of urls just separated by spaces. The sensor I created displays ‘unknown’ as its value.
However, my template works fine in the template editor, I mean the output of the editor is exactly what I want to keep. However, my value_template code is not a kind of conditional option as usual it is a bit more complex with a for loop, it is something like this:

- platform: template
    sensors:
      osbee_programs_to_disable:
        friendly_name: 'osbee_programs_to_disable'
        value_template: >-
          {% set current_dkey = 'dkey=xxxx' %}
          {% set amp = '&' %}
          {% set url = 'http://xxxxx.org:8124/cp?' %}
          {%- for eachProgram in states.sensor.programas_osbee.attributes.progs -%}
            {% set current_pid = loop.index -1 %}
            {%- set current_config = eachProgram.config  -%}
            {%- if current_config % 2 != 0 -%}  
              {%- set current_config = current_config - 1 -%}
            {%- endif -%}
            {%- set current_name = eachProgram.name -%}  
            {%- set current_sts = eachProgram.sts  -%}
            {%- set current_nt = eachProgram.nt  -%}
            {%- set current_pt = eachProgram.pt  -%}
            {%- set changeProgramString = (url~amp~current_dkey~amp~'pid='~current_pid ~amp~'config='~current_config~amp~'nt='~current_nt~amp~'sts='~current_sts~amp~'pt='~current_pt~amp~'name='~current_name |string).replace(" ","") -%}
            {{ changeProgramString }}{{' '}}
          {%- endfor -%}

The template editor output is something like:

http://xxxxxx.org:8124/cp?&dkey=xxxxx&pid=0&config=65538&nt=3&sts=[1260,-1,-1,-1,-1]&pt=[153601,153602,153604]&name=Atardecer http://xxxxxx.org:8124/cp?&dkey=xxxxxx&pid=1&config=32512&nt=2&sts=[660,-1,-1,-1,-1]&pt=[76802,76804]&name=Mediodia

This sensor will be perfect to use as the URLs for a curl shell command. But, Can a sensor template store the same as the editor output or should I try another kind of approach. Thanks!!

For each iteration of the for-loop, shouldn’t it append to the changeProgramString variable?

The way it works now is that each iteration overwrites the previous value of changeProgramString.

What you’re seeing in the Template Editor is the result of each iteration of the for-loop and not the cumulative result of all iterations.

1 Like

Absolutely makes sense. I’d never worked with jinja templates before and thought I had to refer to the editor output. But in any case, shouldn’t it, at least, store the value of the last iteration??. Anyway, I’ll try it (and learn how to append) :slight_smile: and report back. Thanks a lot!

Be advised that a variable used within a for-loop is only defined within the loop (i.e. its scope is limited to the loop). If you want to use a variable within a loop that retains its value outside the loop, you have to use namespace.

Here are two examples:

2 Likes

Hi, I’ve considered both of your indications and this is the result:

- platform: template
    sensors:
      osbee_programs_to_disable:
        friendly_name: 'osbee_programs_to_disable'
        value_template: >-
          {% set current_dkey = 'dkey=xxxxx' %}
          {% set amp = '&' %}
          {% set url = 'http://xxxxxx.org:8124/cp?' %}
          {% set ns = namespace(changeProgString='') %}
          {%- for eachProgram in states.sensor.programas_osbee.attributes.progs -%}
            {% set current_pid = loop.index -1 %}
            {%- set current_config = eachProgram.config  -%}
            {%- if current_config % 2 != 0 -%}  
              {%- set current_config = current_config - 1 -%}
            {%- endif -%}
            {%- set current_name = eachProgram.name -%}  
            {%- set current_sts = eachProgram.sts  -%}
            {%- set current_nt = eachProgram.nt  -%}
            {%- set current_pt = eachProgram.pt  -%}
            {%- set ns.changeProgString = ns.changeProgString~' '~(url~amp~current_dkey~amp~'pid='~current_pid ~amp~'config='~current_config~amp~'nt='~current_nt~amp~'sts='~current_sts~amp~'pt='~current_pt~amp~'name='~current_name |string).replace(" ","") -%}
          {%- endfor -%}
          {{ ns.changeProgString }}

Now, I have appended the string and have made the string variable global with namespace() (didn’t know the limited scope to just the loop, thanks again!)
However, even if inside the template editor the result seems fine:

http://xxxxx.org:8124/cp?&dkey=xxxxx&pid=0&config=65538&nt=3&sts=[1260,-1,-1,-1,-1]&pt=[153601,153602,153604]&name=Atardecer http://xxxxxxxx.org:8124/cp?&dkey=xxxxx&pid=1&config=32512&nt=2&sts=[660,-1,-1,-1,-1]&pt=[76802,76804]&name=Mediodia

… the variable state inside home assistant still displays as ‘unknown’.:sob::sob:

For a template sensor, Home Assistant inspects the contents of value_template and attempts to identify the entities that it must monitor for state-changes. When one of these entities changes its state, that triggers Home Assistant to evaluate the template sensor.

When it cannot automatically find entities to monitor, it will only evaluate the template sensor once, at startup.

To help Home Assistant find the entities it should be monitoring, use the template sensor’s entity_id option. Specify one or more entities that should be monitored. When they change state, the template sensor’s value_template will be evaluated.

So if Home Assistant should be monitoring sensor.programas_osbee then add this line to the template sensor’s configuration:

entity_id: sensor.programas_osbee

Changes to the sensor’s state or its attributes will trigger an evaluation of value_template.

1 Like

I’m not sure why your template uses so many variables but it can be reduced to a more compact format that’s also easier to read:

        value_template: >-
          {% set ns = namespace(urls='') %}
          {%- for p in states.sensor.programas_osbee.attributes.progs -%}
            {% set url = 
            ('http://abc123.duckdns.org:8124/cp?&dkey=abc123'
             ~ '&pid=' ~ (loop.index - 1)
             ~ '&config=' ~ (p.config - 1 if p.config % 2 != 0 else p.config)
             ~ '&nt=' ~ p.nt
             ~ '&sts=' ~ p.sts
             ~ '&pt=' ~ p.pt
             ~ '&name=' ~ p.name).replace(' ', '') %}
            {%- set ns.urls = ns.urls ~ ' ' ~ url -%}
          {%- endfor -%}
          {{ ns.urls }}
1 Like

Thank you Taras!. Indeed, this code is much cleaner and shorter than mine. I’ve changed my code but even with yours and with an entity_id, I’m only getting ‘unknown’ as the state of this template.

We’re getting close, I’ll try again this afternoon. Just in case, Could it be something related to the maximum size of a string variable?

BTW, maybe it is useful for someone else: what I am trying to do is this:

  • I get from a REST sensor, ‘sensor.programas_osbee’, (already working fine) the data from an Open Sprinkler Bee . This is the cheap one that does not support any kind of rain control in its firmware, but it has a rest api to communicate with. I just want to control from HA that, whenever it rains, disable all active irrigation programs in the OpenSprinkler Bee. It will be triggered from an automation whenever it rains.
  • To disable the programs, you just have to make 0 the last bit of each program ‘config’ parameter (a 3 byte long integer), this is easy, as a decimal: if it’s even, the program will already be off; if it’s odd, it’s enable, so just do ‘config-1’.
  • Thus, eventually the variable ns.urls will have ready, all the URLs to change the programs to disable irrigation. Once this is working , it should be as simple as using a Shell Command with cURL and sending these URLs to the Opensprinkler Bee.
  • Then I’ll trigger another automation once per day (at 0h, for example) to reset to ‘enable’ all my irrigation programs.

When finished, maybe this is useful for someone else.

That’s a good question because there is a maximum size for an entity’s state and that’s 255 characters. So if the total length of the string in ns.urls exceeds 255, it won’t work.

An entity’s attribute can store more than 255 characters.

OK, I’ll check and play with this in a couple of hours and I’ll report back. Thanks!

That was the problem!!. I’ve removed an irrigation program and now, with only 1, it’s working fine.

You’ve told me that an entity’s attribute can store more than 255 characters but, now the problem is that as far as I know there is not attribute option for template sensors, is there?
How could I store then my URLs to be able to use them?? I’m thinking of creating a sensor from JSON data in the same JSON format to allow more than 255 characters, but now I’m not sure how to do that. I’ll keep thinking about it. :sweat:

Currently, you cannot add attributes to a template sensor. This feature will be included in a future version of Home Assistant.

At the moment, the RESTful Sensor and the MQTT Sensor offer the ability to create attributes.

I have managed to solve my issue, although I still have to solve how to send the result via cURL.
Here it is my code(even shorter now) for the template: it seems that keeping the result as an array of JSON, it is no longer limited to 255, as it works for several programs:

          {% set ns = namespace (progstochange=[]) %}
          {%- for p in states.sensor.programas_osbee.attributes.progs -%}
            {% set ns.progstochange = ns.progstochange+[{'name': p.name, 'pt': p.pt, 'sts': p.sts, 'nt': p.nt, 'config': (p.config - 1 if p.config % 2 != 0 else p.config)}] %}
          {%- endfor -%}
          {{ns.progstochange}}

Thanks again. :wink:

That’s unlikely. The developer documentation specifically says it is always converted to a string:

You can store anything you want in the state, as long as it’s a string (will be converted if it’s not).

Attempts to store more than 255 characters in state will result in an error message:

I don’t know if it’s unlikely or not, but I have had it recorded as an array, maybe as an array of strings and that is why I’m not having the 255 issue, take a look at it, you can see it in states as [sensor.osbee_programs_to_disable]

Given a true list (array) you can access each one of its items:

Screenshot%20from%202019-06-13%2009-53-43

If you say the value in state is truly an array, you should be able to access each item in it. Try it.

Also, in your screenshot, the length of sensor.osbee_programs_to_disable's value is less than 255 characters.

Now, this is a list, as you can see it gets access to each item with its index, what I didn’t check was the current length of it. I’m going to try to add more irrigation programs to my OSBee, I’ll keep you posted.

Ok, you were absolutely right about the length: I have just checked it with one more program. Then the problem doesn’t start in my template sensor but in my restful sensor, it doesn’t even load the get request and thus it’s not appearing as a sensor, and checking the ha log it says:

“File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/core.py”, line 696, in init
“State max length is 255 characters.”).format(entity_id))”

In theory I’m not needing more than 2 programs for my little garden but, it’s a pity not been able to extend it currently to any amount of programs. Any more ideas will be appreciated. Thanks again.

That’s a list in the Template Editor. The output of that template, when stored in an entity’s state, is converted to a string (as described in the developer documentation) whose length is limited to 255 characters.

Why are you trying to store many URLs in one sensor’s state?

Can you move the values from the restful sensor to attributes, e.g. with json_attributes? Then do you need the full URLs as a state or can you just build them up when you need to send the command?