Architecture¶
┌──────────── user session ────────────┐ ┌─────────────── session 0 ───────────────┐
│ HADA.Tray (WPF, notification icon) │ named │ HADA.Service (Windows service, SYSTEM) │
│ • Session sensors (window, audio, │ pipe │ • System sensors, custom sensors │
│ media, activity, mic, camera) │ ───────► │ and buttons │
│ • Session actions (volume, media │ ◄─────── │ • Lock and power actions │
│ keys, display, notifications) │ commands │ • IpcServer (sensors + control API) │
│ • IpcClient │ │ • EngineSupervisor │
│ • Settings window │ ◄──────► │ • MqttEngine ──────► MQTT broker │
└──────────────────────────────────────┘ │ • HaWebSocketEngine ► Home Assistant│
└─────────────────────────────────────────┘
- HADA.Service runs as a Windows service. It holds the connections to Home Assistant, owns the settings, and runs anything that doesn't need the user's desktop.
- HADA.Tray runs in the logged-in user's session. It reads things a service can't see, such as the focused window and the audio device, and streams them to the service over the
HADA.Sessionnamed pipe. Commands from Home Assistant for the tray's entities come back over the same pipe. Its settings window uses the same pipe to read status and logs and to change settings. The window is the same program started as a second process, which exits when the window is closed: a window costs far more memory than the tray icon and the sensors, and this way that memory is only used while the window is open. - Sensors, actions and engines talk only through an in-process event bus (
HADA.Core). Sensors never reference MQTT or WebSocket code. - There are two kinds of communication engines. Each stays idle until it is configured, and restarts by itself when its settings change:
- MQTT (recommended). Uses MQTT discovery, so entities appear automatically with unique IDs, a device, and availability tracking through a last will. There is one engine per MQTT server, so one per Home Assistant, all fed from the same bus; the
EngineSupervisormatches them to the servers in the settings by their ids. - WebSocket/REST. Needs no broker, but has the limitations listed below.
| Project | Purpose |
|---|---|
HADA.Core |
Models, event bus, entity registry and filter, engine abstraction, file logging |
HADA.Engine.Mqtt |
MQTT engine (MQTTnet) |
HADA.Engine.WebSocket |
Home Assistant WebSocket + REST engine |
HADA.Ipc |
Named pipe protocol: sensor stream and control API, server and clients |
HADA.Platform.Windows |
Win32, Core Audio and WLAN sensors and actions |
HADA.Service |
Worker service host, settings storage, engine supervisor, custom sensors and buttons, update check |
HADA.Tray |
Tray app host, notifications, the media playback sensor (Windows Runtime) and the settings window (WPF-UI, Polish and English) |
HADA.Tests |
xUnit tests |
installer |
WiX project that packs the published apps into an MSI. Built by scripts\Publish-HADA.ps1, not by the solution |
Installed and portable¶
The same two programs run in two ways, told apart by AppInstance in HADA.Core: a file named HADA.portable one folder above the program makes it a portable copy. Everything processes find each other by (the pipe, the single-instance mutexes, the stop and exit events) carries a suffix derived from the copy's folder, empty for the installed app, so copies never meet. In a portable copy the tray app starts HADA.Service.exe as a child process and stops it on exit, settings and logs go to the copy's data folder, and per-user preferences go to a JSON file there instead of the registry.