This release is a pre-release and may not be stable for production use.
OWNd
OWNd is an asynchronous Python library and daemon for the Legrand / BTicino OpenWebNet home automation protocol.
It powers the Home Assistant MyHOME integration and serves as a standalone Python client for discovering, monitoring, and controlling OpenWebNet bus devices over TCP/IP gateways and serial USB interfaces.
[!TIP] 🚀 V2 Phase 2 Architecture Now Live: Phase 2 architecture is active across OWNd and MyHOME! Featuring strongly typed CEN / CEN+ scenario command builders and device triggers (P2), Thermoregulation Central Unit (3550 / 4695) master mode and zone coordination (P4), Multi-Gateway routing and plant isolation (P6), DALI Tunable White support, and 100.0% test coverage verified against the OpenWebNet Golden Corpus.
Key Features
- Hardened Dual-Session Architecture: Decouples real-time bus event monitoring (
OWNEventSession) from command and query execution (OWNCommandSession), preventing command bursts from interrupting event monitoring. - Strongly Typed CEN / CEN+ Command Builders (P2): Dedicated fluent builders (
OWNCenCommand,OWNCenPlusCommand) with strict OpenWebNet golden corpus frame parity for short press, start pressure, still held, and release actions across pushbuttons and rotary encoders. - Thermoregulation Central Unit Coordination (P4): Dedicated builder support for 3550 (
#0) and 4695 (#0#1) central units (OWNHeatingCommand.set_central_mode,set_central_temperature,set_central_antifreeze,set_central_thermal_protection,set_central_off), enabling master heating/cooling state distribution. - DALI Tunable White & Color Temperature: Built-in support for DALI DT8 ballasts (F429 / F461) with Dimension 14 color temperature encoding and bidirectional Kelvin/mireds conversion.
- OpenWebNet Golden Corpus Validation: Cross-checked and validated against the community OpenWebNet Golden Corpus (75+ real-world captured frame scenarios) ensuring exact frame encodings, dimensions, and edge cases.
- Serial & USB Dongle Support: Built-in single-channel serial transport (
AsyncSerialTransport) for the Legrand 3578 USB/ZigBee interface with in-band event and command-reply demultiplexing. - Connection Resilience:
- Fail-closed SHA-1 and HMAC-SHA2 gateway authentication with constant-time signature verification.
- OS-level TCP keepalive (
SO_KEEPALIVE) with aggressive probing (30s idle / 10s interval / 3 count) to detect silent network drops (power loss, cable unplugged) in ~60s. - Periodic application-level keepalives and passive watchdogs.
- Non-blocking bounded timeouts on handshakes and commands to prevent event loop stalls.
- Multi-frame response collection for large bus status sweeps (up to 256 frames).
- Declarative Hardware Profiles: Tailored queue pacing, session concurrency, and subsystem limits for known Legrand/BTicino hardware (F454, F455, MH200N, MH201, MH202, MyHomeServer1, and conservative generic fallbacks).
- Modern Python & Strict 100% Test Coverage: Designed for Python 3.11+, tested continuously against Python 3.11, 3.12, 3.13, and 3.14 with strict 100.0% line coverage unconditionally enforced across all core modules.
Installation
Install the latest stable release from PyPI:
pip install OWNd
To test preview releases or beta builds:
pip install --pre OWNd
Optional Extras
- Serial / USB support (required for Legrand 3578 USB dongles):
pip install "OWNd[serial]"
- Development & test suite:
pip install "OWNd[test]"
Supported Subsystems (WHO Catalog)
OWNd parses OpenWebNet frames and dispatches typed commands and events across the full MyHOME spectrum:
| WHO | Subsystem | Description & Capabilities | Event / Command Classes |
|---|---|---|---|
| 1 | Lighting | On/off switching, dimming level (0–100%), DALI Tunable White (Dimension 14, 2000K–6535K / mireds), status queries | OWNLightingCommand, OWNLightingEvent |
| 2 | Automation | Shutters, blinds, motorized curtains, tilt angles, short & full replies | OWNAutomationCommand, OWNAutomationEvent |
| 3 | Load Control | Load shedding status, circuit priority management | OWNCommand, OWNEvent |
| 4 | Thermoregulation / Climate | Multi-zone temperature readouts, target adjustments, HVAC modes (Heat/Cool/Auto/Off), local offsets, fan coil speeds, valve states, Central Unit 3550/4695 master coordination | OWNHeatingCommand, OWNHeatingEvent |
| 5 | Burglar Alarm | Zone status, system arming / disarming states | OWNAlarmCommand, OWNAlarmEvent |
| 13 | Gateway Diagnostics & Clock | Gateway date/time synchronization, timezone offsets, firmware metadata | OWNGatewayCommand, OWNGatewayEvent |
| 15 | CEN Scenarios | Scenario control, pushbutton push/release/extended press events, strongly typed command builders | OWNCenCommand, OWNCENEvent, OWNScenarioEvent |
| 16 / 22 | Sound Diffusion | Multi-source selection, zone activation, volume adjustment, F441 matrix | OWNSoundCommand, OWNSoundEvent, OWNAVCommand |
| 17 | Scenario Programmer | MH200N / MH202 scenario activation and state monitoring | OWNSceneEvent |
| 18 | Energy Management | Active power (W), hourly/daily/monthly consumption (kWh), Stop & Go breaker diagnostics | OWNEnergyCommand, OWNEnergyEvent |
| 25 | CEN+ & Dry Contacts | 32-button keypads, rotary knob encoders (CW/CCW), dry contacts, PIR sensors, strongly typed command builders | OWNCenPlusCommand, OWNCENPlusEvent, OWNDryContactCommand, OWNDryContactEvent |
Hardware Gateway Profiles
Gateways have varying processing limitations, socket budgets, and pacing requirements. OWNd uses declarative profiles to protect your hardware:
| Gateway Model | Concurrency | Queue Delay | Keepalive | Features |
|---|---|---|---|---|
| MyHomeServer1 | 4 sessions (2 default) | 20 ms | Profile | HMAC-SHA2, Native transitions, Extended frames |
| F454 / F455 | 4 sessions | 50 ms | 90 s | HMAC-SHA2, Native transitions, Extended frames |
| MH202 | 2 sessions | 100 ms | Profile | HMAC-SHA2, Extended frames |
| MH201 | 1 session | 100 ms | Profile | Extended frames, Clock diagnostics |
| MH200N | 1 session | 150 ms | 90 s | Safe pacing, Legacy password auth |
| Generic Gateway | 1 session | 50 ms | Profile | Conservative fallback |
Profiles can be resolved automatically using get_gateway_profile(model_name):
from OWNd.profiles import get_gateway_profile
profile = get_gateway_profile("F454")
print(f"Max concurrent sessions: {profile.max_command_sessions}")
print(f"Command queue delay: {profile.command_queue_delay}s")
Quick Start
1. High-Level TCP Transport (Dual-Session)
import asyncio
from OWNd.connection import OWNGateway
from OWNd.transport.tcp import AsyncTcpTransport
from OWNd.message import OWNMessage
async def main():
# Configure gateway credentials
gateway = OWNGateway({
"address": "192.168.1.50",
"port": 20000,
"password": "12345",
})
transport = AsyncTcpTransport(gateway)
# Register an event listener for bus notifications
def on_event(msg: OWNMessage | str):
if isinstance(msg, OWNMessage) and msg.is_event:
print(f"Bus Event: {msg.human_readable_log}")
transport.register_listener(on_event)
# Connect both event and command channels
if await transport.connect():
print("Connected to OpenWebNet gateway!")
# Send a command: Turn ON light at address 12 (*1*1*12##)
response = await transport.send("*1*1*12##")
print(f"Command response: {response}")
# Keep listening for events
await asyncio.sleep(10)
await transport.disconnect()
if __name__ == "__main__":
asyncio.run(main())
2. Direct Session Management
For fine-grained control, OWNEventSession and OWNCommandSession can be operated independently:
import asyncio
from OWNd.connection import OWNGateway, OWNEventSession, OWNCommandSession
async def main():
gateway = OWNGateway({"address": "192.168.1.50", "port": 20000, "password": "12345"})
# Event listening session
event_session = OWNEventSession(gateway=gateway)
await event_session.connect()
# Command session
command_session = OWNCommandSession(gateway=gateway)
await command_session.connect()
# Query status of zone 1 climate: *#4*1*0##
status = await command_session.send("*#4*1*0##", is_status_request=True)
print(f"Status response: {status}")
await event_session.close()
await command_session.close()
asyncio.run(main())
3. Serial / USB Dongle (Legrand 3578)
import asyncio
from OWNd.transport.serial import AsyncSerialTransport
async def main():
transport = AsyncSerialTransport(port="/dev/ttyUSB0")
transport.register_listener(lambda msg: print(f"Serial Inbound: {msg}"))
await transport.connect()
# Send OpenWebNet frame over serial
await transport.send("*1*1*12##")
await asyncio.sleep(5)
await transport.disconnect()
asyncio.run(main())
4. Strongly Typed CEN / CEN+ & Central Unit Commands (Phase 2)
from OWNd.message import OWNCenCommand, OWNCenPlusCommand, OWNHeatingCommand
# CEN (WHO=15): Button 2 short press on scenario controller 12 -> *15*02#2*12##
frame_cen = OWNCenCommand.short_press(where="12", button=2)
# CEN+ (WHO=25): Button 5 start pressure on keypad 01 -> *25*21#5*01##
frame_cenplus_press = OWNCenPlusCommand.start_pressure(where="01", button=5)
# CEN+ (WHO=25): Button 5 still held event -> *25*23#5*01##
frame_cenplus_held = OWNCenPlusCommand.still_held(where="01", button=5)
# CEN+ (WHO=25): Button 5 short release -> *25*24#5*01##
frame_cenplus_rel = OWNCenPlusCommand.release_from_short(where="01", button=5)
# Central Unit (WHO=4): Set 3550 (#0) master mode to Heating at 21.5°C -> *4*1#0215*#0##
frame_heat = OWNHeatingCommand.set_central_mode(where="#0", mode="heating", temperature=21.5)
# Central Unit (WHO=4): Set 4695 (#0#1) master mode to Cooling at 24.0°C -> *4*2#0240*#0#1##
frame_cool = OWNHeatingCommand.set_central_mode(where="#0#1", mode="cooling", temperature=24.0)
# Central Unit (WHO=4): Turn Central Unit OFF -> *4*303#0215*#0##
frame_off = OWNHeatingCommand.set_central_off(where="#0", mode="heating", temperature=21.5)
Command Line Interface (CLI)
OWNd includes a built-in CLI for discovering gateways and inspecting live bus events:
Auto-Discovery (SSDP)
Scan the local network for OpenWebNet gateways and listen for events:
python -m OWNd
Direct Connection
Connect to a known gateway IP address:
python -m OWNd --address 192.168.1.50 --port 20000 --password 12345 --verbose 2
Available options:
-a,--address: IP address of the gateway-p,--port: Gateway TCP port (default:20000)-P,--password: Numeric OPEN password or HMAC secret (default:12345)-m,--mac: MAC address (used as unique identifier when skipping SSDP)-v,--verbose: Verbosity level (0= WARNING,1= INFO,2= DEBUG)
Development
Clone the repository and install development dependencies:
git clone https://github.com/OpenWebNet-HA/OWNd.git
cd OWNd
pip install -e ".[test,serial]" ruff mypy types-python-dateutil types-pytz
Running Tests
Execute the test suite across all subsystems:
python -m pytest -q
Static Analysis & Linting
Verify type safety and coding standards:
ruff check OWNd tests setup.py
mypy OWNd
License
This project is licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0-only). See the LICENSE file for details.
📊 Code Coverage & Quality Assurance
OWNd maintains an automated test suite with strict 100.0% line coverage (3,087 / 3,087 statements covered with 0 missing lines across all 9 core modules) verified continuously in CI across Python 3.11, 3.12, 3.13, and 3.14:
| Component / Module | Coverage | Notes |
|---|---|---|
OWNd/__init__.py |
100% | Package initialization and version metadata |
OWNd/connection.py |
100% | Hardened dual-session TCP engine, SHA-1/HMAC auth, keepalives & bounded read loops |
OWNd/discovery.py |
100% | SSDP multicast and UPnP XML gateway discovery and descriptor parsing |
OWNd/message.py |
100% | OpenWebNet frame parsers, encoders, and WHO dimension decoders |
OWNd/profiles.py |
100% | Declarative hardware gateway models (F454, MH200N, MH201, MH202, MyHomeServer1) |
OWNd/transport/__init__.py |
100% | Transport subpackage exports |
OWNd/transport/base.py |
100% | Abstract transport layer and event listener notification contracts |
OWNd/transport/serial.py |
100% | Async Serial/USB transport for Legrand 3578 interface with in-band demux |
OWNd/transport/tcp.py |
100% | Dual-session TCP transport linking event and command channels |
Live Test Execution: View detailed line-by-line coverage and test history on Codecov (OpenWebNet-HA/OWNd) or download the interactive coverage report from the CI GitHub Actions run.
Release files for OWNd 2.0.0b6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ownd-2.0.0b6.tar.gz | 90.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ownd-2.0.0b6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 142.2 kB
Release files / ownd-2.0.0b6.tar.gz
| Download URL | ownd-2.0.0b6.tar.gz |
|---|---|
| Size | 90.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79cbeec798c5c92fd725400dcaa708385bb900ac3cf96048955e07761a40d671
|
|
BLAKE2b-256 checksum How to use checksums |
fdeacdb3bee647bb913fda2918fd5587212f270092f21490f1d1649aca01a255
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logRelease files / ownd-2.0.0b6-py3-none-any.whl
| Download URL | ownd-2.0.0b6-py3-none-any.whl |
|---|---|
| Size | 52.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
901d8cde50c95ad60ee0623fbccd43704fb2e5e4cc1deb711f66c2788a90e807
|
|
BLAKE2b-256 checksum How to use checksums |
bf33743923fc1ed8707ffe3b424193475cb608d8f77649255e2502ed1f858579
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency log