A MajorDom integration — bridges Zigbee devices into the MajorDom language.
Built for the MajorDom Hub, but it doesn't need it: this is a standalone, standardized
library for Zigbee that you can use on its own (see Run it standalone below). Built on the
MajorDom Integration SDK. The entry point
is ZigBeeController (majordom_zigbee/controller.py), which the Hub — or the SDK's dev runner —
instantiates and drives through its lifecycle: pairing → commands → teardown.
- Other protocols: browse the MajorDom integrations.
- Create your own: start from the integration template.
Full integration-author docs — the controller lifecycle, data models, storing data, discovery, and a worked example — live at docs.majordom.io.
poetry install && poetry run poe install| Task | Description |
|---|---|
poe check |
Full quality pipeline (ruff, ty, pytest, poetry build/check) |
poe check --ci |
Same, plus git diff --exit-code |
Work lands on develop; master is protected and released via Actions → Release. Tests drive
the controller with the SDK's test doubles against a simulated zigpy device — no radio required
(see tests/).
majordom-zigbee is a standalone library — import it into your own app, or run just this
integration interactively (discover, pair, control, and inspect devices from a prompt) with no Hub.
It needs a Zigbee coordinator radio (a SkyConnect, ConBee, or a zigpy-znp/bellows-supported dongle)
at a serial path.
See Standalone mode for the interactive CLI, watch mode, and the programmatic API.
The integration is radio-backend-agnostic: it picks the right zigpy radio library for the
attached coordinator, so swapping dongles needs no source change.
- Bundled by default: Silicon Labs EmberZNet/EZSP (
bellows— SkyConnect, Sonoff ZBDongle-E) and TI Z-Stack/ZNP (zigpy-znp— CC2652, Sonoff ZBDongle-P). - Optional extras:
pip install 'majordom-zigbee[deconz]'(ConBee/RaspBee),[xbee](Digi XBee),[zigate](ZiGate), or[all-radios]. - Selecting the backend: by default the attached dongle is auto-probed. Pin one explicitly
with the
MAJORDOM_ZIGBEE_RADIOenv var (ezsp/znp/deconz/xbee/zigate) or theZigBeeController.radioattribute. Seemajordom_zigbee/radio.py.
- Protocol / platform: Zigbee via
zigpy(withbellows/zigpy-znpradio libraries). - Transport(s): Zigbee (IEEE 802.15.4).
- Supported devices: Zigbee Home Automation devices — lights, plugs, switches, sensors.
- Credentials needed to pair: none — devices join during an explicit pairing window.
- Hardware adapters: a Zigbee coordinator radio (SkyConnect / ConBee / a
zigpy-supported dongle) — the Hub assigns its OS device path viadependencies.hardware_interfaces(e.g./dev/ttyACM0). - Third-party software services: none —
zigpyspeaks to the radio directly. - OS / permissions: serial-port access to the radio.
| OSI layer | Protocol | Implemented by |
|---|---|---|
| Application (7) | Zigbee Cluster Library (ZCL) | this integration (via zigpy) |
| Network (3) | Zigbee NWK / APS | library (zigpy · radio firmware) |
| Data link / Physical (1–2) | IEEE 802.15.4 | radio adapter (harness) |
-
start_pairing_windowimplemented (Zigbee requires an explicit join window) - Discovery of joining devices;
controller_did_receive_discoverycalled - Re-discovery of already-paired devices on reconnect (
controller_did_connect_device) - Device pairing
- Device schema mapped: endpoints/clusters → parameter list with per-parameter metadata
- Hub → Device control (
send_command— commands and attribute writes) - Device → Hub event subscription (
controller_did_receive_events) -
identify -
unpair -
fetch - Availability tracking while running (
controller_did_lose_device/last_error) - Graceful shutdown in
stop - Tests pass against a simulated
zigpydevice (tests/)
Every parameter's UX metadata is resolved from several sources. Two independent axes, each with its own priority ladder (first match wins). See also the parameter-ux recipe.
Visibility / role / unit — resolved by classify_attribute() in zigbee_spec.py:
| # | Source | What it is |
|---|---|---|
| 1 | OUR_ATTRIBUTE_UX (VISIBILITY_OVERRIDES, USER_READINGS, EVERYDAY_CONTROL_ATTRIBUTES) |
our hand curation — a human's call wins over everything |
| — | metadata / manufacturer-on-system-cluster | forced system (safety; scaling constants & bounds stay hidden) |
| 2 | v2 quirk entity metadata | per-device judgment from a loaded zhaquirks QuirkBuilder (quirk_ux_map()), runtime |
| 3 | ZHA_ATTRIBUTE_UX |
standard-cluster judgment harvested from zha (scripts/harvest_zha.py, vendored — zha is not a runtime dep) |
| 4 | fallback policy | heuristic (reportable → user, writable → setting); logs a warning so uncurated attrs surface. Flip _FALLBACK_HIDE_UNCURATED to hide-by-default once coverage is validated on real devices. |
Bounds (min/max/step) — a separate ladder (resolve_metadata_bounds()):
- the device's own limit attributes' runtime values (
METADATA_SOURCES) — ground truth for this device; - spec tables (
ATTRIBUTE_MIN_STEP, wire-type range); - wire-type default. A missing expected limit is logged (quirk detection).
Quirks. zhaquirks.setup() runs once at controller startup so joined devices are presented in
quirked form (manufacturer clusters decoded into named/typed attributes; v2 entity metadata
attached). This requires the zigpy 2.x stack.
Drift. scripts/check_zha_drift.py re-harvests zha and diffs against the vendored artifact via
the SDK's diff_specs, tiering changes ADD / REMOVE / RECLASSIFY (high-risk — changes what
current users already see). CI opens a Dependabot-style refresh PR on drift.
The device/parameter ids are derived from the device's IEEE address via the SDK's UUID helpers, so they're stable across restarts and namespaced per integration.
See LICENSE. For commercial licensing or partnership inquiries regarding MajorDom, contact us via parker-industries.org/partnership.