Skip to content

Repository files navigation

Introduction

Serialx is a no-compromise serial communication library for Python targeting common platforms such as Linux (POSIX), macOS, and Windows. It provides both synchronous and native asynchronous APIs for all platforms.

For more information, visit serialx's documentation: https://puddly.github.io/serialx/

Installation

pip install serialx

For drop-in import compatibility (serial, serial_asyncio, serial_asyncio_fast) in environments where existing code cannot be migrated:

pip install serialx-compat

Usage

Serialx features a familiar synchronous API:

importserialxwithserialx.serial_for_url("/dev/serial/by-id/port", baudrate=115200) asserial:
data=serial.readexactly(5)
serial.write(b"test")
serial.set_modem_pins(rts=True, dtr=True)
pins=serial.get_modem_pins()
assertpins.rtsisserialx.PinState.HIGHassertpins.dtrisserialx.PinState.HIGH

An async equivalent of the synchronous API:

importasyncioimportserialxasyncdefmain():
asyncwithserialx.async_serial_for_url(
"/dev/serial/by-id/port", baudrate=115200,
) asserial:
data=awaitserial.readexactly(5)
awaitserial.write(b"test")
awaitserial.set_modem_pins(rts=True, dtr=True)
pins=awaitserial.get_modem_pins()
assertpins.rtsisserialx.PinState.HIGH

A (StreamReader, StreamWriter) pair is also available for code already wired up to the asyncio streams API:

importasyncioimportserialxasyncdefmain():
reader, writer=awaitserialx.open_serial_connection(
"/dev/serial/by-id/port", baudrate=115200,
)
try:
data=awaitreader.readexactly(5)
writer.write(b"test")
awaitwriter.drain()
finally:
writer.close()
awaitwriter.wait_closed()

And a low-level asynchronous serial transport for protocol-style consumers:

importasyncioimportserialxasyncdefmain():
loop=asyncio.get_running_loop()
protocol=YourProtocol()
transport, protocol=awaitserialx.create_serial_connection(
loop,
lambda: protocol,
url="/dev/serial/by-id/port",
baudrate=115200,
)
awaittransport.set_modem_pins(rts=True, dtr=True)

ESPHome serial proxy

Serialx can communicate with serial devices exposed by ESPHome.

It can either create the API instance directly, for simplicity:

fromserialximportopen_serial_connectionreader, writer=awaitopen_serial_connection(
url="esphome://192.168.1.42:6053/?port_name=Zigbee&key=...",
baudrate=115200,
)

Or reuse an existing API instance, for efficiency:

fromaioesphomeapiimportAPIClientfromserialximportopen_serial_connectionfromserialx.platforms.serial_esphomeimportESPHomeSerialTransport# An external API instanceapi=APIClient(address="192.168.1.42", port=6053, key="...", password=None)
awaitapi.connect(login=True)
reader, writer=awaitopen_serial_connection(
url=None,
transport_cls=ESPHomeSerialTransport,
api=api,
port_name="Zigbee",
baudrate=115200,
)

Development

All development dependencies are listed in pyproject.toml. To install them, use:

uv pip install '.[dev]'

On macOS and Windows, a Rust toolchain is required to build the native serial port enumeration extension. Install Rust via rustup.

Set up pre-commit hooks with pre-commit install. Your code will then be type checked and auto-formatted when you run git commit. You can do this on-demand with pre-commit run.

Serialx relies on automated testing. CI runs tests using both socat virtual PTYs (Linux/macOS) and socket-based serial pairs. To also test with physical adapter pairs, pass CLI flags to pytest:

pytest --adapter-pair=/dev/serial/by-id/left1:/dev/serial/by-id/right1 \
--adapter-pair=/dev/serial/by-id/left2:/dev/serial/by-id/right2

By default, tests run in parallel. You can disable this by passing -n 0 to pytest.

About

Serial library with native async support for Linux, macOS, Windows, and WebSerial

Topics

Resources

Stars

29 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages