Async Python client for the OpenDisplay AP (OpenEPaperLink)
Project description
oepl
Async Python client for the OpenEPaperLink Access Point (AP).
- Full async/await API via
aiohttp - Live tag updates over WebSocket
- Image upload with optional client-side dithering
- Raw image download and decoding (G5, zlib, bitmap)
- LED flash control
- CLI for interactive use
Installation
pip install py-oepl
With client-side dithering support:
pip install py-oepl epaper-dithering
Quick start
import asyncio
from oepl import OEPLClient
async def main():
async with OEPLClient("192.168.1.100") as client:
tags = await client.get_tags()
for tag in tags:
print(tag.mac, tag.alias, tag.battery_mv, "mV")
asyncio.run(main())
CLI
Set OEPL_HOST to avoid passing --host every time:
export OEPL_HOST=192.168.1.100
List tags
oepl --host 192.168.1.100 tags
oepl tags --json # machine-readable JSON
oepl tags --watch # live stream via WebSocket
AP info
oepl ap # hardware info + current config
oepl ap --json
Upload an image
oepl upload AABBCCDDEEFF image.png
oepl upload AABBCCDDEEFF image.png --lut fast --rotate 90 --ttl 300
--lut choices: default, no-repeat, fast-no-reds, fast
--rotate choices: 0, 90, 180, 270
--ttl is in seconds; 0 lets the AP use the tag's default sleep interval.
Send a command
oepl cmd AABBCCDDEEFF refresh
oepl cmd AABBCCDDEEFF clear
oepl cmd AABBCCDDEEFF reboot
oepl cmd AABBCCDDEEFF scan
Flash LEDs
oepl led AABBCCDDEEFF --color 255 0 0
oepl led AABBCCDDEEFF --color 0 255 0 --flash-speed 0.5 --flash-count 3
oepl led AABBCCDDEEFF --color 0 0 255 --brightness 3 --repeats 4
Download and decode the stored image
oepl get-image AABBCCDDEEFF # prints decoded JPEG to stdout
oepl get-image AABBCCDDEEFF -o out.jpg # save to file
Tag type definitions are fetched directly from the AP — no internet access required.
Python API
OEPLClient
from oepl import OEPLClient
client = OEPLClient(
host="192.168.1.100",
session=None, # optional: supply an existing aiohttp.ClientSession
reconnect_interval=30.0, # seconds between WebSocket reconnect attempts
)
Use as an async context manager (recommended) or call connect() / disconnect() manually.
Tag operations
# Fetch all tags (paginated); populates internal cache; fires on_tag_update callbacks
tags: list[Tag] = await client.get_tags()
# Upload an image (PIL Image or raw bytes)
from PIL import Image
img = Image.open("label.png")
await client.upload_image(
"AABBCCDDEEFF",
img,
ttl=300, # seconds; 0 = tag default
rotate=Rotation.R90,
lut=LUT.FAST,
)
# Set the alias shown in the AP web UI
await client.set_alias("AABBCCDDEEFF", "my-display")
# Send a command
from oepl import TagCommand
await client.send_tag_cmd("AABBCCDDEEFF", TagCommand.REFRESH)
# Flash LEDs
from oepl.led import Color, LEDPattern, LEDSegment
pattern = LEDPattern([LEDSegment(Color(255, 0, 0))], repeats=3)
await client.set_led("AABBCCDDEEFF", pattern)
# Fetch the tag type definition (served by the AP, works offline)
tag_type = await client.get_tag_type(0x16) # returns TagType | None
# Download and decode the stored image for a tag
from oepl import decode_image
raw = await client.get_image_raw("AABBCCDDEEFF") # bytes | None
if raw and tag_type:
jpeg_bytes = decode_image(raw, tag_type)
AP operations
info = await client.get_sysinfo() # APInfo — hardware/firmware
config = await client.get_ap_config() # APConfig — current settings
await client.save_ap_config(config)
await client.set_time(int(time.time()))
await client.reboot_ap()
Live updates via WebSocket
async with OEPLClient("192.168.1.100") as client:
client.on_tag_update(lambda tag: print("updated:", tag.mac))
client.on_ap_status(lambda s: print("AP free heap:", s.free_heap))
client.on_connection_change(lambda ok: print("connected:", ok))
client.on_log(lambda msg: print("AP log:", msg))
# Callbacks fire as WebSocket messages arrive.
# on_tag_update also fires for each tag returned by get_tags().
tags = await client.get_tags()
await asyncio.sleep(60)
Each on_* method returns an unsubscribe callable:
unsub = client.on_tag_update(my_callback)
# later:
unsub()
Models
| Class | Key fields |
|---|---|
Tag |
mac, alias, hw_type, last_seen, battery_mv, lqi, rssi, channel, content_mode, firmware_version |
APInfo |
alias, env, build_version, ap_version, psram_size, flash_size, has_c6, has_ble |
APConfig |
ap_channel, led, maxsleep, tz, preview, night_start, night_end |
APStatus |
ip, heap, free_heap, run_status, temp, wifi_rssi, record_count |
TagType |
type_id, width, height, bpp, color_table, short_lut |
Enums
from oepl import LUT, Rotation, TagCommand
from oepl.enums import APState, RunStatus
| Enum | Values |
|---|---|
LUT |
NO_REPEAT, DEFAULT, FAST_NO_REDS, FAST |
Rotation |
NONE, R90, R180, R270 |
TagCommand |
CLEAR, REFRESH, REBOOT, SCAN |
APState |
OFFLINE, ONLINE, FLASHING, WAIT_CHECKIN, WAIT_SETTIME, IDLE, DOWNLOADING, NO_RADIO |
Exceptions
from oepl.exceptions import (
OEPLError, # base
OEPLConnectionError, # could not reach AP
OEPLTimeoutError, # request timed out (after retries)
OEPLNotFoundError, # 404
OEPLResponseError, # other non-2xx (has .status and .body)
)
Image dithering
When epaper-dithering is installed, upload_image applies Floyd-Steinberg dithering automatically for BWR displays. Override with:
from oepl import DitherMode, ColorScheme
await client.upload_image(
mac,
pil_image,
dither_mode=DitherMode.NONE,
color_scheme=ColorScheme.BW,
)
DitherMode and ColorScheme are None when epaper-dithering is not installed.
Development
uv sync
uv run pytest
License
MIT
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_oepl-0.1.0.tar.gz.
File metadata
- Download URL: py_oepl-0.1.0.tar.gz
- Upload date:
- Size: 112.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a92c9de741933bed667d415ba1cdf000658dccca392c80ff9e7e2df6db78f7a4
|
|
| MD5 |
c2b681422e37dcc89bf2a45f8c5b16a8
|
|
| BLAKE2b-256 |
c103a255ed081093fb4f319e6e7d2348b06f822e2d04b610859c12697b390891
|
Provenance
The following attestation bundles were made for py_oepl-0.1.0.tar.gz:
Publisher:
release.yml on g4bri3lDev/py-oepl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_oepl-0.1.0.tar.gz -
Subject digest:
a92c9de741933bed667d415ba1cdf000658dccca392c80ff9e7e2df6db78f7a4 - Sigstore transparency entry: 1280685467
- Sigstore integration time:
-
Permalink:
g4bri3lDev/py-oepl@7712444271325399994a30046ba4077133eb1bf2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/g4bri3lDev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7712444271325399994a30046ba4077133eb1bf2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file py_oepl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: py_oepl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.7 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 |
be82ee4b40944460fd64a3bd7e34ffc6de13e8c7e3b4844d04183104e8db124b
|
|
| MD5 |
b0d64871b663df994b51b8219362a3bb
|
|
| BLAKE2b-256 |
0b872ffeec4666a7a812c9fae78cf21dc04abacc67a51bc673b42ae1229a71f3
|
Provenance
The following attestation bundles were made for py_oepl-0.1.0-py3-none-any.whl:
Publisher:
release.yml on g4bri3lDev/py-oepl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_oepl-0.1.0-py3-none-any.whl -
Subject digest:
be82ee4b40944460fd64a3bd7e34ffc6de13e8c7e3b4844d04183104e8db124b - Sigstore transparency entry: 1280685474
- Sigstore integration time:
-
Permalink:
g4bri3lDev/py-oepl@7712444271325399994a30046ba4077133eb1bf2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/g4bri3lDev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7712444271325399994a30046ba4077133eb1bf2 -
Trigger Event:
push
-
Statement type: