Home › Portfolio › Home Energy Orchestration System

Home Energy Orchestration System

2026-now · Personal project: design, build and operations

smart meter, P1solar productionprices, per hourcar, state of chargecalendarMQTT busNode-REDrulesHome Assistantchargermetrics and logsTelegram
What feeds the system, where the logic lives, and what it drives.

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.

nowcar neededprice captimecharging window
How a charging window is chosen: the cheapest stretch under the price cap that still ends before the car is needed. A schematic, not real prices.

What brings you here?

It only changes what this site puts first. You can change it later, and nothing is sent anywhere.

Settings

No account, no server. Stored in this browser only, and gone when you clear your site data.

Appearance

This site is dark by default. Light and matching your device are both choices.

Text size

Scales the whole page, not just the body copy.

Motion

The home page has a field of cells that light under the pointer. By default this follows your system setting, which on most Windows machines means no trail.

What brings you here

Changes which menu items are on, and what comes first. Every page stays reachable by link and by address whichever you pick.