Skip to content

Home Assistant

Home Assistant runs on a dedicated machine and handles smart home control, automations, and integrations. The configuration is fully managed in a git repository.

Configuration structure

The main entry point is configuration.yaml, which loads all feature packages automatically:

homeassistant:
  packages: !include_dir_named packages

Each file under packages/ is a self-contained feature area with its own automations, scripts, and entities:

Package Covers
media.yaml Entertainment state machine, Sonos playback and night sound
lighting.yaml Adaptive lighting, Zigbee remote control
adaptive_lighting.yaml Adaptive Lighting integration configuration
air_quality.yaml CO2, PM2.5, VOC monitoring and alerts
air_purifier.yaml Bedroom air purifier — speed driven by the room's own PM2.5 sensor, night mode from the sleep routine
thermal_comfort.yaml Dew-point perception in the humidity-prone rooms, plus frost risk on the terrace
weather.yaml Weather-derived template sensors — cloud cover, estimated illuminance
sauna.yaml Notifies phone and Sonos when the sauna RuuviTag passes 40 °C — see ESPHome
routines.yaml Wake-up and sleep routines
climate.yaml Heating schedules
presence.yaml Presence detection
safety.yaml Safety automations
plants.yaml Plant monitoring
vacuum.yaml Robot vacuum scheduling when away from home
maintenance.yaml Low-battery alerts and other upkeep reminders
messaging.yaml Notification channels — SMTP, mobile app, the shared notify_person script
network.yaml Remote power-off for kontti, plus helper scripts the routines call — see the switches that were removed
frontend.yaml Lovelace dashboards and themes
system.yaml System-level configuration (logger, etc.)
zigbee.yaml Placeholder — Zigbee devices are managed through the ZHA UI

Custom components

Custom components are managed via HACS:

Component Purpose
Adaptive Lighting Dynamic brightness and colour temperature based on time of day
Better Thermostat Improved thermostat control
FMI Finnish Meteorological Institute weather integration
OpenPlantBook Plant species database, used together with the Plant Monitor integration
Plant Monitor Per-plant moisture, light and temperature tracking with watering alerts; pulls thresholds from OpenPlantBook
Proxmox VE Proxmox monitoring in HA
Spook HA extensions and utilities
Spook Inverse Companion to Spook with the opposite set of safety-rail behaviours
Thermal Comfort Apparent temperature and dew-point sensors (used by thermal_comfort.yaml)

Key integrations

  • Sonos — multi-room audio; every spoken announcement, alarm and routine here plays through it
  • Music Assistant — music server running as a HA add-on; installed, but not in use
  • ZHA — Zigbee coordinator for lights and remotes
  • Matter — smart home protocol for compatible devices, including radiator thermostats
  • ESPHome — air quality sensors
  • MCP server — exposes HA to AI assistants via the Model Context Protocol; installed, but not in use

Why these choices

The infrastructure side of this lab has a page of deliberate decisions behind it. This list is shorter, because not everything here was decided.

  • ZHA is what Home Assistant ships with, and it was never weighed against Zigbee2MQTT. It runs the lights, the remotes and the leak sensors without trouble, so nothing has pushed the question — but it arrived as a default, not as a choice.
  • Adaptive Lighting is the custom component that earned its place. It makes the flat more comfortable to live in and does it without being noticed: brightness and colour temperature follow the day with nothing to trigger and nothing to keep an eye on.
  • Matter got the radiator thermostats because it was worth trying, not because Zigbee valves had fallen short. Three of them have since held the flat at a steady temperature through a winter.
  • Music Assistant is a leftover. It runs as an add-on and isn't used. Sonos is driven straight from Home Assistant — every announcement, alarm and spoken routine on these pages goes through the Sonos integration. Music Assistant stayed from an experiment, and it is listed above because it is installed, not because anything depends on it.

Observability

The Home Assistant host runs a Grafana Alloy agent that ships its systemd journal logs to Loki and system metrics to Prometheus on the monitoring server, so HA is covered by the same monitoring stack as the rest of the homelab.

Notable incidents

NVMe failure and a corrupt "latest" backup (2026-07-20)

The server's internal NVMe SSD failed. After replacing the drive, restoring from the most recent backup through the web UI failed repeatedly with a 500 Internal Server Error, surfaced by Supervisor as SupervisorBadRequestError.

The cause was the failed drive itself: it had already been degrading when the last scheduled backup was written, so the archive was truncated mid-write. The file looked plausible — around 550 MB — but the trailing backup.json metadata never made it to disk, and Supervisor rejected the upload as malformed. Inspecting it from a shell confirmed it:

$ tar -tf Automatic_backup_2026.7.2_2026-07-16_07.00_00004655.tar
homeassistant.tar.gz
a0d7b954_vscode.tar.gz
tar: invalid tar magic

Recovery was straightforward because the backups don't live only on the failing host: an older, intact archive was pulled from the Synology DS920+ and restored via Settings → System → Backups → Upload backup. HA came back cleanly on the older snapshot, losing only the few days between it and the corrupt one.

The lesson reinforced the backup strategy: retaining a history of backups off the host — not just the newest one, and not on the same drive being backed up — is what made recovery possible. A backup that only exists on the disk that's dying isn't a backup.

Where the follow-up landed, and where it hasn't

What the incident changed: "the backup ran" and "the backup is restorable" are different facts, and that check is now automated — but only on the two servers that back up with borgmatic. Their weekly canary restore test checks both that an extract succeeds and how old the restored content is. That second check exists because of this incident: a truncated archive and a stale archive are different failures, and file size distinguishes neither.

Home Assistant itself remains the gap — not in whether backups run, but in whether anything checks them. Supervisor takes an automatic backup daily, so the archives exist and are copied off the host. What's missing is everything around them: no deadman switch, no integrity verification, no restore test. Nothing alerts if the schedule silently stops, and nothing reads an archive after it is written. The failure above — a plausible-looking file that is silently truncated — would go undetected here today, on the one host where it has already happened once.

That schedule is configured in the Home Assistant UI rather than in the configuration repository, which puts it outside both Ansible and version control. It's the same category as the restricted NAS key: a load-bearing setting that lives only in a web interface, and therefore one that has to be written down somewhere to exist as anything more than a memory.

My experience

Home Assistant has worked well. I've started using AI for automations and configuration, which has been helped by Home Assistant's recent investment in documentation — partly motivated by LLM training.

The most useful integrations in daily life have been:

  • Adaptive Lighting — the best custom component I've installed. It genuinely improves comfort by adjusting brightness and colour temperature throughout the day.
  • Apollo Air-1 sensors — great for tracking CO2 levels. Home Assistant alerts me when levels are high, so I know when to ventilate.
  • Matter radiator thermostats — I have three, and they keep the apartment at a consistent temperature in winter. I also lower the bedroom temperature at night for better sleep.
  • Plant sensors — I get a phone notification when a plant needs watering, via Bluetooth-proxying ESP32 devices.

Zigbee and Matter have both worked well. The ESP32 devices also serve as Bluetooth proxies for the plant sensors.