Skip to main content

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 a non-blocking async API and a codec layer for typed telegram/state parsing.

What's New in 0.2.1

  • Sensor codec fix (STH01) — correct EWneo type mapping and /100 scaling for temperature and humidity
  • Legacy RX21 format — optional SensorPayloadFormat.LEGACY_RX21 for Table 6 sensors (/10, swapped type codes)
  • 55 unit tests — including dedicated sensor payload coverage

What's New in 0.2.0

  • EWB codec — parse/encode state for all seven neo device types (switch, dimmer, motor, dual/quad variants)
  • Secwave codec — IRP helpers and parsers for SEC_RCV, SEC_LEARN, SEC_SEND, SEC_REPLY_QUERY (aligned with RX21 spec)
  • Easywave Basic codec — button-byte encoding and parse_ew_rcv_ex_result for EW telegrams
  • Protocol fixes — corrected Secwave IRP/ICP layouts; spec-accurate RX09 response parsing
  • Examples & tests — updated HA/coordinator patterns; 45 unit tests

Features

  • Pure Async/Await — no blocking calls on the event loop
  • Multi-Family Support — binary protocol (RX11/21/22/25) + ASCII protocol (RX09)
  • Codec Layer — typed parse/encode instead of raw byte juggling in integrations
  • Robust — error handling, health checks, timeout management
  • Clean API — unified .create() factory method for all devices
  • Type Hints — Pylance strict-mode compliant annotations

Installation

pip install easywave-home-control

Quick Start

import asyncio
from easywave_home_control import RX11Device, RX11ErrorCode

async def main():
    device = await RX11Device.create(port="/dev/ttyUSB0", timeout=5.0)
    try:
        connected = await device.ping_request(timeout=3.0)
        print(f"Connected: {connected}")

        result, hw_version = await device.query_hw_version()
        if result == RX11ErrorCode.SUCCESS:
            print(f"Hardware: {hw_version}")
    finally:
        await device.disconnect()

asyncio.run(main())

Codec Layer

The library separates protocol transport (IRP/ICP over serial) from codec parsing (state words, telegrams, sensor payloads). Home Assistant integrations typically keep device mapping and persistence in the integration layer and use the codec for encode/decode.

Easywave Bidi (EWB neo)

from easywave_home_control import parse_ewb_rcv, parse_ewb_state, encode_ewb_state
from easywave_home_control.codec import StateDirection, SwitchChangeCommand, SwitchDesiredAction
from easywave_home_control.protocols.rx11_rx2x.protocol import DeviceType

# Listen loop (after ewb_rcv_request)
event = parse_ewb_rcv(info_type, serial, info_data, device_type=DeviceType.EWB_DT_SWITCH)

# Query / change state
state = parse_ewb_state(device_type, mode, raw_bytes)
raw = encode_ewb_state(
    device_type, 0,
    SwitchChangeCommand(action=SwitchDesiredAction.ON),
    direction=StateDirection.TO_DEVICE,
)

Supported device types: switch, dual/quad switch, dimmer, motor, dual/quad motor.

DATA sensors (STH01, EWneo)

from easywave_home_control.codec import MeasurementType, SensorPayloadFormat, parse_sensor_payload

payload = parse_sensor_payload(info_data)  # default: SensorPayloadFormat.NEO
if payload.measurement_type == MeasurementType.TEMPERATURE:
    print(payload.temperature_celsius)
elif payload.measurement_type == MeasurementType.HUMIDITY:
    print(payload.humidity_percent)

# Legacy RX21 Table 6 sensors (/10, swapped wire types):
legacy = parse_sensor_payload(info_data, payload_format=SensorPayloadFormat.LEGACY_RX21)

ST01/ST02, SH01, SL01, and RTS40 use classic button telegrams, not this 8-byte payload.

Easywave Basic (EW)

from easywave_home_control.codec import easywave, parse_ew_rcv_ex_result

button_byte = easywave.encode_send_button_byte(easywave.EasywaveSendButton.A)
await device.ew_send_cmd_request(gateway_serial, button_byte)

error_code, event = parse_ew_rcv_ex_result(await device.ew_rcv_ex_request())

Secwave

from easywave_home_control import secwave

error_code, telegram = secwave.parse_sec_rcv_result(await device.sec_rcv_request())

params = secwave.encode_sec_send_cmd_tel_params(
    secwave.SecSendCmdTelRequest(
        button_number=0,
        query=secwave.SecQuery(wants_reply=True, reply_only=False),
        command=int(secwave.SecwaveCommand.OPEN),
        flags=secwave.SecTransmitterFlags(mobile=False, low_battery=False),
    )
)
result, primary, secondary = await device.sec_send_cmd_tel_request(params)
response = secwave.parse_sec_send_response(primary, secondary, queried_reply=True)

