Consolidating a KNX Smart Home Under Home Assistant
When we moved into our new house five years ago, the electrical installation came with a full KNX bus: lights, blinds, heating actuators, the garage door, a weather station, all wired up and configured through ETS by the electrician. On top of that we had a handful of separate smart-home ecosystems that had nothing to do with KNX: a Reolink camera setup, a Zehnder ComfoAirQ ventilation system, a Kostal Plenticore inverter for the PV system, some Woox smart plugs, and a couple of Wi-Fi devices sitting in the Smart Life / Tuya app. KNX itself was visualized through the manufacturer’s native app, AmbiHome.
That’s six apps to check if you want a single overview of “is everything OK in the house right now,” and zero of them talk to each other. This post is about replacing all of that with Home Assistant, what broke along the way, and what I’d genuinely call an upgrade rather than just a change for its own sake.
The starting point
Before Home Assistant, the setup looked like this:
- AmbiHome – native visualization for the KNX installation (lights, blinds, heating, garage door)
- Reolink app – for the camera feeds
- Smart Life app – for a couple of Tuya-based Wi-Fi devices
- Zehnder ComfoConnect app – for the ventilation unit
- Kostal Plenticore app – for the PV inverter / energy data
- Woox app – for the smart plugs
Each of these is fine in isolation. None of them can see what the others are doing. And critically, none of them let you build automations that cross ecosystem boundaries. A few things I wanted just weren’t possible before:
- Weather-dependent shading – lower the blinds on the sun-facing side of the house when it’s sunny, hot, and the forecast says it’ll stay that way, but not when it’s overcast or already cool. This needs sun position, live temperature, brightness, and the daily forecast at once — no single app had access to all of that.
- Battery-saving camera automation – one of the outdoor cameras is battery-powered and only needs its PIR-based motion detection active at night or when nobody’s home. The Reolink app has no concept of “who’s home,” since that’s a Home Assistant/phone-location thing, not a camera thing.
- Auto-off timers – switch off a plug-connected towel radiator after 10 minutes, or a storage room light after 2 minutes, regardless of who or what turned it on. Simple in theory, but there’s no such feature in a smart-plug app that only knows about its own plug.
- Ventilation control based on supply air temperature – put the ComfoAirQ into away mode when the incoming air is too warm to be useful, unless a summer bypass is already running. The Zehnder app can show you the temperature; it can’t act on it.
All four of those became automations that just run in the background now, and none of them required touching a single one of the original apps.
Why Home Assistant, and how it was actually done
The interesting constraint on this project: I didn’t want direct file-system access to the Home Assistant instance — no editing configuration.yaml, no touching .storage/ by hand. Everything, from the initial 60+ KNX entities imported out of the ETS export, to every dashboard, helper, automation, and scene, was created through Home Assistant’s own REST and WebSocket APIs — the same APIs the frontend itself uses (plus a few internal, undocumented ones for KNX-specific operations, reconstructed straight from the matching version of the HA core source). It turned the whole project into something closer to infrastructure-as-code than clicking through a UI: every change is a small, reviewable script, and the full history of “what does this house’s automation look like and how did it get that way” lives in a git repository.
That approach paid off in one very concrete way: nothing here required guesswork about undocumented internals without a fallback. When something behaved oddly, the KNX integration’s live telegram monitor made it possible to tell, on the wire, whether the bug was in Home Assistant, in the physical KNX device, or in a stale internal cache — instead of just poking at the UI and hoping.
What got integrated
- KNX, via the ETS export: lights (including dimmers), switches, covers, climate (heating circuits with setpoint shift), the garage door, and the in-wall weather station, organized by room and floor.
- Kostal Plenticore inverter, feeding Home Assistant’s Energy dashboard (grid, solar, and battery, plus a template sensor to track where battery charge is actually coming from).
- Zehnder ComfoAirQ ventilation, including live supply/extract temperatures and the away/auto/boost presets.
- Reolink cameras, with live feeds embedded directly in the dashboards and PIR motion detection controlled by an automation.
- Two custom dashboards: one for day-to-day control (lights, blinds, climate, garage, cameras), one for monitoring (ventilation, energy, network status).
The rough edges
A few of these took real debugging to get right, and they’re worth mentioning because none of them were obvious from the Home Assistant UI alone:
Covers that refused to open. After lowering all blinds from a central “all down” switch, some of them would show a disabled “open” arrow — as if the cover thought it was already fully open. The underlying KNX devices don’t report real position feedback; Home Assistant was estimating position from travel time, and that estimate could get out of sync with reality badly enough to disable the wrong arrow. Fix: replace the built-in cover position slider with three plain stateless buttons (open / stop / close) that just fire the service call directly, no position tracking involved.
A heating circuit stuck at a fixed setpoint. One room’s heating circuit wouldn’t respond to setpoint changes at all, always reporting the same base temperature. Root cause, found via a live bus capture: the setpoint-shift group address is write-only on that actuator, so the KNX library’s internal cache for it was never initialized and stayed None forever. Writing a single dummy value to that group address once was enough to seed the cache and get it syncing normally.
A camera notification that took four separate fixes to actually work. This one deserves its own paragraph, because every fix revealed the next problem:
- First attempt: attach the camera’s live snapshot URL, including its access token, to the push notification. Camera access tokens rotate every few minutes — by the time the phone fetched the image, the token was already stale, and the notification showed a raw
403 Forbiddeninstead of a photo. - Fix: take an explicit
camera.snapshotfirst and serve that static file instead, sidestepping the rotating token entirely. Except Home Assistant’s static file route for that folder is only registered at startup — since the folder didn’t exist yet, the URL still 404’d, and a restart was needed to pick it up. - After the restart, still 404 — because Home Assistant’s
internal_url/external_urlsettings, which the phone app needs to turn a relative path into a real URL, had simply never been configured. - Even after fixing those, still an occasional 404 — this time from a cache-busting timestamp appended to the URL, which the phone’s notification extension apparently couldn’t handle even though the exact same URL without it worked from every other client. Dropping the cache-buster and just letting the phone briefly cache the same file fixed it for good.
None of these were documented gotchas — each one only became visible by testing the actual delivery path end to end, on the actual device, rather than trusting that a “200 OK” from a curl test on the server meant the whole chain worked.
Where this actually pays off
The dashboards are nice, but they’re not really the point — I could have gotten a unified dashboard out of a much smaller effort. The real payoff is automations that reach across every one of those previously-separate ecosystems at once:
- Shading decisions that combine sun position, live sensor readings, and the weather forecast — something none of the individual vendor apps could ever see all together.
- A camera that only burns battery on motion detection when it’s actually useful (night, or nobody home), driven by phone location data the camera itself has no access to.
- Ventilation that reacts to its own supply air temperature instead of running on a fixed schedule.
- A pile of small quality-of-life automations (auto-off timers, a seasonal heating mode switch, scene-based blind presets) that individually are trivial, but collectively remove a lot of “did I remember to turn that off” mental overhead.
None of this needed a subscription, a cloud dependency, or vendor lock-in beyond the hardware itself — it’s all running locally, and the entire configuration exists as a set of scripts in version control rather than settings buried in five different phone apps.
Closing thoughts
This is very much an ongoing project rather than a finished one — there’s more KNX functionality to expose, and I expect the automation list to keep growing as I notice more “I wish this happened automatically” moments. But the difference between “six apps, zero automations that cross between them” and “one system, several automations that quietly do the right thing” has already been worth the migration effort on its own.