Python module for interacting with ATC BLE firmware on OEPL e-paper tags
Project description
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].ble_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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_atc_ble_oepl-0.3.0.tar.gz.
File metadata
- Download URL: py_atc_ble_oepl-0.3.0.tar.gz
- Upload date:
- Size: 100.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cb4928e3508bab6e73372456e49d7b1e8c4c97d6ff520cfc19e15489bf17c2f
|
|
| MD5 |
4efd611d20f5a67efdadb0502f831ecd
|
|
| BLAKE2b-256 |
500055584c2eed6161a2baf63a683e54224920b912467f0a740b243f458c2907
|
Provenance
The following attestation bundles were made for py_atc_ble_oepl-0.3.0.tar.gz:
Publisher:
release.yml on OpenDisplay/py-atc-ble-oepl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_atc_ble_oepl-0.3.0.tar.gz -
Subject digest:
0cb4928e3508bab6e73372456e49d7b1e8c4c97d6ff520cfc19e15489bf17c2f - Sigstore transparency entry: 1310954439
- Sigstore integration time:
-
Permalink:
OpenDisplay/py-atc-ble-oepl@810f7020a5b2777271a2c092a9ad338289ef4c30 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/OpenDisplay
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@810f7020a5b2777271a2c092a9ad338289ef4c30 -
Trigger Event:
push
-
Statement type:
File details
Details for the file py_atc_ble_oepl-0.3.0-py3-none-any.whl.
File metadata
- Download URL: py_atc_ble_oepl-0.3.0-py3-none-any.whl
- Upload date:
- Size: 38.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7243b5a2a9ea84659561c551800102d7fca2e5ff3f9916209953eb70eae7a1d
|
|
| MD5 |
2884dcbea8182068cff1c48aeb6a580e
|
|
| BLAKE2b-256 |
330862c21ba83e0b341dddda811a305ded32650c4d9b53473182c3a2edc786a9
|
Provenance
The following attestation bundles were made for py_atc_ble_oepl-0.3.0-py3-none-any.whl:
Publisher:
release.yml on OpenDisplay/py-atc-ble-oepl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_atc_ble_oepl-0.3.0-py3-none-any.whl -
Subject digest:
b7243b5a2a9ea84659561c551800102d7fca2e5ff3f9916209953eb70eae7a1d - Sigstore transparency entry: 1310954561
- Sigstore integration time:
-
Permalink:
OpenDisplay/py-atc-ble-oepl@810f7020a5b2777271a2c092a9ad338289ef4c30 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/OpenDisplay
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@810f7020a5b2777271a2c092a9ad338289ef4c30 -
Trigger Event:
push
-
Statement type: