Displaying Last 10–20 Entries from JSON Log in Home Assistant Dashboard

Hi all,

I’m looking for advice or proven patterns to display a log of the last 10 (or 20) number plates on a dashboard card in Home Assistant. I’ve hit a wall with my current setup and the options I’ve tried, and I’d appreciate practical guidance or working examples.

What I have working so far:

Automated JSON Logging:
I have an automation that logs every new ANPR plate detection into a JSON log file. Each entry has a time, date, and the plate:

{"time": "17:27:08", "date": "02-06-25", "plate": "YE11TER"}
{"time": "22:06:18", "date": "02-06-25", "plate": "GH22TRG"}
{"time": "22:16:43", "date": "02-06-25", "plate": "GH22TRR"}

Automation YAML

alias: Log ANPR Plate to JSON
description: Appends every new detected plate to plates_log.json
trigger:
  - entity_id: sensor.driveway_anpr_last_recognized_plate
    platform: state
condition:
  - condition: template
    value_template: >
      {{ trigger.to_state.state | lower not in ['none', 'unknown', 'unavailable', ''] }}
action:
  - data:
      message: >
        {"time": "{{ now().strftime('%H:%M:%S') }}", "date": "{{ now().strftime('%d-%m-%y') }}", "plate": "{{ trigger.to_state.state }}"}
    service: notify.send_message
    target:
      entity_id: notify.file
  # I also update an input_text.last_plate helper for the current plate (optional)
mode: queued

What I need:

  • Display the last 10 or 20 plate log entries (with date and time) on a Lovelace dashboard card.
  • It must work reliably and update in real-time or near real-time.
  • Solution must persist the log (not limited to input_text’s 255 characters).

What I’ve tried (and why it isn’t working):

SQL/MariaDB sensors:

  • Either too slow (plate changes are fast, SQL sensor often misses them), or I run into permission/time-out issues on my minimal hardware install.

python_script:

  • Tried to create a sensor to load last 10 lines from the JSON, but Python scripts in HA don’t support open()/file reading due to sandboxing.

command_line sensor:

  • Tried reading the file with tail or a bash script, but the platform doesn’t seem to work in my environment (barebones install, not HAOS/Supervised, limited shell access).

input_text helper:

  • Limited to 255 characters, so it overflows and loses older entries.

In summary:
I have a working JSON log, automation is solid, but I can’t find a robust way to read and display the last 10–20 entries in a dashboard card. I don’t want to lose historical entries and want a solution that works on minimal hardware (Core install, not Supervised/HAOS).

maybe this is not so easy :slight_smile:

I suggest using a Trigger-based Template Sensor. Here’s are examples for similar applications:

For your application, it would look something like this:

template:
  - trigger:
      - platform: state
        entity_id: sensor.driveway_anpr_last_recognized_plate
    sensor:
      - name: ANPR Plates
        state: "{{ now().timestamp() | timestamp_custom() }}"
        attributes:
          plates: >
            {% set current = this.attributes.get('plates', []) %}
            {% set new = [{
              "time": now().strftime('%H:%M:%S'),
              "date": now().strftime('%d-%m-%y'),
              "plate": trigger.to_state.state }] %}
            {{ (new + current)[:10] }}

I suggest adding not_to to the State Trigger to (minimally) exclude state-changes to unavailable.

      - platform: state
        entity_id: sensor.driveway_anpr_last_recognized_plate
        not_to:
          - unavailable
          - unknown

thanks @123
I do still see the unknown even with the not_to trigger. Maybe I didnt setup correct in the sensor. I did this like this

The Trigger-based Template Sensor is working because I see the first detection was AJ22EYW.

The second detection was Unknown (titlecase) which is different from what’s specified in not_to which is unknown (lowercase).

In other words, the sensor reported a literal Unknown because it wasn’t able to detect the license plate. That’s different from when Home Assistant uses unknown to report that the value hasn’t been received yet.

I suggest you add Unknown as shown below.

      - platform: state
        entity_id: sensor.driveway_anpr_last_recognized_plate
        not_to:
          - unavailable
          - unknown
          - Unknown

Im wondering if my issue is that im not applying this in the right way. Should this be done on the actual sensor sensor.driveway_anpr_last_recognized_plate or the one im creating sensor.anpr_plates like below?


From what I understand from your first post, the following sensor reports what your license plate recognition system detects.

sensor.driveway_anpr_last_recognized_plate

The first post in your example suggested that the sensor’s values are exclusively license plate codes like YE11TER, GH22TRG, etc. However, your screenshot of the Logbook shows that the values can be something other than a legitimate plate such as:

  • None
  • Unknown
  • Ir i weway (whatever that is)

So let’s apply the same conditional logic you used in your automation to the Trigger-based Template Sensor.

I have updated the example to use the latest syntax where
trigger: is now triggers:
- platform: is now - trigger:

In other words, a Trigger-based Template Sensor’s syntax was recently updated to match an automation’s syntax.

template:
  - triggers:
      - trigger: state
        entity_id: sensor.driveway_anpr_last_recognized_plate
    conditions:
      - condition: template
        value_template: >
          {{ trigger.to_state.state | lower not in ['none', 'unknown', 'unavailable', ''] }}
    sensor:
      - name: ANPR Plates
        state: "{{ now().timestamp() | timestamp_custom() }}"
        attributes:
          plates: >
            {% set current = this.attributes.get('plates', []) %}
            {% set new = [{
              "time": now().strftime('%H:%M:%S'),
              "date": now().strftime('%d-%m-%y'),
              "plate": trigger.to_state.state }] %}
            {{ (new + current)[:10] }}

Did you have a chance to test what I suggested?