Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

41 Commits

Folders and files

Repository files navigation

Zigbee Home Assistant Mirror

Firmware workspace for a virtual Zigbee device built from:

  • Waveshare ESP32-P4-POE-ETH as the Ethernet, Web UI and Home Assistant bridge.
  • Waveshare ESP32-H2-Zero-M as the Zigbee radio/device.

The MVP mirrors selected Home Assistant entities to Zigbee endpoints so a TaHoma Switch can see and control them as ordinary Zigbee devices.

Current implementation status

This repository currently contains:

  • Shared C++ model for supported Home Assistant domains.
  • Shared framed protocol for P4 <-> H2 runtime traffic and H2 application OTA.
  • Shared bridge logic for mapping Zigbee commands to Home Assistant service calls.
  • Native CMake tests for the shared code.
  • PlatformIO subprojects for P4 and H2 firmware.
  • P4 HTTP API, Ethernet/HA configuration and embedded Vue/Vuetify Web UI.
  • Signed stable/beta online update channels backed by GitHub Releases and GitHub Pages, with H2-first transactional installation from the P4 Web UI.
  • P4 Web UI/API first-run admin password, session cookie login and CSRF protection for mutating API requests.
  • Guided first-run setup, Home Assistant entity mapping, device state inspection, firmware/configuration maintenance and diagnostics.
  • English and Czech UI, automatic/light/dark theme and configurable automatic status refresh.
  • Page-level lazy loading keeps the initial embedded Web UI bundle small. Runtime refreshes preserve unsaved MQTT drafts, and explicit Network/MQTT refresh actions do not replace edited form values.
  • Optional MQTT telemetry with TLS/authentication, Home Assistant device discovery, retained availability and event-driven bridge diagnostics. MQTT remains secondary to the direct Home Assistant integration and does not duplicate mapped entities.
  • P4 to H2 runtime UART link with H2 OTA, endpoint config sync and H2-reported endpoint state monitoring.
  • H2 persists the latest endpoint config and P4 can resync it during status checks if H2 restarts independently.
  • H2 OTA partition layout includes ESP Zigbee NVRAM partitions (zb_storage, zb_fct).
  • H2 ESP Zigbee runtime configured as a Zigbee router.
  • H2 registers every mapped switch entity as a Zigbee Mains Power Outlet and every mapped light entity as a Zigbee On/Off Light.
  • Explicit Zigbee pairing control from the P4 UI with a bounded 180 second window; H2 does not automatically join an open network at an arbitrary time.
  • P4 Home Assistant WebSocket state monitor with initial/fallback REST polling and P4-to-H2 On/Off state reports for On/Off-capable mapped entities.
  • H2-to-P4 Zigbee On/Off command forwarding to Home Assistant switch/light service calls.

The mapping UI and P4 device-state API also support sensor and binary_sensor entities. Their endpoint plans are validated and persisted, but the current H2 runtime activates only switch and light On/Off endpoints. Native measurement/IAS registration and reporting for sensor and binary_sensor remain future work. Light brightness, color temperature and RGB are not implemented yet.

Layout

firmware/p4/      ESP32-P4 host PlatformIO project
firmware/h2/      ESP32-H2 Zigbee PlatformIO project
lib/              shared portable C++ library
tests/native/     native tests for shared logic
web/p4/           Vue 3 + Vuetify 4 Web UI and Playwright tests
docs/             implementation notes and API contracts

P4 <-> H2 connection and wiring

Detailed wiring, transport and recovery notes are available in docs/wiring.md.

Recommended uart_gpio connection

The default and currently complete transport is a crossed 3.3 V UART link:

ESP32-P4-POE-ETH ESP32-H2-Zero-M Purpose
GPIO20 (UART1 TX) GPIO23 / U0RXD P4 to H2 data
GPIO21 (UART1 RX) GPIO24 / U0TXD H2 to P4 data
GND GND Common signal reference

TX must connect to RX and RX to TX. Both sides use 3.3 V logic. Power the boards through their normal inputs; do not connect 5 V/VBUS between the boards unless the power path has been deliberately verified. A common GND is required.

The default runtime baud rate is 460800 baud. P4 can probe common fallback rates, including 115200, when talking to an older H2 firmware. The Web UI can select the H2 link transport, but UART port and pins are currently compile-time settings in the two platformio.ini files. The relevant P4 flags are ZM_H2_UART_TX_GPIO and ZM_H2_UART_RX_GPIO; the H2 side uses the corresponding crossed ZM_H2_UART_RX_GPIO and ZM_H2_UART_TX_GPIO values.

BOOT and RESET wiring is not required for normal runtime operation or resident H2 OTA. The first H2 installation and recovery from a non-booting H2 image still require a direct H2 USB/ROM flash. Automatic BOOT/RESET control from P4 is not implemented, so no default GPIOs are assigned to those signals.

Direct USB-C connection

The source contains an optional P4 USB-host mode and USB device diagnostics, but it is disabled in the default build. Runtime communication and H2 flashing over a direct P4-to-H2 USB-C cable are not implemented yet. Do not select or wire USB-C as the only P4-H2 path for a production installation; use uart_gpio.

First installation

Prerequisites are PlatformIO, CMake, Python 3 and Node.js/npm. The P4 build runs npm ci automatically when web/p4/node_modules is missing.

  1. Flash H2 directly once so its bootloader, OTA partition table, Zigbee NVRAM partitions and application are installed.
  2. Flash P4, connect Ethernet and wire the UART link shown above.
  3. Open the IP address printed by P4 on its serial console, create the admin password and complete the Setup guide.
  4. Add Home Assistant mappings on the Mappings page. Endpoint changes cause H2 to rebuild its Zigbee endpoints and may trigger an H2 restart.
  5. Start the bounded pairing window on the H2 + Zigbee page and pair the bridge with the Zigbee coordinator/TaHoma Switch.

