Skip to main content

Tests PyPI Python Version

py-atc-ble-oepl

Python library for interacting with ATC BLE firmware over Bluetooth Low Energy.

Installation

uv add py-atc-ble-oepl

For the CLI:

uv add "py-atc-ble-oepl[cli]"

Or run the CLI directly without installing into a project:

uvx --from "py-atc-ble-oepl[cli]" atc-ble scan

CLI

atc-ble scan [--timeout 30] [--json]
atc-ble info  --device ADDR [--timeout 60] [--json]
atc-ble led   --device ADDR [--duration 5]
atc-ble upload --device ADDR IMAGE [--dither-mode burkes] [--fit contain] [--rotate 0] [--no-compress]

Scan for nearby devices:

$ atc-ble scan
┌──────────────────────────────────────┬───────────────┬──────────┐
│ Address                              │ Name          │     RSSI │
├──────────────────────────────────────┼───────────────┼──────────┤
│ 5F4CEF52-A1CD-E2EE-011F-F27129B8D4A9 │ ATC_911943    │  -61 dBm │
└──────────────────────────────────────┴───────────────┴──────────┘

Show device info:

$ atc-ble info --device 5F4CEF52-A1CD-E2EE-011F-F27129B8D4A9
5F4CEF52-A1CD-E2EE-011F-F27129B8D4A9
├── Display
│   ├── Resolution    184 × 384
│   └── Color         BWY
└── Hardware
    ├── OEPL type     0x0060
    ├── Screen type   2  (350 HS BWY UC Inverted)
    └── ...

Flash the LED to identify a device physically:

$ atc-ble led --device 5F4CEF52-A1CD-E2EE-011F-F27129B8D4A9
LED done → ATC_911943

Upload an image:

$ atc-ble upload --device 5F4CEF52-... photo.jpg
Upload complete.

ATC tags advertise infrequently — the default --timeout for device commands is 60 s. On macOS the address is a UUID, not a MAC address.

Python API

Discover devices

from py_atc_ble_oepl import discover_atc_devices

devices = await discover_atc_devices(timeout=30.0)
for d in devices:
    print(f"{d.name}  {d.mac_address}  {d.rssi} dBm")

Upload an image

from py_atc_ble_oepl import ATCDevice

async with ATCDevice("AA:BB:CC:DD:EE:FF") as device:
    success = await device.upload_image("photo.jpg")

upload_image accepts a file path (str), raw bytes, or a PIL Image. It automatically:

  • queries device capabilities (dimensions, color scheme)
  • resizes and fits the image
  • dithers to the display's color palette (MONO / BWR / BWY / BWRY)
  • compresses and uploads over BLE

Image options

from py_atc_ble_oepl import ATCDevice, FitMode, Rotation
from epaper_dithering import DitherMode

async with ATCDevice("AA:BB:CC:DD:EE:FF") as device:
    await device.upload_image(
        "photo.jpg",
        dither_mode=DitherMode.BURKES,   # default
        fit=FitMode.COVER,               # STRETCH / CONTAIN / COVER / CROP
        rotate=Rotation.ROTATE_90,       # 0 / 90 / 180 / 270
        compress=True,                   # default
    )

Read device info

from py_atc_ble_oepl import ATCDevice

async with ATCDevice("AA:BB:CC:DD:EE:FF") as device:
    caps = device._capabilities        # DeviceCapabilities
    cfg  = device.device_config        # DeviceConfig (full hardware settings)
    print(f"{caps.width}x{caps.height}  {caps.color_scheme}")
    print(f"OEPL type 0x{cfg.hw_type:04X}  screen_type {cfg.screen_type}")

Pass a discovered device to avoid re-scanning

On macOS bleak can only connect to a device it has already seen during a scan. Passing the BLEDevice object from discovery skips a second scan:

from py_atc_ble_oepl import discover_atc_devices, ATCDevice

devices = await discover_atc_devices(timeout=30.0)
if devices:
    async with ATCDevice(devices[0].mac_address, ble_device=devices[0].device) as device:
        await device.upload_image("photo.jpg")

API reference

ATCDevice

ATCDevice(mac_address, ble_device=None, auto_interrogate=True, connection_timeout=60.0)
Method / Property Description
async interrogate() Query device capabilities (called automatically on connect)
async flash_led(duration=5.0) Flash the LED for duration seconds to identify the device
async upload_image(image, ...) Upload and display an image
width, height Display dimensions in pixels (None before interrogation)
color_scheme ColorScheme enum value (None before interrogation)
device_config DeviceConfig dataclass with full hardware settings

discover_atc_devices(timeout=30.0)

Scans for ATC BLE devices (manufacturer ID 0x1337). Returns list[DiscoveredDevice].

DeviceCapabilities

Field Type Description
width int Display width in pixels
height int Display height in pixels
color_scheme int 0=MONO, 1=BWR, 2=BWY, 3=BWRY

DeviceConfig

Full hardware configuration from the 0011 dynamic config read. Key fields:

Field Type Description
hw_type int OEPL tag type (hex)
screen_type int ATC screen driver type (1–47)
screen_w, screen_h int Physical display dimensions
screen_colors int Color count
black_invert, second_color_invert bool Color plane polarity
epd_pinout, led_pinout, nfc_pinout, flash_pinout dataclass or None GPIO pin assignments

Acknowledgements

This library is based on the work of Aaron (atc1441) - creator of the ATC BLE firmware and the original web uploader for ATC BLE e-paper tags. The BLE protocol implemented here is derived entirely from that web uploader.

Development

git clone https://github.com/OpenDisplay-org/py-atc-ble-oepl.git
cd py-atc-ble-oepl
uv sync --all-extras

uv run pytest tests/ -v
uv run ruff check .
uv run mypy src/py_atc_ble_oepl

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py_atc_ble_oepl-0.5.1.tar.gz (105.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py_atc_ble_oepl-0.5.1-py3-none-any.whl (38.4 kB view details)

Uploaded Python 3

File details

Details for the file py_atc_ble_oepl-0.5.1.tar.gz.

File metadata

  • Download URL: py_atc_ble_oepl-0.5.1.tar.gz
  • Upload date:
  • Size: 105.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for py_atc_ble_oepl-0.5.1.tar.gz
Algorithm Hash digest
SHA256 434c87e7a04e9637a758c0b9bfd3e7dd4ca2ce8b18b1ff390979a11ca5a5b47b
MD5 51627283db91a8ac7b3e49f8492c8856
BLAKE2b-256 fab27abce674a29f5b8c72ebc3b5e303756c5b7c30fd1abf6ece53d72c0932ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_atc_ble_oepl-0.5.1.tar.gz:

Publisher: release.yml on OpenDisplay/py-atc-ble-oepl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file py_atc_ble_oepl-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: py_atc_ble_oepl-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 38.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for py_atc_ble_oepl-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 11e52be8f92883c6abd12ab25df57e136f221d1685a2638d68a426397f3ce469
MD5 a841c902989e1f2137c7b4323c52f034
BLAKE2b-256 25b07603b893fb7bb5ad9a609610309ef62db0da0b2273f64c2e4729e96894f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_atc_ble_oepl-0.5.1-py3-none-any.whl:

Publisher: release.yml on OpenDisplay/py-atc-ble-oepl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page