See examples/ and examples/README.md for full workflows.

Device Support

RxModule Family (Binary Protocol)

Device Type EW EWB Secwave Baudrate
RX11 USB Transceiver 115200
RX21 Serial Module 115200
RX22 Serial Module 115200
RX25 Serial Module 115200

RX09 Family (ASCII Protocol, Easywave Basic only)

Device Type Baudrate Notes
RX09 Basic Transceiver 57600 Spec-accurate ASCII command parsing

Async API

RxModule Devices

from easywave_home_control import RX11Device, RX11ErrorCode

device = await RX11Device.create(port="/dev/ttyUSB0")

# Easywave Basic
result, info_type, serial, info_data = await device.ew_rcv_ex_request(timeout=30.0)
await device.ew_send_cmd_request(gateway=gateway_serial, button=0)

# Easywave Bidi
result, device_type, receiver = await device.ewb_join_device_request(gateway_serial)
result, mode, state = await device.ewb_query_state_request(gateway, receiver, desired_mode=0)

# Secwave
result, stor_index, *_ = await device.sec_rcv_request(timeout=30.0)
await device.sec_learn_request(user_data=0x00000001, timeout=30.0)

RX09 Device

from easywave_home_control import RX09Device, RX09ErrorCode

device = await RX09Device.create(port="/dev/ttyUSB0")

result, serial_number, button = await device.receive_telegramm(timeout=30.0)
result, num_positions = await device.query_positions()
result = await device.send_telegramm(position=0, button="A")

Common Methods (All Devices)

await device.connect()          # called automatically by .create()
await device.disconnect()
await device.get_device_info()
await device.ping_request(timeout=5.0)
device.is_connected

RxModule Status Properties

device.connection_status   # "connected", "disconnected", "reconnecting", "error", "hardware_error"
device.has_hardware_error
device.state_good
device.last_error

Examples

Example Description
ew_send_receive_example.py EW gateway discovery, send, receive with codec
ewb_pairing_example.py EWB join, query, change, listen
secwave_example.py Secwave listen, learn, send, reply
ha_integration_full_example.py HA coordinator pattern (restore, listen, turn_on)

Testing

pip install -e ".[dev]"
pytest tests/ -v

Protocol Details

RxModule (RX11/21/22/25)

Binary protocol: [SOP 0x81] [Function + Params] [EOP 0x82] with byte stuffing, IRP/ICP request/response pairs, and optional indefinite timeouts on receive functions.

RX09 (RTR09)

ASCII protocol at 57600 8N1: comma-separated commands, \r terminator, responses such as ID,<vendor>,<device>,<version> and spontaneous REC,<serial>,<button>.

Requirements

  • Python 3.9+
  • pyserial >= 3.5

License

Apache License 2.0 — see LICENSE.

Contributing

Contributions are welcome! Please submit issues and pull requests to the Home Assistant repository.

Support

For integration guidance, see 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

easywave_home_control-0.2.1.tar.gz (52.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

easywave_home_control-0.2.1-py3-none-any.whl (63.5 kB view details)

Uploaded Python 3

File details

Details for the file easywave_home_control-0.2.1.tar.gz.

File metadata

  • Download URL: easywave_home_control-0.2.1.tar.gz
  • Upload date:
  • Size: 52.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for easywave_home_control-0.2.1.tar.gz
Algorithm Hash digest
SHA256 0d1332e54b01d11d23c8880a606eaa67fa15c44890720777635c7c938d60235d
MD5 61757507f9cde7cbb36467ed38f2149d
BLAKE2b-256 ae30d871b72be58d63907f7371cb2133605419e5a3561b13cd934dfd6938f479

See more details on using hashes here.

Provenance

The following attestation bundles were made for easywave_home_control-0.2.1.tar.gz:

Publisher: publish.yml on eldateas/PyPI-EasywaveHomeControl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file easywave_home_control-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for easywave_home_control-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6fbb992a4836a0765601536dd6731cbbbb0487ba6f65e6cf971b4de13ae6b373
MD5 29969455a5bd826d7f63071b61d48f64
BLAKE2b-256 4d7e0a4cadc1a8a9b1f4a49b2cf29aabec261d78285c4f5d8b69cae5e054d92b

See more details on using hashes here.

Provenance

The following attestation bundles were made for easywave_home_control-0.2.1-py3-none-any.whl:

Publisher: publish.yml on eldateas/PyPI-EasywaveHomeControl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page