Repository navigation
Serial: pyserial-asyncio -> serialx - #156
Open
markus-becker-tridonic-com wants to merge 12 commits into
Open
markus-becker-tridonic-com wants to merge 12 commits into
markus-becker-tridonic-com wants to merge 12 commits into
Conversation
pyserial-asyncio is deprecated. Switch the RS232 serial drivers to serialx, which provides a compatible create_serial_connection and SerialTransport API, and update the driver-serial and test extras. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lunatone DALI interfaces with an integrated power supply (e.g. the DALI-2 USB PS) leave the bus unpowered by default, so every transmission fails with "error in transmission: 1". Add an opt-in `bus_power` option to DriverLubaRs232 that sets HardwareSettings bit 7 when writing the device settings, switching on the interface's bus power supply. To let callers decide safely (a separate DALI power supply must not clash with the interface's own), implement the two queries needed to detect the situation: - QUERY DEVICE DESCRIPTOR (0x28/0x29): exposes whether the interface has a switchable bus power supply (HardwareFeature bit 7). - READ STATUS (0x2C/0x2D): exposes whether the bus currently has a voltage error, i.e. is unpowered (Status bit 7). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the LUBA device reports an "ADD DALI FRAME TO TX BUFFER" error, no frame reaches the bus, so the "frame sent" event that normally confirms a transmission never arrives. send_dali_command() therefore blocked until the confirmation timeout before failing with a generic error. Surface the rejection immediately: enqueue a transmission confirmation carrying the interface's reason code, and raise a new TransmissionError from send_dali_command() as soon as it is dequeued. The reason codes are enumerated in LubaTxError (LUBA documentation section 2.1.1), letting callers act on specific causes such as an unpowered bus (bus voltage error). Also log frames that cannot be decoded from their transmit echo at debug, including the raw bytes, to aid diagnosis. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The LUBA device echoes each transmitted frame back so the driver can verify it against what it sent. Some valid frames (for example extended DT8 colour queries) cannot yet be round-tripped through Command.from_frame, but the frame was still sent successfully, as the interface itself confirmed. Emitting a warning for every such frame floods the log during normal polling for a purely cosmetic decode gap, so drop it to debug. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DALI-2 USB PS (article nr. 24138246) has been exercised successfully with this driver, so add it to the set of tested article numbers alongside 24166096. Collect them in TESTED_ARTICLE_NUMBERS and widen the "untested device" warning to list every tested number. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A query that received no reply defaulted to a bare Response(None). That response passes check_bad_rsp while its value is None, so a timed-out QueryInstanceType leaked None into control-device autodiscovery and crashed on int(None), aborting driver setup. Default a query to the command's own response type instead, so a timeout is reported as a missing value and check_bad_rsp rejects it as expected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Before each transaction the drivers clear any DALI frame left buffered from a previous exchange. A backward frame that arrives after its query's receive timeout is such a leftover and is expected on a busy bus, yet it was logged at CRITICAL on every occurrence, flooding the log. Two bugs made this worse: the queue was reported as holding N items but only a single item was ever discarded, and the SCI info-queue branch drained the raw-frame queue by mistake, so the info queue was never cleared. Drain each queue fully through a shared helper and log the discard at debug level. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The initial device-info handshake can time out while the bus is busy (for example when an input device is streaming events), which failed the whole connect. Retry the handshake a few times, draining any late LUBA reply left in the RX queue between attempts so it is not mistaken for the next command's response, and only give up after the configured attempt count.
A spontaneous event from an input device can corrupt the backward frame of the first QueryDeviceStatus probe during a scan, which made a present device (and all its instances) look absent and get dropped. Let the bus settle after entering quiescent mode, and retry a status probe that comes back as a framing error while still treating a genuinely missing response as an empty address. Both the settle delay and retry count are tunable.
The serial driver now depends on serialx, which requires Python >=3.10. Raise python_requires accordingly and drop 3.8/3.9 from the CI matrix, since the test extra pulls in serialx and cannot install on older interpreters. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The driver's own event only records that the handshake once succeeded, so an unplugged USB interface still claimed to be connected and every frame ran into its transmit timeout instead of failing fast. For drivers backed by an asyncio transport the protocol, which is told about the loss through connection_lost(), now has the last word. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The serial drivers depend on API introduced in serialx 1.11.0, so raise the floor on both the driver-serial and test extras. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pyserial-asyncio -> serialx
Move from serial_asyncio to serialx (due to the preference of HomeAssistant: https://developers.home-assistant.io/blog/2026/04/27/pyserial-to-serialx?_highlight=serialx.
The new serialx dependency requires 3.10+.
Harden the LUBA RS232 driver and control-device autodiscovery
Summary
A series of robustness fixes for the Lunatone LUBA RS232 driver (
DriverLubaRs232), plus one fix to control-device autodiscovery that the driver changes surface. Together they make the driver usable against real Lunatone hardware with an integrated power supply and a busy, event-streaming bus.What's included
pyserial-asynciotoserialx, which offers a compatiblecreate_serial_connection/SerialTransportAPI. Updates thedriver-serialandtestextras.bus_poweroption): Lunatone interfaces with an integrated PS (e.g. the DALI-2 USB PS) leave the bus unpowered by default, so every frame fails with "error in transmission: 1". Adds the opt-in setting (HardwareSettings bit 7) plus the two queries needed to decide safely whether to switch it on — QUERY DEVICE DESCRIPTOR (switchable-PS capability) and READ STATUS (current bus voltage error).TransmissionErrorcarrying the interface's reason code (LubaTxError) is raised immediately.Response(None), so a timeout is recognised as a missing value rather than leakingNoneinto callers (which crashed autodiscovery onint(None)).QueryDeviceStatusbackward frame, making a present device look absent. The scan now lets the bus settle after entering quiescent mode and retries a framing-error probe while still treating a genuinely missing response as an empty address. Both the settle delay and retry count are tunable.Testing
python3 -m pytest dali/tests/— 118 passed. New coverage added for the timed-out-query response type, the stale-RX drain, the connect-handshake retry, and the autodiscover event-collision handling.