Skip to content

Getting started

Installation

pip install git+https://github.com/j5lien/enocean.git@2.1.0
# or, in a uv-managed project
uv add git+https://github.com/j5lien/enocean.git --tag 2.1.0
# with Prometheus metrics
pip install "enocean[prometheus] @ git+https://github.com/j5lien/enocean.git@2.1.0"

Finding the module's serial port

System Port
Linux /dev/serial/by-id/usb-EnOcean_GmbH_EnOcean_USB_300_... (stable across reboots), or /dev/ttyUSB0
macOS /dev/cu.usbserial-... (use the cu. device, not tty.)
Docker (Linux host) pass the device: devices: ["/dev/serial/by-id/usb-EnOcean...:/dev/serial/by-id/usb-EnOcean..."]

Receiving telegrams

import queue

from enocean import RORG, SerialCommunicator

communicator = SerialCommunicator(port='/dev/ttyUSB0')
communicator.start()  # a thread reading the port
print('Base ID:', communicator.base_id)

try:
    while communicator.is_alive():
        try:
            packet = communicator.receive.get(timeout=1)
        except queue.Empty:
            continue
        if packet.rorg == RORG.BS4:
            packet.parse_eep(0x02, 0x05)  # A5-02-05: temperature sensor, 0 °C to +40 °C
            print(packet.sender_hex, packet.parsed['TMP']['value'], '°C')
finally:
    communicator.stop()

Instead of polling communicator.receive, pass callback= to be called with each packet (from the communicator thread). Rather than choosing the profile yourself, declare your devices: see Devices and decoding.

Decoding and building telegrams without a module

Packets can be parsed from bytes and built from profile fields directly:

from enocean import Packet

# A temperature sensor telegram (A5-02-05) as read from the serial port
frame = bytes.fromhex('55 00 0a 07 01 eb a5 00 00 55 08 01 81 b7 44 00 01 ff ff ff ff 2d 00 75')

status, remaining, packet = Packet.parse_msg(frame)
print(status.name, packet.sender_hex, packet.dbm)  # OK 01:81:B7:44 -45

packet.parse_eep(0x02, 0x05)  # A5-02-05: temperature sensor, 0 °C to +40 °C
temperature = packet.parsed['TMP']
print(temperature['value'], temperature['unit'])  # 26.66... °C
assert round(temperature['value'], 2) == 26.67
from enocean import RORG, RadioPacket

# D2-01-12, command 1 (Actuator Set Output): switch output channel 1 on
packet = RadioPacket.create(
    rorg=RORG.VLD,
    rorg_func=0x01,
    rorg_type=0x12,
    command=1,
    destination=[0x05, 0x99, 0x77, 0xAF],
    sender=[0xFF, 0xC3, 0x6F, 0x80],  # the module's base ID
    IO=1,
    OV=100,
)
print(bytes(packet.build()).hex(' '))  # the ESP3 bytes written to the module
print(packet.parsed['OV']['value'])  # Output value 1% to 100% or ON: 100
assert packet.data[1:4] == [0x01, 0x01, 0x64]

communicator.send(packet) writes a packet to the module; for lights and blinds, see Controlling actuators.