Skip to content

Allow ESPHome transport to be used across threads and event loops#65

Merged
puddly merged 4 commits intodevfrom
puddly/esphome-threaded-loop
Apr 20, 2026
Merged

Allow ESPHome transport to be used across threads and event loops#65
puddly merged 4 commits intodevfrom
puddly/esphome-threaded-loop

Conversation

@puddly
Copy link
Copy Markdown
Owner

@puddly puddly commented Apr 19, 2026

On async transports, we expose a .serial property that references the sync serial interface. This is strictly for backwards compatibility, as some packages rely on accessing modem pins or the serial port name from this sync API.

Zigpy unfortunately has two weird uses:

  1. bellows runs its serial communication in a separate thread. If we pass the Home Assistant Core aioesphomeapi API client instance by reference, we start awaiting tasks across event loops, breaking everything.
  2. zigpy-znp uses transport.serial.dtr = True to set modem lines (once we migrate zigpy to use serialx strictly, this will be transformed into await transport.set_modem_lines(dtr=True)). This calls a sync API from an async context, which runs an async operation synchronously in the parent event loop using the parent event loop. This is an instant asyncio deadlock. Worse yet, Home Assistant registers signal handlers and makes this deadlock worse by being unkillable with CTRL-C and logging absolutely nothing past this point.

To work around these issues, I've made some tweaks:

  1. We support cross-loop and thread-safe operation by using the appropriate asyncio helper functions asyncio.run_coroutine_threadsafe, etc. This change has optimizations for when the API instance's loop is the same as the current loop.
  2. The synchronous set_modem_pins can now be called from an async context but it no longer blocks at all, returning instantly. We log a deprecation warning here.
  3. get_modem_pins throws a RuntimeError. There's no sensible way to implement this.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 19, 2026

Codecov Report

❌ Patch coverage is 98.80952% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.49%. Comparing base (5e8b1d6) to head (00dcd33).
⚠️ Report is 1 commits behind head on dev.

Files with missing lines Patch % Lines
serialx/platforms/serial_esphome.py 98.80% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev      #65      +/-   ##
==========================================
+ Coverage   90.21%   90.49%   +0.27%     
==========================================
  Files          19       19              
  Lines        3087     3145      +58     
==========================================
+ Hits         2785     2846      +61     
+ Misses        302      299       -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@puddly puddly merged commit 4242e3e into dev Apr 20, 2026
38 checks passed
@puddly puddly deleted the puddly/esphome-threaded-loop branch April 20, 2026 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant