File output with curly braces

Hello,

I’d want to write to a file with file notification platform something like:

{"temperature": 21, "humidity": 39}

So I can read it with a file sensor:

sensor:
  - platform: file
    name: Temperature
    file_path: /home/user/.homeassistant/sensor.json
    value_template: '{{ value_json.temperature }}'
    unit_of_measurement: "°C"

I have this in my configuration.yaml:

notify:
  - platform: file
    name: file1
    filename: /config/sensors/file1.txt

Then I have a script to write to file1.txt:

sequence:
  - service: notify.file1
    data_template:
      message: >
        {% set temperature = states('input_number.temperature') %}
        {% set humidity = states('input_number.humidity')
        {"temperature": {{temperature}}, "humidity": {{humidity}}}
mode: single

The problem is that I cannot start a line with a ‘{’ as it gives an error:

Failed to call service script/zz. template value should be a string for dictionary value @ data['message']

How can I output to the file in json format so that sensor can read it?

Thanks.

The following line is missing a %} at the end.

{% set humidity = states('input_number.humidity')  
                                                  ^
                                                  |
                                               MISSING

I misspelled while copying here, sorry, but that’s not the problem. The problem is the initial curly brace in 3rd line of the message. If I write:

"temperature": {{temperature}}, "humidity": {{humidity}}}

there’s no error, but obviously the output is not correct:

"temperature": 21.0, "humidity": 35.0}

Home Assistant supports native types. That means it examines the appearance of a template’s output and infers its type. For example, this is understood to be a list and not a string:

[ 3, 6, 12 ]

In your example, the template’s output is understood to be a JSON object, not a string. However, the value for message must be a string and nothing else. That’s why it states:

template value should be a string for dictionary value @ data['message']

When you remove the leading brace, the resulting text can no longer be interpreted as a JSON object so it’s handled as a string (which is what is needed for message so that’s why there’s no error).

Although it’s possible to disable native types (in which case the output of templates is always handled as a string), it cannot be done on per template basis and applies to all templates.

What about this?

 {% raw %}{"temperature": {{temperature}}, "humidity": {{humidity}}}{% endraw %}

EDIT: never mind I see why that would be stupid.

How about this:

{% set temperature = states('input_number.temperature') %}
{% set humidity = states('input_number.humidity') %}
{% set out = {'temperature': temperature, 'humidity': humidity} %}
  {{ out|to_json }}

Template editor likes it. But it wouldn’t be the first time that was misleading.

The technique used to infer the type doesn’t examine the template’s contents only its output.

That means it doesn’t matter if the template contains a final filter like string, int, float, etc to serve as a hint for the type we want, the conversion process only looks at the output and, based on its appearance, assigns a type.

For example, this template’s output will be handled as float regardless of the final string filter.

{{ 123.45 | string }}

With or without the final to_json the result is the same because it looks like a dictionary (dict) so its type is automatically assigned to be dict.

1 Like

Yeah, been there, https://github.com/home-assistant/core/issues/46198

Thank you for your answers.

I’ve tried:

{% set temperature = states('input_number.temperature') %}
{% set humidity = states('input_number.humidity') %}
{% set out = {'temperature': temperature, 'humidity': humidity} %}
  {{ out|to_json }}

(and also without to_json. It continues giving the same error:
Failed to call service script/zz. template value should be a string` for dictionary value @data['message']

I’ve tried the message code in the developer tools → template and the output seems ok:
The output with to_json is:
{"temperature": "21.0", "humidity": "34.0"}
and without to_json:
{'temperature': '21.0', 'humidity': '34.0'}

The output is interpreted as being of type dict (dictionary) and not a string which is the only thing that is acceptable for message. There’s no way the JSON your template is producing will be interpreted as being a string as opposed to a dictionary (unless you disable native types which will then affect all templates).

Strange…
According to notify code, it should specifically not interpret the template result in the message

UPD:
If I do

{% set out = "23.76" %}
{{ out }}

it works, while it should complain the same way if the template result would be parsed to a float.
Puzzling…

If you have the time, look at the code for inferring the value’s type. If I recall correctly, they had to make exceptions for certain things because, when the evaluation technique was applied very strictly, it became counter-productive (kind of like in this situation).

One example that comes to mind is a string that looks like a hex value. The Sonoff RF Bridge transmits strings that sometimes can look like a hex value. However, that’s due to chance and is not meant to be converted to a numeric value.

I agree but it might be an exception. For example, try a list in the Template Sensor:

{{ ["23.76"] }}

My point was that with “parse_result” to false, it explicitly disable type inferring, and revert to returning string only, at least theoritically.

I will try to debug what’s happening, here.

I carried out a simple experiment and escaped the meaning of the double-quotes. The following service call generates a notification without complaining that the result isn’t a string. In other words, it didn’t try to interpret the result as dict.
Screenshot from 2021-08-29 13-59-26

Here’s the resulting notification:

Screenshot from 2021-08-29 13-59-09

Therefore, try this version:

sequence:
  - service: notify.file1
    data:
      message: >
        {% set temperature = states('input_number.temperature') %}
        {% set humidity = states('input_number.humidity') %}
        {\"temperature\": {{temperature}}, \"humidity\": {{humidity}}}
mode: single


EDIT

Correction. Added missing %}

1 Like

:smiley: :wink:

1 Like

Oh the irony! :man_facepalming:

1 Like

Tricky…

I also think it’s tricky.

Did you try the example I suggested above? I confirmed it works (i.e. doesn’t generate an error).

1 Like

I tried but my output is not the same as yours. I get:

{ \"temperature\": 21.5, \"humidity\": 35.0 }

Well darn, I guess my use of notify.persistent_notification to test it was a bad choice. I can confirm that if I test it with notify.file1 I get the same result you did:

{ \"temperature\": 21.0, \"humidity\": 13.6 }

No error message but the escape character is passed on literally in the output.

1 Like