Rust · § 14a EnWG · § 9 EEG · § 42c EnWG · EEBUS · S2 / EN 50491-12-2
The house is a power station. The law now says so.
hems is the open-source home energy management platform for the German prosumer household. Every grid obligation is modelled as executable, cited, auditable code — and the network operator’s limit lives in a guard plane the optimiser cannot argue with.
Six statutes arrived in three years
A household with a roof, a battery, a car, a heat pump and a hot-water tank is a small power station with legal obligations. Most energy managers treat that as paperwork. hems treats it as computation.
| Since | What it obliges | Where it lives |
|---|---|---|
| 2024 | The network operator may turn controllable devices down, and owes the household a minimum power (§ 14a EnWG, BK6-22-300) | § 14a |
| 2025 | New photovoltaics may feed in 60 % of installed power until an intelligent meter is in operation (§ 9 EEG, Solarspitzengesetz) | § 9 EEG |
| 2025 | Every supplier offers a tariff that moves every quarter hour (§ 41a EnWG) | Tariffs |
| 2025 | Nothing is earned while the spot price is negative, once the meter has been in a year (§ 51 EEG) | § 51 |
| June 2026 | Neighbours may share renewable electricity over the public grid (§ 42c EnWG) | § 42c |
| October 2026 | A storage system must account for which kilowatt-hour was green, quarter by quarter (MiSpeL) | MiSpeL |
Every regulatory number in
the workspace carries the document and clause it came from —
[BK6-22-300 A1 4.5.2], [LPC-031] — and
cargo xtask check-citations resolves all
461 of them against an index of primary sources,
failing the build if one names a document the index does not
carry.
Three planes, three cadences, one order of authority
A house is decided at three speeds, and confusing them is how energy managers go wrong. Every layer may only narrow what the layer above it allowed, so “the grid limit was respected” is a property of the structure rather than a code path somebody remembered to write.
flowchart TB
subgraph edge["hemsd — one process on the box"]
direction TB
P["Planner
receding horizon · advisory
hems-optimizer"]
A["Arbiter
every second · inside the guard
hems-realtime::arbiter"]
G["Guard
every tick · absolute
hems-realtime::guard + hems-grid"]
D["Drivers
sans-I/O · bytes in, events out
hems-drv"]
P -- "targets + envelopes + a price per asset" --> A
A -- "a point inside the interval" --> G
G -- "setpoints that name their reason" --> D
end
SB["FNN Steuerbox
§ 14a limit over EEBUS"] --> D
D --> HW["Inverter · battery · wallbox
heat pump · tank
SunSpec / Modbus TCP"]
CLOUD["tariffd · forecastd
prices and weather"] -. "only ever makes a plan better" .-> P
D -- "measurements" --> G
G -- "verdict" --> A
-
The grid limit is a proven property, not a code path
[A1 4.6 S. 3]requires a network operator’s reduction to beat market control, so it lives in a plane the optimiser cannot reach around — checked by a 1000-round property test over random households, measurements, plans and user overrides. -
It runs the house when the cloud is gone
Guard, arbiter, planner and solar model take time as a parameter and open no socket. A winter day with a § 14a event is a unit test that runs in milliseconds — and a device the manager can only limit is handed back to its own thermostat rather than held at zero.
-
A kilowatt-hour has a price, and it differs per device
The plan carries the dual of each store’s own state equation, so “a reduction takes power from where it is worth least” is a decision rather than a sentence. It prices the § 14a ceiling itself, too.
-
Measured where the rule measures it
§ 14a limits what the controllable devices draw from the grid, so a discharging battery lends them headroom. § 9 EEG limits what leaves the connection point, so the household’s own consumption is headroom too. Applying either per device is the obvious implementation, and it is wrong in euros.
-
Missing measurements are not zeroes
A controllable device the drivers cannot hear from is assumed to be drawing its nameplate; a silent inverter is assumed to be producing nothing. That is the only pair of guesses that cannot overstate a budget, and the verdict names every asset it had to guess for.
-
Shared limits are shared, not repeated
The main fuse in both directions, every sub-board and the 4,6 kVA Schieflast limit go through one weighted max-min allocator and intersect. Bounding each asset by a shared limit individually is the mistake that looks right.
One day, end to end
A January household: a roof, a 10 kWh battery, a car that must be full by seven, a heat pump, a hot-water tank, a dishwasher — and a network operator’s reduction from 17:00 to 18:30.
2026-01-15 — with a § 14a reduction
produced 7.4 kWh
household consumption 11.0 kWh
charged into the car 21.7 kWh
heat pump 22.1 kWh
hot water 3.1 kWh
dishwasher 1.1 kWh, 75 min later
battery throughput 15.6 kWh
imported 52.9 kWh
exported 0.3 kWh
curtailed 0.0 kWh
peak feed-in, per quarter hour 0.12 of 5.88 kW
self-sufficiency 12 %
wallbox on one conductor 0 min (0 switches)
indoor temperature 19.9 – 23.0 °C
outside the comfort band 0.15 K·h
hot-water tank, emptiest 25 % full
roof, as the box learned it 90 % of the model
roof, is it still the roof it was yes, 91 % of 89 %
production forecast, CRPS 173 W (83 % of 30 lit)
load forecast, CRPS 18 W (85 % covered)
electricity bill 19.97 €
…if the quarter hour were netted +0.01 € on the bill saving, which a two-register meter does not forgive
battery life spent 0.63 €
comfort given up 0.22 €
borrowed from the stores 0.08 €
cost of the day 20.90 €
without optimisation 21.96 €
saved 1.07 €
…of it on the bill 0.63 €
§ 14a limit in force 90 min
…against a minimum of 10.5 kW
…covered by the store 1.4 kWh
control events recorded 1 (92 samples)
self-restraint records 1
slowest reaction 0 s, commanded
minutes without a plan 0
commands the hardware clipped 0 ticks (0.00 kWh)
quarter hours § 51 EEG zeroed 0
carbon behind the imports 17.3 kg (327 g/kWh)
the opening plan expected 19.06 €, off by +0.91
without an Energy Guard 2 min
§ 14a limit respected yes
§ 9 EEG ceiling respected yes
described in S2 6 resources
dearest asset vs cheapest 1×
relief from § 14a was worth 0.00 €/kWh
Modul 2 pays above 2417 kWh/a
…on this day it would have -3.77 € on the energy
-
without optimisation
The same day delivering the same service — car charged, house warm, shower hot — with the same equipment: the same battery, run the way every hybrid inverter runs one out of the box, a wallbox that starts on plug-in and ordinary thermostats, against the same weather down to the cloud at 12:19, and under the same grid rules.
The battery is the one that matters most: a store earns the import/export spread on everything it cycles whether or not anybody optimises. A saving measured against an idle one is the value of owning a battery rather than of managing one, and is comparable with nothing in the literature, which measures against rule-based self-consumption.
-
saved, against …of it on the bill
€1,08 against €0,74. The saving counts the battery life, the comfort and the service the plan actually spent. The bill alone is the flattering number every other system quotes — and here it is the smaller of the two, because the factory controller cycles the pack harder and lets the house drift further than the plan does.
-
…covered by the store
[A1 2.3]in one number: kilowatt-hours the battery lent the controllable devices during the reduction, which never crossed the connection point and which the Festlegung therefore does not count. -
relief from § 14a was worth
The shadow price of the network operator’s own ceiling. Nothing here, because the store lends the headroom; €1,20/kWh on the same evening in a household with no store whose car would otherwise leave short. Aggregators price both at “30 % of nominal”.
-
The three forecast lines are the evidence for the money lines
The planner is given only what six weeks of the box’s own metering could have taught it.
--perfect-foresightruns the same day with the planner shown the weather it will actually get — the unmanaged household comes out identical to the cent, which is what makes the difference attributable to foresight at all — and on this January day that is worth €0,28 of a €0,57 bill saving.So a saving published from a perfect-foresight run overstates itself by percent rather than by half — the small claim, and the measured one.
One box, then ten thousand — and none of them a trust anchor
Six services stand around the box. Not one of them can make the house unsafe or unlawful: the § 14a limit comes off a wire from the network operator’s Steuerbox and the guard enforces it locally, prices and weather only ever make a plan better, and an update is checked against a key the box was built with rather than against a hostname.
flowchart LR
subgraph house["the household"]
H["hemsd
guard · arbiter · planner
its own two years of § 14a evidence"]
end
subgraph fleet["the fleet"]
direction TB
T["tariffd
five day-ahead sources"]
F["forecastd
ICON-D2, the sky only"]
HI["histd
evidence · registers
Nachweis · Data Act export"]
FL["fleetd
enrolment · signed config · releases"]
O["obsd
breaches counted, never averaged"]
AG["agentd
proposes · cannot act"]
end
T -- "prices" --> H
F -- "irradiance" --> H
H -- "evidence, from its own outbox" --> HI
H -- "a signed CloudEvent per day" --> O
FL -- "signed configuration + manifests" --> H
O -. "a population, not a household" .-> AG
NB(["network operator"]) -. "Nachweis" .-> HI
HH(["the household"]) -. "Data Act Art. 4 export" .-> HI
-
The same numbers, for an agent
Every fleet service answers over the Model Context Protocol on the port it already binds, over the state its REST routes already read — so the two cannot disagree. What the second surface adds is prose: that an absent price slot is not free electricity, that a § 14a breach is a list with a site and a date rather than a rate, and that a coverage figure under twenty independent days is not a calibration.
-
Read-only, and every call is its own caller
A tool reads the credential on the request that reached it and resolves it against the same credentials the REST routes use. A household’s token reads its own site and nothing else; an operator’s reads its own tenant. An agent must never be able to move a household’s energy — that decision belongs to the arbiter, behind the guard, on the box.
-
An agent proposes, and it cannot act
Two properties rather than a policy. Its output type is a leaf — nothing in the workspace consumes one, so there is no path from an answer into a device. And its authority is derived by attenuation, which refuses to widen: it may read the § 14a record and the fleet aggregate, and it may never take the household’s Data Act export, which is a right of the user.
-
The journal is why it is a runtime
The specialists are pure functions; they run on a durable, replayable runtime anyway. A run, its input and its answer are an append-only hash-chained log, so “why did the queue say that in March” is a replay rather than an argument.
What is different about the numbers
Five rules the whole workspace is built to keep, because each of them is a way a saving figure is routinely inflated.
-
Every cost the optimiser may spend is a cost the report charges
Battery wear, comfort, curtailed production, and the service the plan decided not to deliver. A plan that leaves the car two kilowatt-hours short buys two kilowatt-hours less electricity, and the bill alone would call that a saving.
-
The forecast is allowed to be wrong
The simulated day runs a seeded realisation the planner never sees — a cloud at 12:19, a roof delivering 92 % of its datasheet — and every day scores its own forecasts, in CRPS, beside the money.
-
The baseline obeys the same law
The household the saving is measured against is not an unregulated one. Its Steuerbox turns each device down on its own
[A1 4.4.a]and its roof is capped by § 9 EEG, because a comparison against a household nobody is allowed to be is an advertisement rather than a measurement. -
Compliance is counted, not averaged
One household in ten thousand that failed to respect a reduction is an incident with a name and a date. “99,99 % compliance” reads as success, so the fleet view carries breaches as a list of findings and never as a rate — and a day only reaches it as a signed CloudEvent, under a key belonging to the household that sent it.
-
A box reports what it metered, and no money at all
A saving needs a baseline — what the day would have cost with no energy manager — and that is a counterfactual only a simulation can re-run. So a box on a wall reports the energies and the whole § 14a record and no cost, and the fleet counts those days apart rather than averaging them in as days that saved nothing. A figure a product computes about itself with a model it chose is marketing.
Or use one crate on its own
The domain crates are independent and
none of them does any I/O. hems-grid is the German grid
rules; hems-tariff is what a kilowatt-hour costs;
hems-flex is the household’s flexibility in S2. Every one of
them takes time as a parameter and opens no socket, and a build guard
fails if one of them reaches for a clock.
- hems-core
- hems-grid
- hems-tariff
- hems-forecast
- hems-optimizer
- hems-realtime
- hems-device
- hems-drv
- hems-flex
- hems-events
use hems_grid::para14a::{ControlMode, SteuVe, minimum_power};
use hems_core::prelude::{AssetId, Fallgruppe, Power};
let devices = [
SteuVe {
assets: vec![AssetId::new("wallbox")?],
fallgruppe: Fallgruppe::Ladepunkt,
power: Power::from_kw(11.0),
},
SteuVe {
assets: vec![AssetId::new("battery")?],
fallgruppe: Fallgruppe::Stromspeicher,
power: Power::from_kw(5.0),
},
];
// 4,2 kW + (2 − 1) × 0,8 × 4,2 kW = 7,56 kW — the floor the
// network operator may not go below, [BK6-22-300 A1 4.5.2].
let floor = minimum_power(&devices, ControlMode::Ems);
assert!((floor.kw() - 7.56).abs() < 1e-9);
Where to start
Thirteen pages, in the order they build on each other: run it, then how it is arranged, then what each layer actually does.
-
1
Getting started
Clone it, run the checks, and watch a household get through a January day with a § 14a reduction.
-
2
Architecture
Three control planes at three cadences, and a fixed order of authority between them.
-
3
The domain model
One sign convention, a quarter-hour grid that survives both DST transitions, an electrical tree, and commands that cannot exist without a reason.
-
4
The grid rules
§ 14a EnWG, the EEBUS limitation state machine, § 9 EEG, Modul 3, MiSpeL and § 42c — as code, with the citation for every number.
-
5
Tariffs and prices
A German electricity bill is a stack, and every layer moves for its own reasons: five day-ahead sources, § 51 EEG, and an advisor for the § 14a Module.
-
6
The planner
A receding-horizon mixed-integer linear program that prices battery wear instead of hiding it.
-
7
Forecasting, and being wrong
What the box believes about tomorrow, how it learned it, and what it costs to be wrong about it — the number no other energy manager publishes.
-
8
Flexibility
S2 (EN 50491-12-2) as the internal model: a device describes what it can do, not what it is for.
-
9
Devices and drivers
What a wanted power becomes on real hardware — amperes, contact states, contactors — and the sans-I/O driver contract behind EEBUS, SunSpec and Modbus.
-
10
Simulation and evaluation
Seven reference days and a cold one, hardware simulators that all know how to say no, a seeded weather the planner never sees, and multi-day sweeps that can falsify a day.
-
11
The fleet
The services around one box: day-ahead prices, weather, two years of § 14a evidence, enrolment, signed releases, and a fleet view that counts breaches.
-
12
Agents
Every fleet service answers over the Model Context Protocol, read-only and authorising each caller as itself — beside an advisory plane that cannot act.
-
13
Security and supply chain
The box trusts a key it was built with, not a server. Secrets are references, authorisation is per site, and every release ships an SBOM and provenance.