lsh-protocol is the shared contract that keeps the public LSH repositories
aligned on the same wire format.
It does not contain firmware logic, Node-RED flows, automation rules, or device configuration. It contains the part that needs to stay identical everywhere: wire keys, command IDs, click IDs, golden payloads, compatibility metadata, and the generator that turns those definitions into code for the public LSH projects.
The main branch can move ahead while coordinated work is in progress. If you
are building outside the public LSH repositories, vendor a tagged release so the
protocol copy in your project stays reproducible.
The protocol is shared by these repositories:
lsh-core: the firmware-side controller implementationlsh-bridge: the serial-to-MQTT bridge implementationlabo-smart-home-coordinator: the standalone automation coordinatornode-red-contrib-lsh-logic: the Node-RED package built around the coordinator
The current public stack is documented from the user-facing side here:
This repository stays lower level. It explains the contract and gives maintainers one place to update it before regenerating the consumer-specific outputs.
The full documentation map lives in DOCS.md. Start there when you need to choose between the generated wire reference, the hand-written roles guide, and the consumer integration flow.
For a practical reading path:
- read profiles and roles before designing a bridge, gateway, or coordinator;
- keep the generated reference open when writing encoders, decoders, tests, or generated constants;
- use the LSH reference stack for the current public MQTT/Homie orchestration profile.
shared/lsh_protocol.jsonThe compact protocol specification. Edit this when the wire contract changes.shared/lsh_protocol_golden_payloads.jsonHuman-readable golden examples used by tests, generated docs, and consumers.shared/lsh_protocol.mdGenerated reference documentation. Do not edit it by hand.DOCS.mdThe documentation map for this repository.docs/profiles-and-roles.mdHand-written guidance for implementers. This is where the protocol semantics are explained in normal language.tools/generate_lsh_protocol.pyThe generator used by this repository and by vendored consumer copies.
This repo owns:
- wire command IDs
- compact JSON keys
- click type IDs
- protocol compatibility metadata
- golden payload examples
- generated protocol documentation
- role-neutral guidance for implementers
This repo does not own:
- firmware behavior
- bridge policy
- Node-RED business logic
- Homie projection or Home Assistant discovery
- physical device configuration
That separation matters. A protocol repository should be predictable, stable, and easy to audit. Runtime behavior belongs in the repositories that implement the protocol.
The compatibility agreement is intentionally simple.
BOOT is a re-sync signal. It does not negotiate protocol versions. Runtime
compatibility is checked later, when a peer receives DEVICE_DETAILS.
DEVICE_DETAILS.v carries the wire protocol major. If it matches the locally
compiled wireProtocolMajor, the peers may continue the handshake. If it does
not match, the payload must be rejected.
Keep these two values separate:
wireProtocolMajordecides runtime wire compatibility.specRevisiontracks the source-of-truth revision used to generate docs and code.
specRevision is useful for humans, CI, and vendoring checks. It is not a wire
negotiation mechanism.
LSH logical payloads are transport-agnostic. The same command can travel over serial, MQTT, or another profile-defined transport as long as the payload shape stays valid.
The current shared transport rules are:
- JSON over serial: newline-delimited JSON
- MsgPack over serial:
END + escaped(payload) + END, withEND = 0xC0,ESC = 0xDB,ESC_END = 0xDC, andESC_ESC = 0xDD - MQTT JSON: raw JSON payload
- MQTT MsgPack: raw MsgPack payload
The base protocol does not decide how many hops a deployment has, whether a bridge exists, whether commands are forwarded, how state is projected into Homie, or how Home Assistant discovery is handled. Those are profile decisions.
The LSH payload format assumes a trusted environment and a cooperative broker. It does not provide authentication, encryption, replay protection, or integrity checks inside the payload itself.
Use the security mechanisms of the transport and deployment: MQTT users and ACLs, TLS, network isolation, serial trust boundaries, and operating-system controls.
Each consumer repository vendors this repo at vendor/lsh-protocol through
git subtree.
For stable external integrations, vendor a released tag:
git remote add lsh-protocol git@github.com:labodj/lsh-protocol.git || git remote set-url lsh-protocol git@github.com:labodj/lsh-protocol.git
git fetch lsh-protocol
git subtree add --prefix=vendor/lsh-protocol lsh-protocol <tag> --squashTo update an existing vendored copy:
git remote add lsh-protocol git@github.com:labodj/lsh-protocol.git || git remote set-url lsh-protocol git@github.com:labodj/lsh-protocol.git
git fetch lsh-protocol
git subtree pull --prefix=vendor/lsh-protocol lsh-protocol <tag> --squashUse main only when you are intentionally coordinating unreleased protocol work
across multiple LSH repositories:
git remote add lsh-protocol git@github.com:labodj/lsh-protocol.git || git remote set-url lsh-protocol git@github.com:labodj/lsh-protocol.git
git fetch lsh-protocol
git subtree pull --prefix=vendor/lsh-protocol lsh-protocol main --squashAfter updating the vendored copy, regenerate or verify the target-specific files from the consumer repository:
python3 tools/update_lsh_protocol.py
python3 tools/update_lsh_protocol.py --checkConsumer wrappers should default to the vendored subtree. Use --protocol-root
or LSH_PROTOCOL_ROOT only when you explicitly want to test against a local
protocol checkout.
Generate the shared Markdown reference in this repository:
python3 tools/generate_lsh_protocol.pyCheck that generated files are up to date:
python3 tools/generate_lsh_protocol.py --checkGenerate outputs for the public consumers:
python3 tools/generate_lsh_protocol.py \
--target shared-doc \
--target core \
--target bridge \
--target coordinator \
--target node-red \
--core-root /path/to/lsh-core \
--bridge-root /path/to/lsh-bridge \
--coordinator-root /path/to/labo-smart-home-coordinator \
--node-red-root /path/to/node-red-contrib-lsh-logicTarget meanings:
shared-docwrites the generated Markdown reference.corewrites C++ headers forlsh-core.bridgewrites C++ headers forlsh-bridge.coordinatorwrites TypeScript protocol constants for the standalone coordinator package.node-redwrites TypeScript protocol constants for packages that consume this protocol directly from a Node-RED repository.
The node-red target is retained for direct Node-RED package consumption. For
new LSH automation work, the preferred direction is to keep protocol logic inside
labo-smart-home-coordinator and let Node-RED wrap that library.
When the wire contract changes:
- Edit
shared/lsh_protocol.json. - Update
shared/lsh_protocol_golden_payloads.jsonif examples changed. - Run
python3 tools/generate_lsh_protocol.py. - Run
python3 tools/generate_lsh_protocol.py --check. - Propagate the vendored protocol copy into consumer repositories.
- Run each consumer's protocol update/check command.
- Commit the spec, generated docs, and consumer-generated outputs together in the appropriate repositories.
Use these concepts precisely:
wireProtocolMajor: runtime wire compatibilityspecRevision: source-of-truth revision for generated code and documentation- git tags: released protocol milestones
Small documentation or generator-quality changes do not necessarily imply a new wire protocol major. Any payload shape or command meaning change must be handled with the same care as a firmware/API compatibility change.