|
Bogdan Timofte
authored
2 weeks ago
|
1
|
# Home Bus
|
|
|
2
|
|
|
|
3
|
## Purpose
|
|
|
4
|
|
|
|
5
|
The `home` bus exposes room-centric and user-facing telemetry, state, and control topics for the living environment.
|
|
|
6
|
|
|
|
7
|
Typical systems connected to this bus include:
|
|
|
8
|
|
|
|
9
|
- environmental sensors
|
|
|
10
|
- lighting and switches
|
|
|
11
|
- motion/contact/presence sensors
|
|
|
12
|
- thermostats and climate devices
|
|
|
13
|
- smart sockets for on/off control semantics
|
|
|
14
|
|
|
|
15
|
The `home` bus models living-space behavior, not electrical topology.
|
|
|
16
|
|
|
|
17
|
This document defines the `home`-specific topic grammar and semantic ownership.
|
|
|
18
|
|
|
|
19
|
Shared rules for payload profiles, metadata, time semantics, quality, and operational topics are defined in `mqtt_contract.md`.
|
|
|
20
|
|
|
|
21
|
|
|
|
22
|
## Scope and Ownership
|
|
|
23
|
|
|
|
24
|
The `home` bus owns room and automation semantics.
|
|
|
25
|
|
|
|
26
|
The `energy` bus owns electrical accounting semantics.
|
|
|
27
|
|
|
|
28
|
Examples:
|
|
|
29
|
|
|
|
30
|
- `vad/home/living-room/power/tv/value` is room control state
|
|
|
31
|
- `vad/home/living-room/power/tv/set` is room control command
|
|
|
32
|
- `vad/energy/load/living-room-entertainment/active_power/value` is electrical telemetry
|
|
|
33
|
|
|
|
34
|
Rule:
|
|
|
35
|
|
|
|
36
|
- publish user-oriented control/state on `home`
|
|
|
37
|
- publish electrical measurement and accounting on `energy`
|
|
|
38
|
|
|
|
39
|
|
|
|
40
|
## Normative Topic Contract (v1)
|
|
|
41
|
|
|
|
42
|
All home topics MUST follow:
|
|
|
43
|
|
|
|
44
|
`<site>/home/<location>/<capability>/<device_id>/<stream>`
|
|
|
45
|
|
|
|
46
|
Where:
|
|
|
47
|
|
|
|
48
|
- `<site>`: deployment/site id, for example `vad`
|
|
|
49
|
- `<location>`: normalized area identifier in kebab-case
|
|
|
50
|
- `<capability>`: semantic capability in snake_case
|
|
|
51
|
- `<device_id>`: stable device/entity identifier in kebab-case
|
|
|
52
|
- `<stream>`: one of `value`, `last`, `set`, `meta`, `availability`
|
|
|
53
|
|
|
|
54
|
Examples:
|
|
|
55
|
|
|
|
56
|
- `vad/home/bedroom/temperature/bedroom-sensor/value`
|
|
|
57
|
- `vad/home/living-room/motion/radar-south/value`
|
|
|
58
|
- `vad/home/kitchen/light/ceiling-switch/value`
|
|
|
59
|
- `vad/home/living-room/power/tv/set`
|
|
|
60
|
|
|
|
61
|
|
|
|
62
|
## Canonical Identity Model
|
|
|
63
|
|
|
|
64
|
The `home` bus is built around stable room semantics.
|
|
|
65
|
|
|
|
66
|
Rules:
|
|
|
67
|
|
|
|
68
|
- `<location>` MUST describe the living-space area, not the vendor room name if the two differ
|
|
|
69
|
- `<device_id>` MUST identify the logical endpoint exposed to consumers
|
|
|
70
|
- one physical device MAY publish multiple capabilities under the same `<device_id>`
|
|
|
71
|
- replacing a physical Zigbee sensor SHOULD keep the same canonical `<device_id>` when the logical endpoint remains the same
|
|
|
72
|
|
|
|
73
|
Examples:
|
|
|
74
|
|
|
|
75
|
- `bedroom-sensor` is a logical endpoint
|
|
|
76
|
- `0x00158d0008aa1111` is a vendor identity and belongs in `meta.source_ref`, not in the topic path
|
|
|
77
|
|
|
|
78
|
This keeps HomeKit, historian, and automations insulated from vendor identity churn.
|
|
|
79
|
|
|
|
80
|
|
|
|
81
|
## Stream Semantics
|
|
|
82
|
|
|
|
83
|
- `value`: live semantic sample, whether measurement, held state, or transition notification
|
|
|
84
|
- `last`: retained last-known timestamped sample for startup/bootstrap decisions
|
|
|
85
|
- `set`: command/request topic for controllable capabilities
|
|
|
86
|
- `meta`: static or slowly-changing metadata
|
|
|
87
|
- `availability`: online/offline state
|
|
|
88
|
|
|
|
89
|
Rules:
|
|
|
90
|
|
|
|
91
|
- adapters SHOULD emit live hot-path data only on `value`
|
|
|
92
|
- adapters SHOULD deduplicate repeated `value` samples when the semantic value did not change
|
|
|
93
|
- adapters SHOULD publish retained `last` for the latest known timestamped sample
|
|
|
94
|
- legacy `state` and `event` topics SHOULD be treated as compatibility-only during migration
|
|
|
95
|
|
|
|
96
|
Command safety:
|
|
|
97
|
|
|
|
98
|
- `set` MUST NOT be retained
|
|
|
99
|
- stateful devices SHOULD acknowledge commands via `value`
|
|
|
100
|
- payload profile and time semantics follow `mqtt_contract.md`
|
|
|
101
|
|
|
|
102
|
|
|
|
103
|
## Payload Contract
|
|
|
104
|
|
|
|
105
|
To keep Node-RED flows efficient, two payload profiles are allowed.
|
|
|
106
|
|
|
|
107
|
### Profile A: Hot Path Scalar (recommended)
|
|
|
108
|
|
|
|
109
|
For frequent updates, payload SHOULD be scalar.
|
|
|
110
|
|
|
|
111
|
Examples:
|
|
|
112
|
|
|
|
113
|
- `vad/home/bedroom/temperature/bedroom-sensor/value` -> `23.6`
|
|
|
114
|
- `vad/home/living-room/motion/radar-south/value` -> `true`
|
|
|
115
|
|
|
|
116
|
Metadata is published separately on retained `meta`.
|
|
|
117
|
|
|
|
118
|
Example:
|
|
|
119
|
|
|
|
120
|
- Topic: `vad/home/bedroom/temperature/bedroom-sensor/meta`
|
|
|
121
|
- Payload:
|
|
|
122
|
|
|
|
123
|
```json
|
|
|
124
|
{
|
|
|
125
|
"unit": "C",
|
|
|
126
|
"source": "zigbee_adapter",
|
|
|
127
|
"precision": 0.1
|
|
|
128
|
}
|
|
|
129
|
```
|
|
|
130
|
|
|
|
131
|
### Profile B: Envelope JSON (optional)
|
|
|
132
|
|
|
|
133
|
For streams that need inline quality/timestamp:
|
|
|
134
|
|
|
|
135
|
```json
|
|
|
136
|
{
|
|
|
137
|
"value": 23.6,
|
|
|
138
|
"unit": "C",
|
|
|
139
|
"observed_at": "2026-03-08T10:15:12Z",
|
|
|
140
|
"quality": "good"
|
|
|
141
|
}
|
|
|
142
|
```
|
|
|
143
|
|
|
|
144
|
Recommendation:
|
|
|
145
|
|
|
|
146
|
- prefer Profile A on high-frequency paths
|
|
|
147
|
- use Profile B where source quality/time must travel with each sample
|
|
|
148
|
- if exact source timestamp must be preserved, use Profile B
|
|
|
149
|
- use `last` with Profile B and `observed_at` when control consumers need a startup sample with freshness information
|
|
|
150
|
- do not repeat metadata or adapter internals in `value` payloads
|
|
|
151
|
- keep Profile B envelopes small and canonical
|
|
|
152
|
|
|
|
153
|
|
|
|
154
|
## Capability Catalog (initial)
|
|
|
155
|
|
|
|
156
|
Environmental capabilities:
|
|
|
157
|
|
|
|
158
|
- `temperature`
|
|
|
159
|
- `humidity`
|
|
|
160
|
- `pressure`
|
|
|
161
|
- `illuminance`
|
|
|
162
|
- `co2`
|
|
|
163
|
- `voc`
|
|
|
164
|
- `pm25`
|
|
|
165
|
- `pm10`
|
|
|
166
|
|
|
|
167
|
Presence and safety capabilities:
|
|
|
168
|
|
|
|
169
|
- `motion`
|
|
|
170
|
- `presence`
|
|
|
171
|
- `contact`
|
|
|
172
|
- `water_leak`
|
|
|
173
|
- `smoke`
|
|
|
174
|
- `gas`
|
|
|
175
|
- `tamper`
|
|
|
176
|
|
|
|
177
|
Control and user-facing capabilities:
|
|
|
178
|
|
|
|
179
|
- `light`
|
|
|
180
|
- `power`
|
|
|
181
|
- `lock`
|
|
|
182
|
- `cover_position`
|
|
|
183
|
- `thermostat_mode`
|
|
|
184
|
- `target_temperature`
|
|
|
185
|
- `fan_mode`
|
|
|
186
|
- `button`
|
|
|
187
|
|
|
|
188
|
Device health capabilities that are still user-relevant:
|
|
|
189
|
|
|
|
190
|
- `battery`
|
|
|
191
|
- `battery_low`
|
|
|
192
|
|
|
|
193
|
Rules:
|
|
|
194
|
|
|
|
195
|
- use `power` on `home` only for control semantics
|
|
|
196
|
- use electrical telemetry such as `active_power` and cumulative counters such as `energy_total` on `energy`
|
|
|
197
|
- radio diagnostics such as `linkquality` SHOULD NOT go on `home` by default
|
|
|
198
|
- `presence` SHOULD normally be a higher-level derived state, not a raw single-device detection
|
|
|
199
|
- raw mmWave detection, including Zigbee `presence` with `fading_time=0`, SHOULD be published as `motion`
|
|
|
200
|
|
|
|
201
|
|
|
|
202
|
## MQTT Delivery Policy
|
|
|
203
|
|
|
|
204
|
Default policy:
|
|
|
205
|
|
|
|
206
|
- `value`: QoS 1, retain false
|
|
|
207
|
- `last`: QoS 1, retain true
|
|
|
208
|
- `set`: QoS 1, retain false
|
|
|
209
|
- `meta`: QoS 1, retain true
|
|
|
210
|
- `availability`: QoS 1, retain true (LWT preferred)
|
|
|
211
|
|
|
|
212
|
Cold-start rule:
|
|
|
213
|
|
|
|
214
|
- control consumers such as thermostats SHOULD subscribe to retained `last` to obtain the latest known sample at startup
|
|
|
215
|
- `last` SHOULD include `observed_at` so staleness can be evaluated immediately after subscribe
|
|
|
216
|
- consumers MAY unsubscribe from `last` after bootstrap and continue consuming lightweight live updates on `value`
|
|
|
217
|
- consumers that depend on deterministic retained bootstrap SHOULD use a dedicated MQTT client session instead of sharing the same broker config with unrelated subscribers
|
|
|
218
|
|
|
|
219
|
If uncertain, choose QoS 1.
|
|
|
220
|
|
|
|
221
|
|
|
|
222
|
## Node-RED Implementation Optimizations
|
|
|
223
|
|
|
|
224
|
Translation is done in Node-RED, so the contract is optimized for low processing overhead.
|
|
|
225
|
|
|
|
226
|
Guidelines:
|
|
|
227
|
|
|
|
228
|
- keep topic shape stable to simplify wildcard routing and switch nodes
|
|
|
229
|
- avoid unnecessary JSON parse/build in high-rate pipelines
|
|
|
230
|
- publish device metadata on retained `meta` to avoid payload repetition
|
|
|
231
|
- publish canonical MQTT-ready messages as early as possible after normalization
|
|
|
232
|
- emit MQTT-ready messages only at the publish boundary
|
|
|
233
|
- do not carry adapter-internal normalization structures on forwarded `msg` objects
|
|
|
234
|
- discard temporary normalization fields before publish
|
|
|
235
|
- keep `value` streams extremely lightweight
|
|
|
236
|
- centralize mapping rules (location, capability, device_id) in reusable subflows
|
|
|
237
|
- keep one ingress subflow per protocol and one normalization subflow shared by all
|
|
|
238
|
- avoid broad `#` subscriptions in hot paths
|
|
|
239
|
|
|
|
240
|
Suggested Node-RED subscriptions:
|
|
|
241
|
|
|
|
242
|
- `+/home/+/+/+/value`
|
|
|
243
|
- `+/home/+/+/+/last`
|
|
|
244
|
- `+/home/+/+/+/set`
|
|
|
245
|
|
|
|
246
|
|
|
|
247
|
## Interaction with Other Buses
|
|
|
248
|
|
|
|
249
|
Cross-bus correlations are implemented by consumers, not by changing bus semantics.
|
|
|
250
|
|
|
|
251
|
Examples:
|
|
|
252
|
|
|
|
253
|
- combine `home` presence with `energy` load telemetry for occupancy-aware energy decisions
|
|
|
254
|
- combine `home` climate data with `network` device state for diagnostics
|
|
|
255
|
|
|
|
256
|
|
|
|
257
|
## Historian Relationship
|
|
|
258
|
|
|
|
259
|
The `home` bus is a major historian source.
|
|
|
260
|
|
|
|
261
|
For ingestion worker compatibility, each message should map to:
|
|
|
262
|
|
|
|
263
|
- `metric_name`
|
|
|
264
|
- `device_id`
|
|
|
265
|
- `value`
|
|
|
266
|
- `observed_at`
|
|
|
267
|
|
|
|
268
|
`device_id` recommendation:
|
|
|
269
|
|
|
|
270
|
- `<location>.<device_id>`
|
|
|
271
|
|
|
|
272
|
Historian defaults:
|
|
|
273
|
|
|
|
274
|
- `value` streams SHOULD be ingested by default
|
|
|
275
|
- `last` streams SHOULD NOT be ingested as normal telemetry samples
|
|
|
276
|
- `meta.historian.mode` SHOULD describe whether a `value` stream represents `sample`, `state`, or `event` semantics
|
|
|
277
|
- when Profile A scalar is used, `observed_at` will usually fall back to ingestion time
|
|
|
278
|
- string enum states are semantically valid, but the current PostgreSQL ingestion API directly supports only numeric and boolean samples
|
|
|
279
|
|
|
|
280
|
Example:
|
|
|
281
|
|
|
|
282
|
- Topic: `vad/home/bedroom/temperature/bedroom-sensor/value`
|
|
|
283
|
- `metric_name = temperature`
|
|
|
284
|
- `device_id = bedroom.bedroom-sensor`
|
|
|
285
|
|
|
|
286
|
Zigbee2MQTT projection examples:
|
|
|
287
|
|
|
|
288
|
- `zigbee2mqtt/bedroom_sensor.temperature` -> `vad/home/bedroom/temperature/bedroom-sensor/value`
|
|
|
289
|
- `zigbee2mqtt/front_door_contact.contact` -> `vad/home/entrance/contact/front-door/value`
|
|
|
290
|
- `zigbee2mqtt/bedside_remote.action` -> `vad/home/bedroom/button/bedside-remote/value`
|
|
|
291
|
|
|
|
292
|
|
|
|
293
|
## Relationship with HomeKit
|
|
|
294
|
|
|
|
295
|
The `home` bus is a primary source for HomeKit adapters.
|
|
|
296
|
|
|
|
297
|
HomeKit integration remains a consumer layer concern:
|
|
|
298
|
|
|
|
299
|
- Device -> Protocol Adapter -> MQTT Bus -> HomeKit Adapter -> HomeKit
|
|
|
300
|
|
|
|
301
|
The `home` bus contract SHOULD remain independent from HomeKit-specific modeling constraints.
|
|
|
302
|
|
|
|
303
|
|
|
|
304
|
## Design Principles
|
|
|
305
|
|
|
|
306
|
- keep room-centric semantics explicit and stable
|
|
|
307
|
- separate control semantics (`home`) from electrical accounting (`energy`)
|
|
|
308
|
- optimize for deterministic, low-cost Node-RED translation
|
|
|
309
|
- keep topic grammar strict and payload overhead low
|
|
|
310
|
- keep canonical IDs independent from vendor identifiers
|