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.
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. - 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: Designed for Python 3.11+, tested continuously against Python 3.11, 3.12, 3.13, and 3.14.
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%), 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 | 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 | 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 | 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())
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 unit test suite with strict line coverage tracking across all core modules:
| Component / Module | Coverage | Notes |
|---|---|---|
OWNd/__init__.py |
100% | Package initialization and version metadata |
OWNd/discovery.py |
100% | SSDP multicast and UPnP XML gateway discovery and descriptor parsing |
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/tcp.py |
100% | Dual-session TCP transport linking event and command channels |
OWNd/message.py |
100% | OpenWebNet frame parsers, encoders, and WHO dimension decoders |
OWNd/connection.py |
100% | Hardened dual-session TCP engine, SHA-1/HMAC auth, keepalives & bounded read loops |
OWNd/transport/serial.py |
100% | Async Serial/USB transport for Legrand 3578 interface with in-band demux |
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.0b3
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.0b3.tar.gz | 84.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ownd-2.0.0b3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 133.4 kB
Release files / ownd-2.0.0b3.tar.gz
| Download URL | ownd-2.0.0b3.tar.gz |
|---|---|
| Size | 84.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3f5dc8b307bfcce22f3deb3a8ae9a2d8ada951ed979c5cd587a7dec251d11261
|
|
BLAKE2b-256 checksum How to use checksums |
23b93a264494770ba497f610785db0c0440ccf62e71dfeb4b2e8620ce143b153
|
| 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 10, 2026.
Transparency logRelease files / ownd-2.0.0b3-py3-none-any.whl
| Download URL | ownd-2.0.0b3-py3-none-any.whl |
|---|---|
| Size | 49.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
924f6b74e62462133ba7d86af114c56db39ce7066bb4a04346ead8e0909f35e3
|
|
BLAKE2b-256 checksum How to use checksums |
6234ea2f505701c64048e054e5015590442a935cdc19ea2375cd91f530db5a94
|
| 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 10, 2026.
Transparency log