Skip to main content

eDIDIO Control Python Library

A Python library for communicating with and controlling the Control Freak eDIDIO S10 lighting controller.

Features

  • Asynchronous TCP and TLS communication with eDIDIO controllers
  • Support for DMX and DALI (including DALI DT8 CCT) message creation
  • High-level helpers for DALI group levels, standard commands, and scene recall
  • SpektraPlus control (start/stop/pause sequences, themes, and static scenes)
  • Authoring (0.4.0): create/save SpektraPlus sequences and themes, and schedules/alarms (create_spektra_sequence_message, create_spektra_theme_message, create_alarm_message), plus read/query builders (create_spektra_read_message, create_read_device_message, create_diagnostic_message) and a request() helper that decodes the reply
  • Live events + state (0.5.0): subscribe to the controller's pushed event stream (edidio_control_py.events), turn DALI bus traffic into light levels (edidio_control_py.state), and a shared thread-safe gateway dispatcher with an optional state feed (edidio_control_py.gateway)
  • Connection management including keep-alive and auto-reconnect
  • Async context manager support

Installation

pip install edidio_control_py

Quick Start

import asyncio
from edidio_control_py import EdidioClient

async def main():
    async with EdidioClient("192.168.1.10", 23) as client:
        # Set a DALI device to 50% brightness (arc level 127)
        await client.set_dali_arc_level(
            message_id=1, line_mask=1, address=0, arc_level=127
        )

asyncio.run(main())

Connecting

Plain TCP (port 23)

client = EdidioClient("192.168.1.10", 23)
await client.connect()

TLS (port 443)

client = EdidioClient("192.168.1.10", 443, use_tls=True)
await client.connect()

By default, TLS uses the system CA bundle for certificate verification. If your device uses a self-signed certificate, pass a custom SSL context:

import ssl

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

client = EdidioClient("192.168.1.10", 443, use_tls=True, ssl_context=ctx)
await client.connect()

Async Context Manager

The client supports async with, which automatically connects and disconnects:

async with EdidioClient("192.168.1.10", 443, use_tls=True) as client:
    await client.set_dali_arc_level(message_id=1, line_mask=1, address=0, arc_level=254)

API Reference

EdidioClient(host, port, timeout=5.0, *, use_tls=False, ssl_context=None)

Parameter Type Description
host str IP address or hostname of the eDIDIO device
port int Port number — typically 23 (TCP) or 443 (TLS)
timeout float Timeout for network operations in seconds (default: 5.0)
use_tls bool Enable TLS (default: False)
ssl_context ssl.SSLContext | None Custom SSL context for TLS connections

Methods

await client.connect()

Establishes a TCP/TLS connection and starts the keep-alive task.

await client.disconnect()

Closes the connection and cancels the keep-alive task.

await client.set_dali_arc_level(message_id, line_mask, address, arc_level)

Sets a DALI device brightness. arc_level is clamped to 0–254.

await client.set_dmx_level(message_id, zone, universe_mask, channel, level, fade_time_by_10ms=0)

Sends a DMX level command. level is a list of channel values.

await client.set_dali_group_arc_level(message_id, line_mask, group, arc_level)

Sets a whole DALI group's brightness. arc_level is clamped to 0–254.

await client.send_dali_command(message_id, line_mask, address, command, arg=0)

Sends a standard DALI command to an address. command is a DALICommandType value (e.g. DALICommandType.DALI_OFF, DALI_MAX_LEVEL, DALI_FADE_UP); arg carries a command argument where applicable.

await client.recall_dali_scene(message_id, line_mask, scene)

Recalls a stored DALI scene across all fittings on the given line(s).

await client.recall_dali_scene_on_group(message_id, line_mask, group, scene)

Recalls a stored DALI scene on a specific group.

await client.send_spektra_control(message_id, spektra_type, zone, index, action)

Starts/stops/pauses a SpektraPlus sequence, theme, or static scene on a zone. spektra_type is a SpektraTargetType value (SEQUENCE, THEME, STATIC); action is a SpektraActionType value (START, STOP, PAUSE).

await client.send_spektra_stop(message_id, zone, line_mask=0xFF)

Stops SpektraPlus playback on a zone and turns the output off (matching how the SpektraPlus app stops playback).

await client.send_dali_commands_sequence(commands)

Sends a list of pre-built DALI protobuf byte messages with a 50ms gap between each.

await client.send_protobuf_message(message)

Sends a raw framed protobuf message (prefixed with 0xCD + 2-byte length).

await client.receive_protobuf_response()

Reads and returns the next protobuf payload from the device, stripping the frame header.

EdidioClient.create_dali_message(message_id, line_mask, address, *, ...)

Static method. Builds and frames a DALI protobuf message. Exactly one action field must be provided (e.g. command, custom_command, query, type8, etc.).

EdidioClient.create_dmx_message(message_id, zone, universe_mask, channel, repeat, level, fade_time_by_10ms=0)

Static method. Builds and frames a DMX protobuf message.

EdidioClient.create_spektra_control_message(message_id, spektra_type, zone, index, action)

Static method. Builds and frames a SpektraPlus control message.

EdidioClient.create_spektra_stop_message(message_id, zone, line_mask=0xFF)

Static method. Builds and frames a SpektraPlus stop external-trigger message.

Enums

For convenience the protocol enums are re-exported from the package, so you don't need to reach into the generated protobuf module:

from edidio_control_py import (
    DALICommandType,
    CustomDALICommandType,
    SpektraTargetType,
    SpektraActionType,
)

await client.send_dali_command(1, line_mask=1, address=0, command=DALICommandType.DALI_MAX_LEVEL)
await client.send_spektra_control(2, SpektraTargetType.SEQUENCE, zone=1, index=0, action=SpektraActionType.START)

Properties

Property Type Description
client.connected bool True if currently connected
client.host str Host address
client.port int Port number
client.use_tls bool True if TLS is enabled

Live events & state (0.5.0)

The controller can push events — every DALI frame on the bus, input presses, sensor changes, SpektraPlus playback, commands — to a subscribed client. This is how integrations report the real light state, including changes made by wall panels, schedules, SpektraPlus or other apps.

Event Stream v2 needs firmware ≥ 1.4.0; older firmware can use the legacy EventMessage path (EventStream(..., use_v2=False), no DALI-level decoding).

EventStream — a dedicated push connection

from edidio_control_py.events import EventStream

async def on_event(ev: dict):
    print(ev["kind"], ev)          # dali / input / sensor / spektra / command / ...

stream = EventStream("192.168.1.50", on_event=on_event)
await stream.start(["dali", "inputs", "sensors"])
...
stream.recent(since_seq=0)          # rolling buffer, for pollers
await stream.stop()

It uses its own connection (never interferes with request/response), skips idle timeouts, and on connection loss reconnects with backoff and resubscribes (stream.reconnects, stream.connected).

state — DALI frames → levels

from edidio_control_py.state import LevelTracker, dali_change

tracker = LevelTracker(groups={(1, 3): [5, 6]})   # optional group membership
change = dali_change(ev)          # DaliChange(line, address, level, scene, command) or None
if change:
    touched = tracker.apply(change)   # [(line, address, level), ...]

Addresses use the eDIDIO convention (0–63 short, 64 + g group, 80 broadcast); lines are 1-based. level is None when the frame doesn't state one (RECALL MIN, GO TO SCENE). Broadcast frames update every known target on the line.

Only frames that really happened count: a TX frame (direction 0) must have status SUCCESS (0) — ATTEMPT/TIMEOUT/COLLISION etc. are ignored, so a command sent with no bus power never shows as a level — and RX frames (direction 1, which use their own frame_type numbering: 4 = 16-bit) must be successfully received. The controller reports its own transmissions twice (TX SUCCESS + RX echo); LevelTracker drops the repeat. Decoded DALI events carry ok and, for TX, status_name. Verified on firmware 1.6.2.

gateway.EdidioDispatcher — shared by every Python bridge

from edidio_control_py.gateway import EdidioDispatcher

async def on_state(change, touched):
    for line, address, level in touched:
        publish(line, address, level)

d = EdidioDispatcher("192.168.1.50", on_state=on_state)
await d.start()
d.submit({"kind": "dali_level", "line": 1, "address": 5, "level": 200})  # any thread

Intents: dali_level, dali_group_level, dali_scene, dali_command, spektra, spektra_stop, dmx_color (see the module docstring). With on_state/on_event it also runs an EventStream and keeps d.levels current; if the stream can't start, commands still work.

Exceptions

All exceptions are defined in edidio_control_py.exceptions.

Exception Description
EDIDIOConnectionError Base exception for connection failures
EDIDIOCommunicationError Error during send/receive (subclass of EDIDIOConnectionError)
EDIDIOTimeoutError Operation timed out (subclass of EDIDIOCommunicationError)
EDIDIOInvalidMessageError Received a malformed message (subclass of EDIDIOCommunicationError)
from edidio_control_py.exceptions import EDIDIOConnectionError, EDIDIOTimeoutError

try:
    await client.connect()
except EDIDIOTimeoutError:
    print("Connection timed out")
except EDIDIOConnectionError as e:
    print(f"Connection failed: {e}")

Requirements

  • Python >= 3.9
  • protobuf >= 5.29.2 (the generated modules check the runtime version at import)

License

MIT

Metadata

Release files for edidio-control-py 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for edidio-control-py 0.5.0
File Size Uploaded Provenance
edidio_control_py-0.5.0.tar.gz 123.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for edidio-control-py 0.5.0
File Interpreter ABI Platform
edidio_control_py-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 241.3 kB

Release files / edidio_control_py-0.5.0.tar.gz

Download URL edidio_control_py-0.5.0.tar.gz
Size 123.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7ac6c4689807bf6a96def8380fa218a65716b90e3910f5893cb448b798c18ac7
BLAKE2b-256 checksum
How to use checksums
d56ddb58cfac9cb1531fec794392ee3e2f7a5103e96d200366314115b1079fcd
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 29, 2026.

Transparency log

Release files / edidio_control_py-0.5.0-py3-none-any.whl

Download URL edidio_control_py-0.5.0-py3-none-any.whl
Size 118.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1d678250746dcaeb913f63a925a6cec84b27225236355dcbcb3fc5e24c9331dc
BLAKE2b-256 checksum
How to use checksums
811100a98132ef3544824efe2a1229b81485a0f338a71662282b7002342b9fd6
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 29, 2026.

Transparency log

Security for edidio-control-py 0.5.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for edidio_control_py-0.5.0.tar.gz

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 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page