Python module for interfacing with Ajazz AKP devices (such as the AKP153E Desk Controller), enabling programmatic control over button images, callbacks, and device states.
pip install pyajazz
# or
git clone https://github.com/superdeee/pyajazz
cd pyajazz
pip install .hidapi. You may need to install OS-specific dependencies before installing cython-hidapi. Refer to the cython-hidapi README for details.
This module provides an easy-to-use Python interface for controlling Ajazz AKP Devices. pyajazz module is primarily designed for the AKP153E Desk Controller. However, other models such as the AKP05, AKP03, and similar rebranded devices may also work (untested). The module handles all necessary requests, allowing full use of the device without relying on the Chinese OEM software.
❗ Read the usage section carefully to avoid bricking your device!
- 🧠 Thread-safe read/write handling
- 🧾 Queue-based events handling
- 🔧 Easy event callbacks configuration
- 🧯 Error handling
- 🧰 Context-managed connection lifecycle
- Device state:
- Initialize / Shut down
- Sleep / Wake up
- Set screen brightness
- Set/Clear individual icon slots
- Clear the whole screen
- Set shutdown logo
- Button press/release callbacks
from pyajazz import Device
jpeg_img_data = ... # load JPEG data from file or create image programmatically
with Device() as device:
device.on_press(position=1, callback=lambda: print("Hello world!"))
device.set_button_image(position=1, data=jpeg_img_data)
while True:
device.handle_pending_inputs()When setting button images, JPEG-encoded data must be provided via the data parameter. Images should have dimensions that are multiples of 16 (e.g., 16×N x 16×M) and contain minimal metadata. Ensure metadata is stripped to prevent issues, including possible device bricking. Attempting to set a logo image with extensive metadata (e.g., EXIF) may cause the device to become unresponsive. Always sanitize image files before sending. Images wider/longer than 96 pixels will overflow to adjacent buttons areas.
Note: The AKP153 display controller expects the screen to be rotated 90° clockwise relative to its physical orientation. Therefore, images must be rotated 90° counter-clockwise before sending to the device.
Each screen/button area is referred to as a position, numbered 1–18. The layout for AKP153 device is as follows:
+------+------+------+------+------+----+
| 13 | 10 | 7 | 4 | 1 | 16 |
+------+------+------+------+------+----+
| 14 | 11 | 8 | 5 | 2 | 17 |
+------+------+------+------+------+----+
| 15 | 12 | 9 | 6 | 3 | 18 |
+------+------+------+------+------+----+
Tip: Reading and encoding the images with opencv is a nice way of removing unnecessary metadata
image_data = cv2.imencode('.jpg', image, [int(cv2.IMWRITE_JPEG_QUALITY), 100])set_button_image(position, data)
position(int): ID of the screen area (1–18)data(bytes | bytearray | list[int]): JPEG-encoded image (max ~10kB, no metadata)
clear_button_image(position)
position(int): ID of the screen area (1-18)
clear_all()
The Ajazz AKP153 supports two types of button events - press and release - which can be configured independently. The device does not support key combinations.
on_press(position, callback)
position(int): ID of the button (1-15)callback(Callable): callable to run on press event
on_release(position, callback)
position(int): ID of the button (1-15)callback(Callable): callable to run on release event
clear_callbacks(position)
position(Optional[int] = None): ID of the button (1-15) or None (clears all button callbacks)
The device supports sleep mode in which it handles any USB input requests (button images can be set while asleep). The first user interaction after entering the sleep mode wakes it up, but is not populated to the controlling software.
sleep()
wakeup()
set_brightness(brightness)
brightness(int): brightness level (0-100)
While not connected to any software Ajazz devices display a logo which can be changed with the command below. Note that the display's physical resolution is 854x480, but due to the device's rotation, logo images must be 480x854 (portrait orientation). Make sure any redundant image metadata, such as EXIF, is removed prior to sending!
set_logo_image(data)
data(bytes | bytearray | list[int]): JPEG encoded image data with no metadata (512kB maximum)
Logo image can be sanitized with OpenCV using a following snippet:
def prep_logo(path):
logo_img = cv2.imread(path)
if logo_img is None:
raise FileNotFoundError(f"Can't open: {path}")
if logo_img.shape[0] < logo_img.shape[1]:
logo_img = cv2.rotate(logo_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
if logo_img.shape != (854, 480, 3):
print("Logo image resolution must be 854x480, resizing...")
logo_img = cv2.resize(logo_img, (480, 854))
success, encoded_image = cv2.imencode('.jpg', logo_img, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
if not success:
raise ValueError("Can't encode as JPEG.")
return encoded_image