Homelab Haven

AI-Built Home Assistant Dashboards and Automations

September 4, 20263 min read

Home Assistant's built-in dashboard and automation editors are built for a human clicking through menus one step at a time. When I let an AI assistant work on my config, it does better the other way around: editing the actual YAML files that back dashboards, automations and packages, over the same file share I'd use from my own laptop. In practice that means direct read/write access to configuration.yaml, automations.yaml, dashboards/*.yaml and packages/*.yaml, plus a separate live channel for reading current entity state and calling services without a config reload just to check something. The UI editors aren't wrong for what they're for. They're just the wrong interface for something that can already hand you a full YAML block and apply it in one go.

A few things separate an assistant that edits config safely from one that quietly breaks it. It needs the real, current entity_id, not a friendly name: renaming an entity in the HA UI doesn't change its entity_id, so an automation still referencing the old one fails silently instead of loudly. It needs to know which file currently owns a given YAML key, because Home Assistant doesn't warn about duplicate top-level keys across included files: whichever loads later silently wins, and check_config reports success either way. And it needs to know which sensors are actually old enough to have statistics, since recorder.get_statistics only returns history for sensors that have existed continuously; a freshly created template sensor has no past to query no matter how correct its current value is.

A script that died without a trace

A script stored a value in its variables: block and called .isoformat() on it later in a template. That's fine for a real datetime, except datetime values passed through a script's variables: block get rendered to plain strings first, so calling a datetime method on them afterward throws, and the script just stops. About 13 milliseconds, nothing written to any log. The fix was formatting the value once, at the point where it's still a real datetime, not later:

# Wrong: value already stringified by the time this runs
variables:
  now_stamp: "{{ now() }}"
# ...later...
message: "{{ now_stamp.isoformat() }}"   # fails silently

# Right: format it once, at the point where it's still a real datetime
variables:
  now_stamp: "{{ now().isoformat() }}"

Two files, one key, no warning

Two packages each declared a top-level sensor: list. Home Assistant merged them by keeping the one it loaded second: no error, no warning, check_config passing cleanly both times. The first package's sensors just quietly stopped existing. The only reason it surfaced at all was noticing a sensor missing from the entity list that should've been there.

A dashboard, briefly zero bytes

A dashboard YAML file got opened in write mode before its replacement content was fully ready, and a failure partway through left it at 0 bytes: a live, in-use dashboard, gone. Recovery took about ten minutes from Home Assistant's own automatic encrypted backup, no separate tooling needed. The rule since: write to a temporary file first, then move it into place. Never truncate a live config file and fill it in afterward.

What it's actually built, in practice

This isn't just automations. A real energy-tracking sensor package, a mobile dashboard with several tabs, and waste-collection/appliance-status automations have all been built and are running this way. Here's a small real example: a derived energy sensor built from the same two smart-meter inputs behind the battery-sizing simulations:

sensor:
  - sensor:
      - name: grid_import_total_kwh
        unique_id: grid_import_total_kwh
        unit_of_measurement: kWh
        device_class: energy
        state_class: total_increasing
        state: >
          {{ (states('sensor.p1monitor_energy_consumption_high') | float(0))
             + (states('sensor.p1monitor_energy_consumption_low') | float(0)) }}

What I'd do differently

None of those three failures showed up in a passing check_config. A green config check means the YAML is syntactically valid, not that the automation does what was meant, or that a rename hasn't silently orphaned a reference somewhere. All three got caught by noticing something missing (a sensor not appearing, a push not arriving), never by any built-in validation. I'd rather make "does this entity actually exist and update" a habit after every config change than keep trusting a clean check as proof anything works.

Related

If this saved you some time, a coffee keeps the lights on and the posts coming.

☕ Buy me a coffee

Comments

Comments are checked before they appear.