Case study
NILM disaggregation · Linkya
ML · Time-series · Signal processing
GitHubHome Assistant tracks everything — except the water heater, hardwired to the breaker panel with no smart plug possible. Linkya feeds the Linky signal into a Seq2Point NILM engine: draw the signatures, train, the heater emerges and publishes to HA. Blind spot closed.
Home Assistant measures everything with a smart plug: TV, laptop, fridge, oven, router. Consumption is covered, appliance by appliance.
Except the water heater. Hardwired directly to the breaker panel, dedicated circuit — no way to slip a smart plug in. And yet it runs several times a day, a few kilowatt-hours each cycle. In the HA consumption donut, it shows as an 'untracked' slice.
NILM — Non-Intrusive Load Monitoring: infer each appliance's usage from the meter's aggregate signal alone.
Linkya implements a Seq2Point model on an LSTM/GRU architecture with an attention mechanism. In the interface, we explore the global Linky signal, spot the water heater's characteristic transitions, draw the signatures, then train.
The model learns to recognize these fingerprints in the aggregate signal's noise. Once trained, it detects continuously and publishes the heater's state directly to Home Assistant via MQTT.
Let's be honest: I wasn't an ML specialist before this project. I cleared the path paired with AI, without dropping the discipline that makes the result trustworthy. Working with AI →
In the UI, we visualise the global Linky signal over the last N days, spot the characteristic transitions — a regular power spike, a consistent start time (off-peak hours for the water heater). We highlight signatures on the curve and assign them to the appliance.
After a few signatures are recorded, we train the model to recognise these fingerprints in the aggregate signal's noise. We can then test detection on the rest of the curve and refine signatures as needed. A bad detection can become a 'negative' signature — the model learns what not to confuse.
Once the model is satisfactory and the option enabled, Linkya detects continuously and publishes the appliance state directly to Home Assistant via MQTT.
The main blind spot is closed: activations, estimated usage, time patterns. The entity appears in HA just like any smart plug.
The 'untracked' slice disappears. Consumption is now attributed, including the appliance that couldn't be wired.
Every detection is scored against the reference signatures. The 30-day mean confidence is visible live.
Everything runs on the meter signal already captured. No new hardware, no wiring.
Identical appliances. Same heater model in the bedroom, the guest room and the office: identical electrical signature.
The model clearly sees that a heater is running, but not which one. Three interchangeable loads, one fingerprint. That's NILM's real wall in general — not a Linkya bug, a physical constraint of the aggregate signal.
Linkya isn't a standalone project: it's one of the apps deployed on my self-hosted infra.
Same Raspberry Pi, same Docker Compose base as the rest. One more app behind the single front door.
See the infra →References
-
[01]
Hart, G.W. (1992). Nonintrusive appliance load monitoring
The founding NILM paper. Hart established the principle in 1992: detect load transitions in an aggregate signal to recover individual appliances. Thirty years later, the core approach is unchanged.
-
[02]
NILMTK, Non-Intrusive Load Monitoring Toolkit
The academic reference framework. I don't use it (too heavy, designed for research not embedded deployment), but its benchmarks and datasets set my expectations for what's actually achievable.
-
[03]
Zhang et al. (2018). Sequence-to-Point Learning with Neural Networks for NILM
The reference paper for the Seq2Point model: a window of aggregate meter readings as input, a single appliance's power at the central point as output. The baseline architecture for Linkya.