I try to make some kind of syslog screen to display debug messages from my DIY device, getting them over MQTT. I use a Markdown card to display {{ states(‘sensor.log’) }} .
The incoming MQTT messages are text strings separated by ‘\n’ (2 symbols). Message can contain one or many such strings.
When receiving, HA converts combination of ‘\’ and ‘n’ into one new line symbol (for my opinion).
Idea is to replace on screen as many most oldest string lines as was new received.
My 1st realization was successful and represents a chain of trigger-based template sensors :
template:
- trigger:
- trigger: state
entity_id: text.iot_6_l
sensor:
- name: iot_6_log_4
state: >
{{ states('sensor.iot_6_log_3') }}
attributes:
was_upd: >
{{ as_timestamp(now()) }}
- trigger:
- trigger: state
entity_id: sensor.iot_6_log_4
attribute: was_upd
sensor:
- name: iot_6_log_3
state: >
{{ states('sensor.iot_6_log_2') }}
attributes:
was_upd: >
{{ as_timestamp(now()) }}
- trigger:
- trigger: state
entity_id: sensor.iot_6_log_3
attribute: was_upd
sensor:
- name: iot_6_log_2
state: >
{{ states('sensor.iot_6_log_1') }}
attributes:
was_upd: >
{{ as_timestamp(now()) }}
- trigger:
- trigger: state
entity_id: sensor.iot_6_log_2
attribute: was_upd
sensor:
- name: iot_6_log_1
state: >
{{ states('sensor.iot_6_log_0') }}
attributes:
was_upd: >
{{ as_timestamp(now()) }}
- trigger:
- trigger: state
entity_id: sensor.iot_6_log_1
attribute: was_upd
sensor:
- name: iot_6_log_0
state: >
{{ states('text.iot_6_l') }}
attributes:
was_upd: >
{{ as_timestamp(now()) }}
Here the most oldest lines are in ‘sensor. iot_6_log_4’. It receives a signal about newly received message and replaces its current state with the state of “previous” sensor in chain and changing own attribute ‘was_upd’.
This triggers a “previous” sensor, who performs similar actions with own “previous” neighbor in chain, and so on to the first sensor in chain who replaces its current state with the newly received message.
The chain of that sensors is represented on HA dashboard with vertical stack of Markdown cards
This scheme works as expected with the only drawback: each card in stack can contain from one to several lines and therefore screen updates may occur unevenly. In addition, there is an unused space between the cards.
All this logically leads to the idea of implementing a similar mechanism in a single sensor and use a single Markdown card.
Here it is:
template:
- trigger:
- trigger: state
entity_id: text.iot_6_l
sensor:
- name: iot_6_log
state: >
{% set log_raw = [this.state | default(''), states('text.iot_6_l')]|join('\n') %}
{% set log_text = log_raw.split('\n') %}
{% set scr_lines = states('input_number.iot_6_log_scr')|int(10) %}
{% set screen = namespace(log = "") %}
{% for line in log_text %}
{% if ((loop.length - loop.index) < scr_lines) %}
{% if (screen.log == "") %}
{% set screen.log = line %}
{% else %}
{% set screen.log = [screen.log, line]|join('\n') %}
{% endif %}{% endif %}{% endfor %}
{{ screen.log }}
Here after receiving trigger signal the current state is joined with new message adding separator ‘\n’. Next, whole text is divided into lines removing the separators ‘\n’.
After, in loop the new text is creating with filtering by allowed number of lines on screen.
For my opinion this must work but…
Result is kinda strange. New lines are appearing an disappearing in random time and random amount.
I found a mention in the documentation that
" The state template must not render to a string, including unknown
or unavailable
."
But my working chain successfully uses such templates.
And before “must not render” there is a text “If the sensor is numeric,…”
Resuming, please verdict if my idea is realizable (maybe errors in my template or something else) or it’s the case when “must not render to a string”.
Any opinion will be appreciated.