Native verification

PlatformIO is not required for the shared-code tests:

cmake -S . -B /tmp/zigbee-mirror-build
cmake --build /tmp/zigbee-mirror-build
/tmp/zigbee-mirror-build/native_tests

Web UI verification:

cd web/p4
npm ci
npm run build
npm run test:e2e

The automatic refresh interval is configured in the user menu and pauses while the browser tab is hidden. Setup, Mappings, Network, MQTT and H2 transport forms participate in the shared unsaved-changes guard. MQTT runtime telemetry continues to refresh while its configuration draft is being edited; use the form's Discard action to reload the latest stored values deliberately.

For local UI development against a physical P4:

cd web/p4
P4_PROXY_TARGET=http://192.168.1.50 npm run dev

PlatformIO builds

The firmware projects are intentionally split because the two boards have different targets and partition layouts.

pio run -d firmware/p4
pio run -d firmware/h2

Recommended build targets are exposed through the root Makefile:

make build-p4
make build-h2

These targets copy the OTA application images into dist/ as stable filenames:

dist/firmware.p4.bin
dist/firmware.p4.factory.bin
dist/firmware.h2.bin
dist/firmware.h2.factory.bin
dist/partitions.h2.bin

firmware.*.bin files are application-only images for OTA. The factory images contain the complete flash layout used for initial/recovery flashing. Builds are fingerprinted and skipped when their inputs and outputs are unchanged; use FORCE_REBUILD=1 make build-p4 or FORCE_REBUILD=1 make build-h2 to force one. The shared base version is stored once in the root VERSION file.

Direct upload and serial monitor

PlatformIO upload targets build as needed and flash the board connected to the selected serial port:

make upload-h2 H2_PORT=/dev/ttyACM0
make upload-p4 P4_PORT=/dev/ttyACM1

make monitor-h2 H2_PORT=/dev/ttyACM0
make monitor-p4 P4_PORT=/dev/ttyACM1

PORT=/dev/ttyACM0 can be used as a common override for any single-board command. Port names are examples and must match the local machine. For a full P4 factory/recovery flash use:

make factory-upload-p4 P4_PORT=/dev/ttyACM1
make recovery-upload-p4 P4_PORT=/dev/ttyACM1

recovery-upload-p4 erases the complete P4 flash first and therefore removes stored configuration.

OTA upload

The Firmware page checks signed online update channels by default. Stable builds are published from v* tags, while every commit on main publishes a beta prerelease. Release application images are stored as immutable GitHub Release assets; fixed channel manifests are deployed under GitHub Pages. See docs/online-updates.md for repository setup, release commands, key handling and the security model.

Online installation updates and verifies H2 first, waits for H2 to report the target version, then updates and restarts P4. It requires the uart_gpio H2 transport. Manual file upload remains available on the P4 and H2 firmware tabs as a local recovery path.

Application images can be uploaded over the P4 HTTP API or from the Firmware page in Web UI. The H2 firmware is sent through the P4 bridge to the resident H2 OTA agent, so IP is the P4 API address:

make ota-upload IP=192.168.1.50
make ota-upload-h2 IP=192.168.1.50
make ota-upload-p4 IP=192.168.1.50

These targets log in to the P4 web API first and prompt for the P4 UI password when no password is provided. If the interactive password is wrong, the prompt allows up to three attempts. For non-interactive use:

OTA_PASSWORD='admin-password' make ota-upload IP=192.168.1.50
make ota-upload-p4 IP=192.168.1.50 PASSWORD='admin-password'

For HTTPS P4 URLs with an untrusted local certificate, add OTA_SCHEME=https and OTA_INSECURE=1.

ota-upload uploads H2 first and P4 second because a successful P4 OTA schedules a P4 restart.

P4-to-H2 OTA updates only the H2 application image. If the H2 partition layout changes, connect to the H2 USB/UART port once and flash the partition table too:

make upload-h2 PORT=/dev/ttyACM0

The current H2 layout requires the Zigbee NVRAM partitions zb_storage and zb_fct. A boot log containing Failed to find zb_storage partition means the physical H2 still has an older partition table and cannot be fixed by H2 OTA alone.

Each local firmware build embeds build metadata. The ESP-IDF app version uses the format <VERSION>-0.local+YYYYMMDD.HHMMSS; the low 0.local prerelease marker ensures that beta and stable builds of the same planned version are valid upgrades. CI release builds use the exact stable or beta release version. The P4 API also exposes the full local ISO timestamp in /api/health and /api/ota/status.

MQTT telemetry

The MQTT page uses MQTT 3.1.1 and can publish one diagnostic Home Assistant device containing P4, Home Assistant, H2, Zigbee, synchronization, update, version, uptime and count entities. The broker connection supports username/password authentication, TLS with the built-in public CA bundle or a custom CA, a configurable discovery prefix and a configurable heartbeat. Broker passwords and custom CA material are write-only in normal API responses.

This channel is observability-only: mapped switch, light, sensor and binary_sensor entities are not recreated over MQTT, and MQTT command topics are not accepted. Direct Home Assistant REST/WebSocket communication remains the source of control and confirmed state. Topic layout, discovery entities, payload examples and setup instructions are in docs/mqtt.md.

The P4 build runs web/p4 through Vite before compiling firmware, converts the generated dist/ files into C++ assets, and embeds them into the app image. Use ZM_SKIP_WEBUI_BUILD=1 pio run -d firmware/p4 only for a fallback page when Node.js dependencies are not available.

The local pio executable must have a working PlatformIO Python environment. If it fails with ModuleNotFoundError: No module named 'platformio', reinstall or repair PlatformIO before building firmware.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages