This project defines the architecture and conventions used to build a semantic MQTT bus for a heterogeneous home infrastructure.
The environment includes multiple device ecosystems and protocols such as Zigbee, custom ESP firmware, network telemetry, energy systems, and HomeKit integrations. These systems publish data using incompatible topic structures and payload formats.
The purpose of this repository is to define a canonical internal structure that allows all telemetry, events, and states to be normalized and consumed by multiple systems.
The MQTT bus acts as the central integration layer between devices and higher-level services.
Primary documents:
consolidated_spec.md: consolidated reference linking all specs, decisions, and end-to-end tracesmqtt_contract.md: shared transport, payload, metadata, and historian rulessys_bus.md: operational namespace for adapters, workers, and infrastructure componentshome_bus.md: room-centric semantic bus contractenergy_bus.md: electrical telemetry bus contractaddapters.md: adapter responsibilities and normalization rulesadapter_implementation_examples.md: practical Node-RED adapter patterns, flow integration guidance, and known failure modeshistorian_worker.md: historian worker responsibilities for consuming buses and writing to PostgreSQLtdb_ingestion/mqtt_ingestion_api.md: PostgreSQL historian ingestion API contract for numeric and boolean measurementstdb_ingestion/counter_ingestion_api.md: counter ingestion API contract (stabilized, not yet implemented)The architecture separates five fundamental layers:
Device Layer
Devices publish telemetry using vendor-specific protocols and topic structures.
Examples:
Protocol Adapter Layer
Adapters translate vendor-specific topics and payloads into canonical MQTT bus contracts.
Adapters perform only normalization and protocol translation.
They must not implement automation logic, aggregation, or persistence.
MQTT Semantic Bus
The canonical model is implemented as multiple semantic buses (for example home, energy, network), each with a strict domain contract.
All higher-level services consume data from this layer.
The bus is intentionally lightweight: canonical publications must remain minimal, MQTT-ready messages rather than rich adapter envelopes.
Historian Worker Layer
Historian persistence is handled by a worker that subscribes to canonical bus topics and writes them into PostgreSQL using the historian ingestion API.
Consumer Layer
Multiple systems consume the bus simultaneously:
Pipeline:
Device -> Protocol Adapter -> MQTT Bus -> Historian Worker / Other Consumers
IoT ecosystems lack a common telemetry model.
Different devices publish data using incompatible conventions:
This lack of standardization creates several problems:
The semantic MQTT bus solves this by enforcing strict internal addressing contracts per bus.
Adapters isolate vendor inconsistencies and expose normalized data to the rest of the system.
Each bus defines its own topic grammar, but all buses inherit the same shared contract from mqtt_contract.md.
The shared contract defines:
value, last, set, meta, availability)scalar and envelope)observed_at, published_at, ingested_at)<site>/sys/... with detailed rules in sys_bus.mdSemantic categories such as sample, state, and event are carried by meta.historian.mode, not by introducing separate live stream names in v1.
This keeps ingestion simple and predictable while allowing low-overhead Node-RED flows.
Protocol translation is implemented in Node-RED.
To keep flow cost low and determinism high:
meta topicstopic, payload, and stream-policy QoS/retain onlymsg objects# subscriptions on high-volume pathsThese constraints are reflected in each bus specification.
The new broker is treated as a clean semantic boundary.
Production-facing legacy topics may continue to exist temporarily, but adapters should normalize data into the new broker namespace without leaking old topic structures into the canonical contract.
The target split is:
One of the primary consumers of the bus is the historian.
The historian records time-series measurements for long-term analysis.
Typical use cases include:
The historian does not communicate directly with devices.
Instead, it subscribes to normalized bus topics.
Current ingestion modeling is split in two:
tdb_ingestion/mqtt_ingestion_api.mdenergy_total follow the separate contract in tdb_ingestion/counter_ingestion_api.md (stabilized, not yet implemented)Example subscriptions:
+/home/+/+/+/value+/energy/+/+/+/valueThis architecture ensures that historical data remains consistent even when devices or protocols change.
The project aims to achieve the following objectives:
Adapters
Components that translate between systems and canonical bus contracts.
In practice there are two useful classes:
Buses
Domain-specific normalized telemetry spaces (for example home, energy, network).
Streams
Named data flows associated with a capability or metric (value, last, set, meta, availability).
Semantic interpretation such as sample, state, or event is carried by retained meta, especially meta.historian.mode.
Consumers
Systems that subscribe to the bus and process the data.
Protocol isolation
Device ecosystems must not leak their internal topic structure into the system.
Contract-driven addressing
All normalized telemetry must follow explicit per-bus topic contracts.
Loose coupling
Consumers must not depend on specific device implementations.
Extensibility
New buses, locations, devices, and metrics must be easy to integrate.
Observability
All telemetry should be recordable by a historian.
Node-RED efficiency
Topic and payload design should minimize transformation overhead in Node-RED.
The MQTT semantic bus is therefore optimized as a low-memory, low-CPU event bus for constrained accessories, SBCs, thin VMs, and high-rate Node-RED flows.
The system is currently being deployed with a new MQTT broker running on:
192.168.2.101
The legacy broker at:
192.168.2.133
will be progressively phased out while Node-RED adapters migrate traffic into the canonical bus.