Gutgrinda Skullkrumpa da Choppy is a bearded dragon. She requires careful
temperature management: basking temps between 95–110°F during the day, ambient
heat above 70°F at night, and full-spectrum lighting on a seasonal schedule.
Before automation, this was entirely manual — check a thermometer, toggle a plug,
hope nothing goes wrong overnight. The system built here uses a Zigbee sensor,
three Matter smart plugs, and 236 lines of Home Assistant YAML to handle every
edge case: day/night transitions, seasonal light schedules, failure alerting, and
runtime threshold adjustment — all deployed from a single make command,
all producing Prometheus metrics that feed into the same Grafana dashboards used
for the rest of the homelab.
01The Problem with Manual Anything
Bearded dragons are ectotherms. They cannot regulate their own body temperature, which means the enclosure is their entire climate system. The thermal requirements are specific:
These are not suggestions. Sustained temperatures outside these ranges cause illness. A lamp left on all night disrupts sleep cycles. A dead bulb on a cold night is an emergency.
Before automation, management was entirely manual: check the thermometer, toggle a smart plug app, remember to turn things off before bed. The cognitive overhead was low on any given day but accumulated across weeks and months. More importantly, it was failure-prone — there is no reliable human process for "check the thermometer at 2am."
The goal was simple: zero manual intervention under normal conditions, immediate notification when something is abnormal.
02Hardware Layer
The physical layer consists of four devices, all running open protocols with no proprietary cloud dependencies.
| Device | Model | Protocol | Role |
|---|---|---|---|
| Temp/humidity sensor | ZG-227Z | Zigbee → Z2M → MQTT | Ground truth for all automation logic |
| Basking lamp plug | Linkind LC09003256 | Matter (WiFi) | Primary daytime heat source |
| Ambient light plug | Linkind LC09003256 | Matter (WiFi) | Full-spectrum UVB light, seasonal schedule |
| Ceramic heater plug | Linkind LC09003256 | Matter (WiFi) | Nighttime supplemental heat |
Why Zigbee for the sensor? Battery-powered Zigbee sensors are cheap, reliable, and integrate directly with the Zigbee2MQTT instance running on the homelab. The ZG-227Z reports temperature and humidity every few minutes on a CR2032 battery with months of life.
Why Matter for the plugs? Matter over WiFi gives local-network control without a proprietary cloud. The Linkind plugs commission directly into Home Assistant, are controllable offline, and expose on/off state back to HA for monitoring. No proprietary hubs. No cloud dependencies. Everything talks to infrastructure I control.
03Integration Layer
The data path from hardware to automation engine runs through three stages:
192.168.86.36192.168.86.41sensor.* entities, runs automation engineHome Assistant OS runs as a VM on the Proxmox cluster. HAOS restricts direct SSH access (a protected addon limitation), so file deployment goes through the Proxmox qm guest exec API rather than direct SSH — handled transparently by the Makefile. Matter commissioning is done once via the HA Companion app; after initial pairing, plugs are persistent switch.* entities.
04Automation Logic
All automation lives in a single HA package file (beardie.yaml) deployed to the HAOS config directory. The logic divides into three clean domains: daytime heat management, nighttime heat management, and the seasonal lighting schedule. Sun state comes from HA's built-in sun.sun entity, which calculates sunrise/sunset from the configured latitude/longitude — the schedule adapts to seasonal day length automatically, no cron jobs required.
Daytime Heat — Basking Lamp
The basking lamp runs closed-loop temperature control during daylight hours:
The sunset failsafe is intentional redundancy. The too-hot cutoff would trigger if the lamp ran at night and overheated, but explicitly turning it off at sunset eliminates the scenario entirely.
Nighttime Heat — Ceramic Heater
The ceramic heater runs the same closed-loop logic, constrained to nighttime hours:
The sunrise handoff is the cleanest part of the design. The ceramic heater and basking lamp never run simultaneously — the heater shuts off at sunrise, and the basking lamp's own automation takes over within minutes as the enclosure warms up.
Lighting Schedule — Ambient Light
UVB lighting follows fixed offsets from sunrise and sunset. The 30-minute buffers mimic a natural dusk/dawn transition and give the basking lamp a head start before UVB comes on each morning.
Runtime Threshold Adjustment
All temperature thresholds are exposed as input_number helpers in the HA dashboard. Changing a threshold is a slider adjustment — no YAML edit, no redeploy. This matters for seasonal tuning: summer ambient temps mean the enclosure holds heat differently than winter.
| Threshold | Default | Adjustable Range |
|---|---|---|
| Basking lamp ON | 95°F | 70–120°F |
| Basking lamp OFF | 110°F | 70–130°F |
| Ceramic heater ON | 70°F | 50–85°F |
| Ceramic heater OFF | 80°F | 55–90°F |
| Alert: too hot | 115°F | 80–140°F |
| Alert: too cold | 70°F | 50–90°F |
05Alerting
Three conditions trigger Discord notifications. Hold timers before alerting are intentional — a momentary temperature spike from opening the enclosure door should not fire a page. A sustained anomaly should.
The Discord webhook URL is stored in HAOS secrets.yaml and never committed to the repository. The Makefile accepts it as a deploy-time parameter and writes it to the secrets file — the value lives only in the running system and in the operator's secure storage.
06Infrastructure-as-Code
The full deployment runs from a single make target:
make beardie GUTGRINDA_DISCORD_WEBHOOK=<webhook_url> HA_TOKEN=<token> The Makefile target deploys beardie.yaml to /mnt/data/supervisor/homeassistant/packages/ on the HAOS VM via qm guest exec (Proxmox host API), then calls the HA API to reload automations. If the Discord webhook has not changed, the parameter can be omitted — the existing value in secrets.yaml is preserved.
No configuration exists outside of the repository and the running system. Wipe the HAOS VM and make beardie restores the full automation stack in under two minutes.
Webhook URLs and API tokens are passed as parameters at deploy time. Nothing sensitive is committed to the repository. Secrets live only in the running system.
Every threshold is runtime-adjustable via the HA dashboard. Changing a value does not require a redeploy — the YAML reacts to input_number state in real time.
This is the same IaC discipline applied across the rest of the homelab — every service is a make target, every secret is a parameter, nothing is manually configured. See the Homelab Architecture paper for the full stack this runs on.
07Observability
The package exposes all enclosure entities to Prometheus via the HA Prometheus integration:
prometheus:
namespace: homeassistant
filter:
include_entity_globs:
- "sensor.0xa4c13874d0343902_*" # temp + humidity
- "switch.gutgrinda_*" # lamp/heater state
- "input_number.gutgrinda_*" # thresholds Temperature history, device on/off state, and threshold values all appear in Grafana alongside the rest of the infrastructure metrics. A single dashboard can show server CPU load next to enclosure temperature — not because those metrics are related, but because the same observability stack covers everything.
A basking lamp that cycles on and off every 90 seconds is technically within range — temps are between 95°F and 110°F — but the duty cycle is abnormal. It may indicate a failing bulb, a drafty enclosure location, or unusual ambient temps. Prometheus duty-cycle data makes this visible; raw threshold logic never would.
08Where AI Enters the Picture
The current system is entirely rule-based. It works well for the defined conditions, but rule-based systems have a known failure mode: they handle the cases you thought of, not the ones you didn't. AI augmentation can address three gaps without replacing the automation layer.
A model trained on normal duty-cycle patterns could flag abnormal lamp cycling before temperatures actually go out of range — catching a failing bulb or unusual ambient condition earlier than any fixed threshold.
Prometheus has months of temperature and duty-cycle history. An LLM with access to that data and the current date could recommend threshold adjustments based on how the enclosure behaves differently across seasons.
Instead of raw Grafana panels, a daily summary generated from Prometheus data: average temps, heater activation count, days since last alert. No new sensors — just a Claude API call against data already collected.
None of this requires replacing the rule-based system. The automations run regardless. AI adds a layer of interpretation on top of data that's already being collected — the same pattern that makes AlertMind useful for infrastructure triage.
09The Pattern Is Not About Bearded Dragons
The full stack in use here is the same architecture pattern found in industrial monitoring, building management systems, and server room environmental control — implemented on commodity hardware with open-source software.
| Environment | Sensor | "Basking Lamp" | Alert trigger |
|---|---|---|---|
| Server room | Temp/humidity sensor | CRAC unit relay | Rack ambient >85°F |
| Greenhouse | Temp + soil moisture | Misting relay + grow light | Frost risk or drought threshold |
| Brewery fermentation | Thermocouple probe | Heating pad or glycol chiller | Out-of-range fermentation temp |
| 3D printing farm | Enclosure temp + humidity | Heater relay, exhaust fan | ABS warp risk, filament moisture |
In every case: sensor → MQTT → Home Assistant → smart relay → alert. The YAML changes. The stack does not. The same Proxmox homelab that runs this enclosure automation also runs Traefik, Authentik, a Kubernetes cluster, Grafana, and AlertMind — all managed by a single operator from a single Makefile.
10Conclusion
What started as "I don't want to manually manage smart plugs" became a fully observable, IaC-driven, alert-enabled environment monitoring system running on the same infrastructure stack used for production-grade services.
The implementation took one afternoon. The ongoing maintenance cost is near zero — the system has operated for months without a config change. The alerts have fired twice: once when a bulb started failing and the lamp couldn't reach target temp, and once when the Zigbee sensor battery died. Both times, Discord delivered the notification before the temperature reached a dangerous level.
Not the technology. Not the architecture diagram. The outcome is that an enclosure is safer and more consistent than it was before, managed by a system that runs without human intervention and pages when something is wrong. That outcome is achievable for any environment where temperature, humidity, or device state matters — at the cost of a few smart plugs, an open-source automation platform, and an afternoon of YAML.