An async implementation of the EnOcean Serial Protocol Version 3.
Project description
enocean-async
A light-weight, asynchronous, fully typed Python library for communicating with EnOcean devices over a USB gateway. Based on pyserial-asyncio-fast and the EnOcean Serial Protocol Version 3 (ESP3).
Note: The API may still change (even significantly!). Feedback and contributions are welcome.
Features
Receive pipeline — observables
Incoming radio telegrams are decoded into typed StateChange objects at the top of the pipeline. Callbacks are available at every stage for lower-level access:
# Stage 4 — semantic: one StateChange per observable per device
gateway.add_state_change_callback(lambda sc: print(sc))
# StateChange(device_address=…, observable_uid='temperature', value=21.3, unit='°C', channel=None, …)
# Stage 3 — decoded EEP message (field values + semantic entities)
gateway.add_eep_message_received_callback(lambda msg: ..., sender_filter=eurid)
# Stage 2 — parsed ERP1 telegram (RORG, sender, raw payload bits)
gateway.add_erp1_received_callback(lambda erp1: ...)
# Stage 1 — raw ESP3 packet (before any parsing)
gateway.add_esp3_received_callback(lambda pkt: ...)
Observable UIDs are stable string constants defined in ObservableUID (temperature, illumination, switch_state, position, cover_state, window_state, energy, power, …). Multi-channel actuators include a channel field so individual outputs can be distinguished.
Send pipeline — typed actions
Commands are sent to devices using typed Action objects:
from enocean_async.capabilities.cover_actions import SetCoverPositionAction
from enocean_async.capabilities.action_uid import ActionUID
await gateway.send_command(destination=device_eurid, action=SetCoverPositionAction(position=75))
Device management
gateway.add_device(address=eurid, eep=EEP.from_string("D2-05-00"), name="Living room blind")
Learning / teach-in
await gateway.start_learning(timeout_seconds=60)
# Gateway accepts UTE teach-in (with automatic response); 4BS teach-in, and 1BS teach-in are NOT YET SUPPORTED
gateway.stop_learning()
Gateway utilities
- Retrieve EURID, Base ID and firmware version info
- Change the Base ID
- Auto-reconnect: when the serial connection is lost, the gateway retries for up to 1 hour
What works
- Full receive pipeline: raw serial bytes → ESP3 → ERP1 → EEP decode → capabilities →
StateChangecallbacks - Full send pipeline: typed
Action→EEPHandler.encode()→ ERP1 → ESP3 → serial - Device registration with per-device EEP and capability instantiation
- Learning mode: UTE teach-in (query parsing + automatic bidirectional response)
- Auto-reconnect on connection loss
- EURID, Base ID, firmware version retrieval; Base ID change
- Parsing of all EEPs listed in SUPPORTED_EEPS.md
- Sending commands for: D2-05-00 (covers), D2-20-02 (fan), A5-38-08 (dim gateway), D2-01 (switches/dimmers)
What is missing / not yet implemented
- ECID sub-dispatch for D2-01 extended commands
- More EEPs (contributions welcome — see SKILLS.md for the step-by-step guide)
- Logging coverage is partial
- 4BS teach-in, 1BS teach-in
Implemented EEPs
See SUPPORTED_EEPS.md.
Architecture
Receive pipeline (observables)
Radio signal
│ serial bytes
▼
EnOceanSerialProtocol3
│ ESP3 framing (sync, CRC, packet type)
▼
ESP3Packet
│ RADIO_ERP1 detection
▼
ERP1Telegram rorg, sender EURID, raw payload bits, rssi
│ EEP profile lookup → EEPHandler.decode()
▼
EEPMessage
.values {field_id → EEPMessageValue} ← EEP spec vocabulary: "TMP", "ILL1", "R1"
.entities {observable_uid → EntityValue} ← semantic vocabulary: "temperature", "illumination"
│ Capability.decode() (one call per capability in device.capabilities)
├── ScalarCapability(observable_uid=TEMPERATURE) → reads entities["temperature"]
├── ScalarCapability(observable_uid=ILLUMINATION) → reads entities["illumination"]
├── CoverCapability → reads entities["position"] + entities["angle"], infers "cover_state"
├── PushButtonCapability → reads values["R1"], values["EB"], … (stateful, no observable_uid)
└── MetaDataCapability → emits rssi, last_seen, telegram_count
│ _emit()
▼
StateChange(device_address, observable_uid, value, unit, channel, timestamp, source)
│ on_state_change callback
▼
Application
Send pipeline (actions)
Application
│ gateway.send_command(destination, action=SetCoverPositionAction(position=75))
▼
Action (typed dataclass, action_uid class variable)
│ EEPSpecification.command_encoders[action.action_uid](action)
▼
EEPMessage
.message_type ← selects which telegram type to encode
.values ← {field_id → EEPMessageValue(raw)} filled in by the encoder
│ EEPHandler.encode()
├── Determine buffer size from field layout
├── Write CMD bits at cmd_offset / cmd_size
└── Write each field's raw value at field.offset / field.size
▼
ERP1Telegram(rorg, telegram_data, sender, destination)
│ .to_esp3()
▼
ESP3Packet
│ Gateway.send_esp3_packet()
▼
Radio signal → Device
See ARCHITECTURE.md for a detailed description of the EEP layer, the capability layer, and the key design decisions.
Contributing
See CONTRIBUTING.
Dependencies
This library has one dependency:
- pyserial-asyncio-fast (BSD-3 licensed)
Technology documentation
- EnOcean Serial Protocol Version 3 (ESP3)
- EnOcean Radio Protocol 1 (ERP1)
- EnOcean Alliance Specifications
- EURID Specification V1.2
- EEP V3.1 (high-level)
- EEPViewer (individual profiles)
Copyright & license
Copyright 2026 Henning Kerstan
Licensed under the Apache License, Version 2.0 (the "License"). See LICENSE file for details.
Project details
Release history Release notifications | RSS feed
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 enocean_async-0.4.1.tar.gz.
File metadata
- Download URL: enocean_async-0.4.1.tar.gz
- Upload date:
- Size: 66.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee0a9df1551ef1b2ce3d6d2f0a5c48c64e737040a67af6ee91f5b4c83e89e720
|
|
| MD5 |
146fc7a423e6bfe15fa4b8da55bc95b2
|
|
| BLAKE2b-256 |
187d9269d4a1fe9b7d09fe365036e0044fea7ed22074698387b66112ffb52e30
|
Provenance
The following attestation bundles were made for enocean_async-0.4.1.tar.gz:
Publisher:
publish.yml on henningkerstan/enocean-async
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enocean_async-0.4.1.tar.gz -
Subject digest:
ee0a9df1551ef1b2ce3d6d2f0a5c48c64e737040a67af6ee91f5b4c83e89e720 - Sigstore transparency entry: 1006719255
- Sigstore integration time:
-
Permalink:
henningkerstan/enocean-async@ad7381f64b2423499d6bf7f8bea2c5070250394a -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/henningkerstan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ad7381f64b2423499d6bf7f8bea2c5070250394a -
Trigger Event:
push
-
Statement type:
File details
Details for the file enocean_async-0.4.1-py3-none-any.whl.
File metadata
- Download URL: enocean_async-0.4.1-py3-none-any.whl
- Upload date:
- Size: 79.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aee8c536299e2a603310d166d0783d7ef842f00a527333629670b900b22a2e82
|
|
| MD5 |
984c2beaa297cc99fa2448f32304facc
|
|
| BLAKE2b-256 |
0938d0f5a6864949a61d5cf571f4a6f755c5c3f2368d53cbac642e3ad9162fd4
|
Provenance
The following attestation bundles were made for enocean_async-0.4.1-py3-none-any.whl:
Publisher:
publish.yml on henningkerstan/enocean-async
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enocean_async-0.4.1-py3-none-any.whl -
Subject digest:
aee8c536299e2a603310d166d0783d7ef842f00a527333629670b900b22a2e82 - Sigstore transparency entry: 1006719256
- Sigstore integration time:
-
Permalink:
henningkerstan/enocean-async@ad7381f64b2423499d6bf7f8bea2c5070250394a -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/henningkerstan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ad7381f64b2423499d6bf7f8bea2c5070250394a -
Trigger Event:
push
-
Statement type: