Pure Python async/await implementation for controlling EASYWAVE radio modules (RX11, RX21, RX22, RX25, RX09)
Project description
EASYWAVE Home Control
Pure Python async/await implementation for controlling EASYWAVE radio modules (RX11, RX21, RX22, RX25, RX09).
Optimized for Home Assistant integration with 100% non-blocking async API.
Features
- Pure Async/Await - No blocking calls on event loop, optimized for Home Assistant
- Multi-Family Support - Binary protocol (RX11/21/22/25) + ASCII protocol (RX09)
- High Performance - 115200 baud communication, request pipelining
- Robust - Comprehensive error handling, health checks, timeout management
- Clean API - Unified
.create()factory method for all devices - Type Hints - Full Pylance strict-mode compliant type annotations
- Well-Tested - Comprehensive examples and integration patterns
Installation
pip install easywave-home-control
Quick Start
Creating Devices
The easiest way is the AsyncDeviceFactory — or use the device class directly for full type safety:
import asyncio
from easywave_home_control import AsyncDeviceFactory, RX11Device
async def main():
# Option A: Factory (string-based, flexible)
device = await AsyncDeviceFactory.create("RX11", port="/dev/ttyUSB0", timeout=5.0)
# Option B: Device class directly (type-safe, recommended for typed code)
# device = await RX11Device.create(port="/dev/ttyUSB0", timeout=5.0)
try:
# Device is now connected and ready to use
info = await device.get_device_info()
print(f"Device: {info}")
# Check connectivity
connected = await device.ping_request(timeout=3.0)
print(f"Connected: {connected}")
# Use device-specific methods
result, hw_version = await device.query_hw_version() # hw_version: str
if result == RX11ErrorCode.SUCCESS:
print(f"Hardware Version: {hw_version}")
finally:
# Always disconnect when done
await device.disconnect()
asyncio.run(main())
Device Support
RxModule Family (Binary Protocol, 115200 baud)
| Device | Type | Functions | Features | Baudrate |
|---|---|---|---|---|
| RX11 | USB Transceiver | 25 | EW, Bidi, Secwave | 115200 |
| RX21 | Serial Module | 25 | EW, Bidi, Secwave | 115200 |
| RX22 | Serial Module | 25 | EW, Bidi, Secwave | 115200 |
| RX25 | Serial Module | 25 | EW, Bidi, Secwave | 115200 |
RX09 Family (ASCII Protocol, 57600 baud)
| Device | Type | Functions | Features | Baudrate |
|---|---|---|---|---|
| RX09 | Basic Receiver | 11 | EW Basic only | 57600 |
Async API Documentation
Device Classes
RxModule Devices (Binary Protocol, 115200 baud)
from easywave_home_control import RX11Device, RX21Device, RX22Device, RX25Device
# All RxModule devices use the same interface
device = await RX11Device.create(port="/dev/ttyUSB0")
# RxModule-specific methods
result, hw_version = await device.query_hw_version() # hw_version: str, e.g. "RX11 V1.3"
result, fw_version = await device.query_fw_version() # fw_version: str, e.g. "1.4"
# Easywave Basic (EW) functions
result, info_type, transmitter, info_data = await device.ew_rcv_button_request(timeout=30.0)
await device.ew_send_cmd_request(gateway=gateway_serial, button=0)
# Easywave Bidi (EWB) functions
result, receiver = await device.ewb_join_device_request(transmitter_serial)
# Check error codes
if result == RX11ErrorCode.SUCCESS:
print(f"Success: {info_type}")
else:
print(f"Error: {result}")
RX09 Device (ASCII Protocol, 57600 baud)
from easywave_home_control import RX09Device
device = await RX09Device.create(port="/dev/ttyUSB0")
# RX09-specific methods
result, serial_number, button_code = await device.receive_telegramm(timeout=30.0)
result, position_count = await device.query_positions()
result = await device.send_telegramm(position=0, button_code="A")
# Check error codes
if result == RX09ErrorCode.SUCCESS:
print("Success")
Common Methods (All Devices)
# Connection management
# Note: .connect() is called automatically by .create() — only needed for direct instantiation
await device.connect() -> bool
await device.disconnect() -> None
# Device info
await device.get_device_info() -> dict[str, Any]
await device.ping_request(timeout=5.0) -> bool
# Properties (common)
device.is_connected -> bool
RxModule-Only Properties (RX11 / RX21 / RX22 / RX25)
# Detailed status — one of: "connected", "disconnected", "reconnecting", "error", "hardware_error"
device.connection_status -> str
device.has_hardware_error -> bool # True after USB disconnect or I/O error
device.state_good -> bool # False if protocol errors have occurred
device.last_error -> str | None # Last error message, if any
Error Codes
Each device family has device-specific error code enums:
# RxModule devices (RX11, RX21, RX22, RX25)
from easywave_home_control import RX11Device, RX11ErrorCode
if result == RX11ErrorCode.SUCCESS:
print("Command successful")
elif result == RX11ErrorCode.ERR_RF_TIMEOUT:
print("RF communication timeout")
elif result == RX11ErrorCode.ERR_FAILSTATE:
print("Device in failure state")
# RX09 device
from easywave_home_control import RX09Device, RX09ErrorCode
if result == RX09ErrorCode.SUCCESS:
print("Command successful")
Callbacks
Register callbacks to respond to device events:
def on_disconnect():
print("Device disconnected or hardware error occurred")
device.set_disconnect_callback(on_disconnect)
# Register callback for spontaneous RCV messages (RX09 only)
def on_button_pressed(data: dict[str, Any]):
serial = data["serial_number"]
button = data["button_code"]
print(f"Button {button} pressed on {serial:06X}")
device.register_rcv_callback(on_button_pressed)
Examples
See the examples/ directory for complete working examples:
- ew_send_receive_example.py - Discover gateways, send commands, receive button presses (RX11 & RX09)
- ewb_pairing_example.py - Easywave Bidi device pairing workflow
- ha_integration_full_example.py - Home Assistant integration pattern with background listening
Protocol Details
RxModule Protocol (RX11/21/22/25)
Binary request/response protocol at 115200 baud with:
- Packet structure:
[SOP(0x81)] [Function+Params] [EOP(0x82)] - Byte stuffing for bytes in range 0x80-0x82
- Request/response IRP/ICP pairs with handles
- Timeout support (default 5.0s, RCV functions support indefinite waiting)
RX09 Protocol (RX09)
ASCII text-based protocol at 57600 baud with:
- Comma-separated commands (e.g.,
CMD,PARAM1,PARAM2) - CR (0x0D) line terminator
- Simple request/response matching
- Limited function set (Easywave Basic only)
Type Hints & Validation
The library is fully typed and passes Pylance strict-mode validation:
pylance --mode=strict
All generic types are explicitly specified (no Unknown types):
dict[str, Any]instead ofdictlist[bytes]instead oflistasyncio.Task[None]instead ofasyncio.Task
Requirements
- Python 3.9+
- pyserial >= 3.5
License
Apache License 2.0 - See LICENSE for details.
Contributing
Contributions are welcome! Please submit issues and pull requests to the Home Assistant repository.
Support
For support, please refer to the Home Assistant Easywave Integration documentation.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file easywave_home_control-0.1.2.tar.gz.
File metadata
- Download URL: easywave_home_control-0.1.2.tar.gz
- Upload date:
- Size: 36.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c852d7f5be28337440b5bc91c536d6a0f01c5e433deabb572f5d2468bc58c735
|
|
| MD5 |
d37a192355417f5f5ce70e64057116b3
|
|
| BLAKE2b-256 |
e83fdcf249984214beea303255ccc6547e267f3b935fdf54437e376f397abb64
|
Provenance
The following attestation bundles were made for easywave_home_control-0.1.2.tar.gz:
Publisher:
publish.yml on eldateas/PyPI-EasywaveHomeControl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easywave_home_control-0.1.2.tar.gz -
Subject digest:
c852d7f5be28337440b5bc91c536d6a0f01c5e433deabb572f5d2468bc58c735 - Sigstore transparency entry: 2057497390
- Sigstore integration time:
-
Permalink:
eldateas/PyPI-EasywaveHomeControl@875048f5517c14fdf4bd53ebb780b31c87438640 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/eldateas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@875048f5517c14fdf4bd53ebb780b31c87438640 -
Trigger Event:
push
-
Statement type:
File details
Details for the file easywave_home_control-0.1.2-py3-none-any.whl.
File metadata
- Download URL: easywave_home_control-0.1.2-py3-none-any.whl
- Upload date:
- Size: 38.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bc4d31da9863ac29c4fadd9053e08370d2c3e8d0155305d823d3316307ba092
|
|
| MD5 |
9527d5650e344bf03010b0ce31ebda0e
|
|
| BLAKE2b-256 |
e1a8ae3194fa50f5c7b94abd0997b972515047e76ddcf7dc2ca6a0357c704d78
|
Provenance
The following attestation bundles were made for easywave_home_control-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on eldateas/PyPI-EasywaveHomeControl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easywave_home_control-0.1.2-py3-none-any.whl -
Subject digest:
0bc4d31da9863ac29c4fadd9053e08370d2c3e8d0155305d823d3316307ba092 - Sigstore transparency entry: 2057497554
- Sigstore integration time:
-
Permalink:
eldateas/PyPI-EasywaveHomeControl@875048f5517c14fdf4bd53ebb780b31c87438640 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/eldateas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@875048f5517c14fdf4bd53ebb780b31c87438640 -
Trigger Event:
push
-
Statement type: