Home Energy Orchestration System
2026-now · Personal project: design, build and operations
The charging dashboard while the car charges: state of charge at 69.9 percent, 10.52 kW going in, the price at 22.2 cents, and a locked window running until 15:45.
Electricity in my house no longer has one price. On a dynamic contract it changes through the day, there are solar panels on the roof, and there is a car that wants a great deal of energy at once. Left alone, a car starts charging the moment it is plugged in, whatever power costs at that moment.
This is the system that decides instead. It is a personal project, it runs locally on a mini PC at home, and I built it to the same rules I would apply to a production landscape, because the house depends on it in the same way.
What it is
A repurposed Dell OptiPlex Micro running Proxmox. Home Assistant has a virtual machine of its own. The supporting services run as Docker Compose stacks in a separate container, grouped by what they are responsible for rather than by product: automation, monitoring, IoT, EV and infrastructure among them. A local AI stack has a container to itself, so that inference cannot starve the automation.
Three components have one job each and keep to it. Home Assistant knows the devices. Node-RED holds the logic. MQTT is the bus everything talks over: encrypted, with an account per service that can reach its own topics and nothing else.
What feeds it is the smart meter’s P1 port, read every ten seconds, the solar production and a forecast of it, the prices of the energy contract about a day and a half ahead, the charger, and the car’s state of charge.
The rules it is built to
- Codified, not clicked. Everything lives in Git. A deployed state that differs from the repository is a bug, not a normal condition.
- Observable from day one. If it runs, it logs. If it logs, it can be graphed.
- Local-first. Cloud is an integration, not a dependency. There is one deliberate exception, the car maker’s own API, because the car offers no other way to ask how full it is.
- Energy-aware by default. Any flow that touches a load knows the current price.
- Boring technology on the critical paths. Nothing experimental on anything that heats or charges.
- It serves the household, not the hobby. If it makes daily life worse it is wrong, no matter how clever.
Decisions with lasting consequences are written down as architecture decision records, nineteen so far, and the ones that were later reversed are kept, struck through, with the reason.
Charging the car
Charging when power is cheap or the sun is out is the easy half. The half I actually wanted is that the car is always charged enough for what is in the calendar, as cheaply as the deadlines allow. That makes it scheduling against a deadline rather than following a price: cost is what is optimised, enough by the time I leave is the constraint, and when time is short, cheap loses.
Plugging in is detected automatically. The system asks the car how full it is, reads the price curve and picks a window. Every five minutes it looks three days ahead in my calendar for appointments with a location, works out how far away they are and how much charge that takes, and moves the window if it has to. Below a floor it charges at once, whatever the price, so there is always enough for an errand. A message on Telegram says what it decided, and a lamp in the house shows the state of the car without anyone opening a dashboard: breathing blue while it charges, steady green when it is done, blinking red when the charger reports a fault.
Two existing engines came first, EVCC and then EMHASS, and both were stopped. What runs now is smaller: a Node-RED flow and two Python scripts. The platform carries 650 automated tests, which run every ten minutes and show as a badge on the dashboard. The prices are moving from hourly to quarter-hourly, so nothing in the code assumes an hour: every consumer derives the width of a slot from the data in front of it.
Knowing when it is broken
Metrics go through Telegraf into InfluxDB and logs into Loki, with Grafana dashboards per domain rather than one screen for everything. The alerts are few on purpose: a price spike, a meter that has gone quiet, too little known about tomorrow’s prices, a disk filling up, the internet gone, a container that is unhealthy, a backup that did not run. Every alert that is not actionable trains you to ignore the channel.
Backups run in two layers, a nightly application-consistent copy with checksums and a whole-guest image, with an encrypted copy off site. A backup that has never been restored is a hope, not a backup, so the restore is drilled: a container was destroyed on purpose and recovered from the image.
What went wrong
In July 2026 the nightly backup archived a media directory it should have skipped. It filled the disk, and the full disk corrupted the metrics database in the middle of a write. Freeing the space did not bring the database back. Recovery took a fix to the script and a manual repair of the data.
What came out of it is the part worth keeping: disk alerts at 85 and 93 percent, a health check on every container, and an alert for the case nobody thinks of, a backup job that has silently stopped running at all.
An assistant with limits
A local language model can read the state of the platform: devices, metrics, logs, the bus. Acting on it goes through a gateway that allows two things, pausing or resuming the charger and sending a notification. That list is enforced by a separate service rather than by the prompt, so editing the assistant cannot widen it.
Each action asks for confirmation first. I treat that as a speed bump for a well-behaved model and not as a safety boundary, since nothing binds the confirmation to what I actually meant. The real backstops are the short list, a log of every attempt, allowed or refused, and a Telegram message for every action taken, whatever happened in the chat.
The assistant’s older ability to call Home Assistant services directly got the same confirmation and the same trail. Narrowing that to a list of its own is still to do.
Where it stands
It has run the house’s energy since July 2026. Heating is planned and not built. Whether it saves what it was meant to save is a question for a full year of data, which it does not have yet.







