Labo Smart Home (LSH) is a wired, local-first home automation stack for installations
where wall buttons, relays, and indicator LEDs need to remain responsive, predictable,
and under local control.
The project grew out of a home installation that began during a renovation and kept evolving afterward. The core design goal has not changed: keep the wired controls dependable, then expose their state and commands cleanly through MQTT/Homie and the orchestration layer instead of hiding the system inside a single opaque box.
The current reference installation uses six Controllino Maxi PLCs, each paired with an ESP32 Wi-Fi bridge. The PLCs handle physical I/O and local behavior. The bridges publish controller state over MQTT using the Homie device model. The orchestration layer can run in Node-RED or as a headless Node.js coordinator.
This repository is the public entry point for LSH. It shows how the pieces fit together, where each component lives, which docs to read next, and now also hosts the stack composer that turns controller contracts into bridge firmware fragments, coordinator configuration and Node-RED node settings.
LSH is a reference stack for wired home automation. A Controllino controller keeps local inputs and outputs usable without the network. An ESP32 bridge publishes state and accepts commands over MQTT/Homie. A coordinator adds behavior that needs system-wide context, while a shared protocol package keeps compact payloads aligned across the components.
The public repositories are installable packages rather than only source snapshots: the controller and bridge libraries are available through PlatformIO, and the orchestration layer can be used either from Node-RED or as a standalone Node.js runtime.
You can adopt the stack in two ways. The recommended first route is the stack configurator: edit one controller TOML file, edit one stack TOML file, then generate the PlatformIO fragments and orchestration settings. The manual route is still available for existing projects, but then you keep controller, bridge, MQTT, coordinator and Node-RED values aligned yourself.
LSH is not a packaged plug-and-play smart-home product or the simplest path for a few Wi-Fi bulbs. It fits projects whose maintainers are comfortable with electrical planning, firmware builds, MQTT services, and gradual integration.
- Your wired controls need to keep working when Wi-Fi or the MQTT broker is unavailable.
- You prefer small components with clear boundaries over a single all-in-one automation box.
- You want command IDs, compact keys, and payload shapes defined in one place.
- You already use, or can reasonably adopt, tools such as PlatformIO, MQTT, Homie, Node.js, or Node-RED.
- You are looking for a reference implementation shaped by real panels, timing constraints, and maintenance work.
The photo below shows the current panel layout: a Controllino Maxi paired with an internal ESP32 bridge, a dedicated controller-to-bridge link, and external USB service leads kept available for firmware maintenance.
The early photos show how the project started: cable runs, controller bring-up and panel work during the house renovation.
|
|
| Early wiring while bringing multiple cable runs into the system. | One of the first Controllino-based installations during integration. |
For details on power, UART, level shifting, and panel serviceability, read HARDWARE_OVERVIEW.md.
| Repository | Role | Latest public release |
|---|---|---|
lsh-core |
Arduino/Controllino runtime for wired controller-side logic | |
lsh-bridge |
ESP32 bridge for serial LSH protocol and MQTT/Homie exposure | |
labo-smart-home-coordinator |
Standalone orchestration runtime for CLI and Node.js services | |
node-red-contrib-lsh-logic |
Node-RED wrapper around the coordinator runtime | |
lsh-protocol |
Shared protocol spec, generators, and golden payloads |
Optional Home Assistant discovery is handled outside LSH by generic Homie discovery projects, not by the LSH coordinator:
| Repository | Role | Latest public release |
|---|---|---|
homie-home-assistant-discovery |
Standalone daemon or embeddable Node.js core | |
node-red-contrib-homie-home-assistant-discovery |
Node-RED wrapper for Homie discovery |
Maintained infrastructure forks are available when needed, but they are supporting code
rather than starting points. The
homie-esp8266 fork is published as
labodj/homie-v5 for
ESP8266/ESP32 Arduino projects that need Homie 3.0.1 compatibility plus opt-in Homie
v4/v5 discovery modes. The MQTT client fork lives at
async-mqtt-client.
+------------------+ +------------------+ +-------------+ +-----------------------------+
| lsh-core |<--->| lsh-bridge |<--->| MQTT broker |<--->| coordinator / Node-RED node |
| Controllino side | | ESP32 bridge | | transport | | orchestration |
+------------------+ +------------------+ +-------------+ +-----------------------------+
Practical boundary summary:
lsh-coreimplements wired I/O, device topology, local click handling and compact payload encoding.lsh-bridgehandles the serial handshake, MQTT transport, Homie exposure, cached snapshot replay and bridge-side synchronization.labo-smart-home-coordinatormaintains registry state, watchdog logic, startup recovery, and distributed click orchestration.node-red-contrib-lsh-logicruns the coordinator inside Node-RED.lsh-protocolkeeps command IDs, compact keys, compatibility metadata, and generated artifacts in sync across the components.
Home Assistant is not part of the LSH runtime path. If you want Home Assistant MQTT
discovery, attach a generic Homie discovery daemon or Node-RED discovery node to the
Homie topics published by lsh-bridge.
For the exact MQTT topics, bootstrap rules, PING, BOOT, and network-click semantics,
read REFERENCE_STACK.md.
- Use DOCS.md as the public documentation map.
- Follow GETTING_STARTED.md for a first end-to-end lab setup.
- Use STACK_CONFIG.md when you want one TOML file to generate bridge, coordinator and Node-RED configuration from a controller profile.
- Keep TROUBLESHOOTING.md nearby once real MQTT traffic and hardware are involved.
If you are evaluating LSH for adoption, keep the public examples close to the stock configuration for the first successful run. Avoid changing topics, codecs, device names, and hardware assumptions all at once. Get a clean controller-to-bridge-to-coordinator chain working first, then customize one layer at a time.
A few design choices have stayed consistent through the years:
- wired controllers first, network second
- local logic must keep working when Wi-Fi or the broker misbehaves
- shared protocol contracts avoid copy-pasted constants
- resource usage matters on both AVR and ESP32 targets
- topology is treated as static between controller boots
Since lsh-core v3.0.0, controller topology is configured from TOML and compiled into
optimized static profiles. New adopters typically edit lsh_devices.toml; a device
profile no longer needs hand-written C++ topology code or hand-maintained actuator ID
lookup tables.
The public stack composer adds the deployment layer on top of that controller profile.
lsh_stack.toml keeps MQTT codec choices, Node-RED context exports and distributed
network-click actor targets outside the firmware TOML, while still generating the
bridge/coordinator contract and exact Node-RED node settings. It also emits PlatformIO
fragments for per-device controller environments and stack-wide bridge firmware
profiles, so every bridge device can run the same selected bridge binary while keeping
device-specific uploads as IDE targets.
The normal starting point is one personal installation folder with two normal PlatformIO projects inside it:
If lsh-stack is already installed:
lsh-stack new my-lsh-installation
cd my-lsh-installation
lsh-stack setupFrom a GitHub Release, you can use the single-file launcher without checking out this repository:
python /path/to/lsh-stack.pyz new my-lsh-installation
cd my-lsh-installation
python /path/to/lsh-stack.pyz setupFrom a checkout of this repository, use the standard Python launcher script. On Windows,
use py instead of python if that is how Python is installed:
python /path/to/labo-smart-home/lsh-stack.py new my-lsh-installation
cd my-lsh-installation
python /path/to/labo-smart-home/lsh-stack.py setupIf you only want to evaluate or build lsh-core firmware first, create a standalone
controller project instead of the whole stack:
lsh-stack new-core my-lsh-core
cd my-lsh-core
platformio run -e core_panelnew writes core/platformio.ini and bridge/platformio.ini once, then leaves those
manual files alone. If you already have only lsh_stack.toml and lsh_devices.toml,
setup creates the missing core/bridge PlatformIO shells beside them without
overwriting existing project files. Use either VSCode with the PlatformIO extension or
the platformio CLI if it is available. The setup command runs the normal first-use
sequence: it installs/builds the starter core once when the PlatformIO CLI is available,
regenerates generated/, checks the stack and prints the next build targets. The
lower-level generate command still replaces only the files in generated/.
Run lsh-stack status whenever you are unsure what has already been generated or which
command should come next. It does not build firmware or rewrite files.
For local or symlinked controller checkouts, set [core].tool; generated controller
environments then use the matching local platformio_lsh_static_config.py instead of a
.pio/libdeps path.
Edit core/lsh_devices.toml and lsh_stack.toml; treat generated/ as disposable;
keep persistent manual extensions in overrides/ or in the core/ and bridge/
PlatformIO files.
For Node-RED, install node-red-contrib-lsh-logic, add the node to a flow and follow
generated/node-red-setup.md. The generator gives exact copy-paste values for the
lsh-logic node, while MQTT broker settings and the surrounding flow stay in Node-RED.
For bridge builds and uploads, use the generated PlatformIO environments from the IDE or
CLI. Profile tasks such as bridge_littlefs build one wide firmware shared by every
bridge device. The same profile exposes LSH OTA j1, LSH OTA j2 and LSH OTA All
custom targets for Homie/MQTT OTA in the PlatformIO IDE. For CLI use, the stack command
can build the default bridge profile and OTA-upload one device, a subset, or every
bridge:
lsh-stack ota j1
lsh-stack ota j1 j2
lsh-stack otaIf a prerequisite is missing, the command exits with the install command to run.
LSH did not begin as a clean public multi-repo design. Early versions were much more monolithic, and a lot of automation logic lived in large Node-RED flows. Over time, the project was split into reusable pieces: controller runtime, ESP32 bridge runtime, protocol source of truth, standalone coordinator and a thin Node-RED wrapper.
The repositories were published after years of real-world use, refactoring and cleanup.
This repository remains the reference-stack entry point, not a runtime peer like
lsh-core or lsh-bridge. Its active software surface is intentionally small: the
lsh-stack composer and the quality gates around the public documentation and examples.
Component release history still lives in the runtime repositories listed above.


