Sending API data to Luanzi clock through HA

I am trying to publish the time of the next train leaving on my Ulanzi clock with Awtrix.

Setup:
HA installed in a virtual machine on my Synology NAS

  • Connection to the NS API is successful
  • NS Integration works
  • MQTT Broker en MQTT Integration also work
  • Sending messages to the Ulanzi clock work
  • Testing the integration in an automation template also works

But…

Displaying the next departure time on the clock does not work.

Here is the YAML text:

alias: Send next train to Awtrix
description: Sends valid train time to Awtrix via MQTT
mode: single
trigger:
  - platform: time_pattern
    minutes: "/1"  # Every minute
variables:
  train_payload: >
    {% set going = state_attr('sensor.castricum_amsterdam', 'going') %}
    {% if going %}
      {% set time = state_attr('sensor.castricum_amsterdam', 'departure_time_planned') %}
      {{ '{"text":"Next train: ' ~ time ~ '","duration":15}' }}
    {% else %}
      {% set next_time = state_attr('sensor.castricum_amsterdam', 'next') %}
      {% if next_time %}
        {{ '{"text":"Next train: ' ~ next_time ~ '","duration":15}' }}
      {% else %}
        {{ '{"text":"No valid train info","duration":15}' }}
      {% endif %}
    {% endif %}
action:
  - service: mqtt.publish
    data:
      topic: aw_trixie
      payload: "{{ train_payload }}"
      qos: 0

Here is the (working) Jinja2 template:

{% set going = state_attr('sensor.castricum_amsterdam', 'going') %}
{% if going %}
  {% set time = state_attr('sensor.castricum_amsterdam', 'departure_time_planned') %}
  {"text": "Next train: {{ time }}", "duration": 15}
{% else %}
  {% set next_time = state_attr('sensor.castricum_amsterdam', 'next') %}
  {% if next_time %}
    {"text": "Next train: {{ next_time }}", "duration": 15}
  {% else %}
    {"text": "No valid train info", "duration": 15}
  {% endif %}
{% endif %}

Here is an overview of the state attributes of this integration:

going: true
departure_time_planned: "18:09"
departure_time_actual: "18:10"
departure_delay: true
departure_platform_planned: "2"
departure_platform_actual: "2"
arrival_time_planned: "18:38"
arrival_time_actual: "18:38"
arrival_delay: false
arrival_platform_planned: "4"
arrival_platform_actual: 4b
next: "18:25"
status: normal
transfers: 1
route:
  - Castricum
  - Amsterdam Sloterdijk
  - Amsterdam Centraal
remarks: null
attribution: Data provided by NS
icon: mdi:train
friendly_name: Castricum-Amsterdam

Please help me find what I am doing wrong… :pray:

With the help of an LLM and several iterations I was able to fix it. I post here the result for my own documentation and for others to benefit from:

Main alteration was making this into a custom-app on the ulanzi clock and updating that every minute.

It shows the time of the next departure, unless there is 10 minutes left, then it counts down in minutes. When there time left is 5 minutes or less, the text is bright red to warn that you have to run.

Not on picture: when the next two trains are not going, the text ‘storing’ is shown (disruption in Dutch)

alias: Update Awtrix train app
description: Show countdown (minutes only) to next train in Awtrix rotation
triggers:
  - minutes: /1
    trigger: time_pattern
actions:
  - data:
      topic: aw_trixie/custom/train
      payload: "{}"
      retain: true
    action: mqtt.publish
  - delay: "00:00:01"
  - data:
      topic: aw_trixie/custom/train
      payload: |
        {
          "text": "{{ train_text }}",
          "color": "{{ text_color }}",
          "duration": 10,
          "icon": 2351,
          "push_token": "{{ now().timestamp() }}"
        }
      qos: 0
      retain: true
    action: mqtt.publish
mode: single
variables:
  going: "{{ state_attr('sensor.castricum_amsterdam', 'going') }}"
  status: "{{ state_attr('sensor.castricum_amsterdam', 'status') }}"
  departure_str: "{{ state_attr('sensor.castricum_amsterdam', 'departure_time_planned') }}"
  next_train_str: "{{ state_attr('sensor.castricum_amsterdam', 'next') }}"
  transfers: "{{ state_attr('sensor.castricum_amsterdam', 'transfers') | int }}"
  train_minutes: |
    {% if going %}
      {% set today = now().date().isoformat() %}
      {% set departure_dt = strptime(today ~ ' ' ~ departure_str, '%Y-%m-%d %H:%M') %}
      {% set now_naive = now().replace(tzinfo=None) %}
      {% set diff = (departure_dt - now_naive).total_seconds() | int %}
      {% if diff > 0 %}
        {{ (diff // 60) }}
      {% else %}
        0
      {% endif %}
    {% else %}
      0
    {% endif %}
  train_text: |
    {% if not going and status == 'cancelled' %}
      STORING
    {% elif not going %}
      {{ next_train_str }}
    {% elif transfers >= 1 %}
      {{ next_train_str }}
    {% elif train_minutes > 10 %}
      {{ departure_str }}
    {% elif train_minutes < 4 %}
      {{ next_train_str }}
    {% else %}
      {{ train_minutes }} min
    {% endif %}
  text_color: |
    {% if not going and status == 'cancelled' %}
      #FF0000
    {% elif not going or transfers >= 1 %}
      #0063d3
    {% elif train_minutes >= 0 and train_minutes <= 5 %}
      #FF0000
    {% else %}
      #ffc917
    {% endif %}