API reference#

The phomemo package, as installed from PyPI.

Printer#

High-level async printer client.

The Printer class is the primary developer-facing API. It composes the transport, protocol, event, and imaging modules into a single coherent interface for controlling Phomemo thermal printers.

Typical usage:

from phomemo import Printer, Density

async with Printer("m08f", "AA:BB:CC:DD:EE:FF") as printer:
    await printer.connect()

    # Check device state before printing
    info = await printer.query_device_info()
    print(f"Battery: {info.battery}%, Lid: {info.lid}, Paper: {info.paper}")

    # Wait for a sheet, print, feed it out
    await printer.wait_for_ready()
    await printer.print_image("photo.png", density=Density.MEDIUM)
    await printer.eject_paper()
exception phomemo.printer.PrintAborted(reason)[source]#

Bases: RuntimeError

A raster transfer stopped before the page was complete.

Parameters:

reason (str)

Return type:

None

reason#

Short human-readable cause, e.g. "cancelled".

exception phomemo.printer.SheetRanOut(reason)[source]#

Bases: PrintAborted

The sheet left the paper sensor before the raster was printed.

Parameters:

reason (str)

Return type:

None

class phomemo.printer.DeviceInfo(battery=None, lid=None, paper=None, firmware=None, serial=None, auto_off_minutes=None)[source]#

Bases: object

Snapshot of device state collected from query responses.

Parameters:
  • battery (int | None)

  • lid (LidState | None)

  • paper (PaperState | None)

  • firmware (str | None)

  • serial (str | None)

  • auto_off_minutes (int | None)

battery#

Battery percentage (0-100), or None if not queried.

Type:

int | None

lid#

Lid state, or None if not queried.

Type:

phomemo.events.LidState | None

paper#

Paper state, or None if not queried.

Type:

phomemo.events.PaperState | None

firmware#

Firmware version string, or None if not queried.

Type:

str | None

serial#

Serial number string, or None if not queried.

Type:

str | None

auto_off_minutes#

Auto-off timer in minutes (0 = disabled), or None if not queried.

Type:

int | None

class phomemo.printer.Printer(profile='m08f', address=None)[source]#

Bases: object

Async BLE client for Phomemo thermal printers.

Manages the full lifecycle: BLE connection, device queries, image preparation, raster printing, and paper transport. Designed to be used as an async context manager.

Parameters:
  • profile (str | PrinterProfile) – Either a profile name string (e.g. "m08f") or a PrinterProfile instance.

  • address (str | BLEDevice | None) – BLE MAC address (e.g. "AA:BB:CC:DD:EE:FF"), resolved by a targeted scan at connect() time, or a BLEDevice from discover(), used as is. If omitted, connect() auto-discovers a nearby device matching the profile’s ble_name_pattern.

property profile: PrinterProfile#

The active printer hardware profile.

property is_connected: bool#

Whether the BLE connection is active.

property address: str | None#

The printer’s BLE address, once known.

property chunk_bytes: int#

Bytes per BLE write on the current connection.

property rows_sent: int#

Raster rows handed to the transport in the current transfer, else 0.

on_event(callback)[source]#

Register a listener for parsed device events.

Callbacks receive typed event dataclasses (LidEvent, PaperEvent, BatteryEvent, MotorStopEvent, etc.) as they arrive from the printer.

Parameters:

callback (Callable[[DeviceEvent], None]) – Called with each parsed DeviceEvent.

Return type:

None

async connect(discovery_timeout=5.0)[source]#

Connect to the printer.

The transport always receives a BLEDevice from a live scan: BlueZ can only connect to a device it has seen advertising recently, so a bare MAC address is resolved through a targeted scan first. Without an address, scans for a nearby device whose advertised name matches the profile’s ble_name_pattern, connects to it and keeps its address for later connects; several matches are refused rather than picking one.

Parameters:

discovery_timeout (float) – Scan duration in seconds, for both name-based discovery and MAC resolution. Ignored if a BLEDevice was provided at instantiation.

Raises:
  • ConnectionError – If the connection fails, if a name scan finds no matching device or several, or if the given MAC address is not seen within discovery_timeout.

  • RuntimeError – If already connected.

Return type:

None

async disconnect()[source]#

Disconnect from the printer.

Safe to call even if not connected.

Return type:

None

async initialize()[source]#

Send ESC @ to reset the printer to its default state.

Should precede standalone ESC-prefix commands to prevent the BLE bridge chip from intercepting them.

Return type:

None

async set_density(level)[source]#

Set print concentration via ESC N 04.

Persists until the next ESC @ reset. A model without density control ignores the call.

Parameters:

level (Density) – Desired concentration level.

Return type:

None

async feed(lines=20)[source]#

Feed paper by the given number of lines via ESC d.

Each line is approximately one dot row (0.125mm at 203 DPI).

Parameters:

lines (int) – Number of lines to feed (1-255).

Return type:

None

async eject_paper(max_feeds=64, step_rows=120, ahead=2, feed_seconds=0.7, clear_rows=160)[source]#

Feed the sheet out until the sensor reports it gone, then clear the rollers.

Feeds of step_rows (15mm by default) are kept ahead deep in the printer’s queue so the motor runs without pauses: one more goes out whenever feed_seconds (about one section at the measured feed speed) pass without the paper-absent event. On the event, one feed of clear_rows pushes the sheet out of the rollers. A sheet already past the sensor, or a printer that does not answer the initial query, gets three feeds of clear_rows blind.

Parameters:
  • max_feeds (int) – Maximum number of feeds before giving up (about 1m).

  • step_rows (int) – Rows per feed.

  • ahead (int) – Feeds queued before waiting for the sensor event.

  • feed_seconds (float) – Wait per feed for the sensor event before queuing the next one; longer than the feed itself stalls the motion, shorter queues ahead of the motor.

  • clear_rows (int) – Rows fed once the sensor is clear.

Returns:

True once the sheet has left the sensor, False if it is still present after max_feeds feeds.

Return type:

bool

async set_auto_off_timer(minutes)[source]#

Set the auto-off timer.

Parameters:

minutes (int) – Timeout in minutes, a multiple of 5 up to AUTO_OFF_MAX_MINUTES. 0 disables the timer.

Raises:

ValueError – If minutes is out of range or not a multiple of 5.

Return type:

None

async query_battery(timeout=1.0)[source]#

Query the current battery level.

Parameters:

timeout (float) – Seconds to wait for a response.

Returns:

Battery percentage (0-100), or None if no response.

Return type:

int | None

async query_firmware(timeout=1.0)[source]#

Query the firmware version.

Parameters:

timeout (float) – Seconds to wait for a response.

Returns:

Version string (e.g. "1.1.3"), or None if no response.

Return type:

str | None

async query_serial(timeout=1.0)[source]#

Query the device serial number (ASCII form).

Parameters:

timeout (float) – Seconds to wait for a response.

Returns:

Serial number string, or None if no response.

Return type:

str | None

async query_lid(timeout=1.0)[source]#

Query the lid state.

Parameters:

timeout (float) – Seconds to wait for a response.

Returns:

LidState.CLOSED or LidState.OPEN, or None.

Return type:

LidState | None

async query_paper(timeout=1.0)[source]#

Query the paper sensor state.

Important: Paper state is unreliable when the lid is open. Always query lid state first.

Parameters:

timeout (float) – Seconds to wait for a response.

Returns:

PaperState.PRESENT or PaperState.ABSENT, or None.

Return type:

PaperState | None

async query_auto_off(timeout=1.0)[source]#

Query the auto-off timer.

Parameters:

timeout (float) – Seconds to wait for the response.

Returns:

Timeout in minutes (0 = disabled), or None if the printer did not answer.

Return type:

int | None

async query_device_info(timeout=1.0)[source]#

Query all available device information.

Sends battery, firmware, serial, lid, paper, and timer queries sequentially. Each query returns as soon as the device responds (typically ~70ms), so the full round-trip is well under 1s.

Paper state is discarded if the lid is open (sensor unreliable).

Parameters:

timeout (float) – Per-query timeout in seconds.

Returns:

A DeviceInfo with all available fields populated.

Return type:

DeviceInfo

async check_ready(timeout=1.0)[source]#

Check whether the printer is ready to print.

Queries lid and paper state. Returns a tuple of (ready, reason) where ready is True if the printer can accept a print job.

Parameters:

timeout (float) – Per-query timeout in seconds.

Returns:

(True, "ready") or (False, reason_string).

Return type:

tuple[bool, str]

async reconnect(discovery_timeout=5.0)[source]#

Drop and re-establish the BLE connection to the same device.

A printer that ran a raster past the end of the sheet stops answering queries and discards further print data until the connection is re-established.

Parameters:

discovery_timeout (float) – Scan budget if the device has to be found again.

Return type:

None

async wait_for_ready(timeout=60.0, poll=0.5, settle=3.0, mute_threshold=3)[source]#

Block until a sheet is loaded and seated at the print head.

Polls check_ready so a caller can start a page job and let the user insert the sheet afterwards. The paper sensor reports the sheet as soon as its edge arrives; the feed motor keeps pulling for about two more seconds and then the printer repeats the paper-present event. settle waits for that repeat (capped) so raster data is not sent while the sheet is still moving.

mute_threshold consecutive query timeouts mean the printer is in the error state left by a sheet overrun; the connection is dropped and re-established once, which clears it.

Parameters:
  • timeout (float) – Maximum seconds to wait for a sheet.

  • poll (float) – Seconds between readiness checks.

  • settle (float) – Maximum seconds to wait for the feed to complete after the sheet is detected; 0 sends as soon as it is detected.

  • mute_threshold (int) – Consecutive query timeouts before a reconnect.

Raises:

TimeoutError – With the last readiness reason as message.

Return type:

None

async print_bitmap(bitmap, height, density=Density.MEDIUM, mode=ScaleMode.NORMAL, feed_lines=20, on_progress=None, wait_for_completion=True, completion_timeout=30.0, abort=None, short_sheet_fraction=0.25)[source]#

Send pre-rendered bitmap data to the printer as one continuous stream.

Parameters:
  • bitmap (bytes) – Packed 1-bit bitmap (width_bytes * height bytes).

  • height (int) – Number of rows in the bitmap.

  • density (Density | None) – Print concentration (None to skip).

  • mode (ScaleMode) – GS v 0 scaling mode.

  • feed_lines (int) – Paper feed lines after printing.

  • on_progress (Callable[[int, int], None] | None) – Progress callback (chunks_sent, total_chunks) per connection-sized chunk.

  • wait_for_completion (bool) – If True, block until the motor-stop signal is received and confirm the printer still answers.

  • completion_timeout (float) – Maximum seconds to wait for completion.

  • abort (Event | None) – Set it to stop the transfer; the band in progress is completed with white rows so the printer stays in sync.

  • short_sheet_fraction (float) – A paper-absent event while more than this fraction of rows is still unsent means the sheet is shorter than the page.

Raises:
  • RuntimeError – If not connected.

  • TimeoutError – If completion signal not received within timeout.

  • PrintAborted – If abort was set; reason is "cancelled".

  • SheetRanOut – If the sheet ran out early (paper-absent while most of the page is unsent, or the printer muted after the motor stop). PrintAborted("lid opened") if the lid was open when the motor stopped: the printer discards data in that state.

Return type:

None

async print_image(source, density=Density.MEDIUM, fit=ImageFit.FIT_WIDTH, dither=DitherMode.FLOYD_STEINBERG, feed_lines=20, on_progress=None, wait_for_completion=True, completion_timeout=30.0)[source]#

Full print pipeline: load image, process, print, feed.

Accepts a file path or an already-opened PIL Image. Handles the complete pipeline from image to paper.

Parameters:
  • source (str | Path | Image) – Path to an image file, or a PIL Image instance.

  • density (Density | None) – Print concentration (None to skip).

  • fit (ImageFit) – How to fit the image to the print area. FIT_PAGE bounds the result by the profile’s max_rows as well as its width.

  • dither (DitherMode) – Dithering algorithm for 1-bit conversion.

  • feed_lines (int) – Paper feed lines after printing.

  • on_progress (Callable[[int, int], None] | None) – Progress callback (chunks_sent, total_chunks).

  • wait_for_completion (bool) – If True, block until print-complete signal.

  • completion_timeout (float) – Maximum seconds to wait for completion.

Raises:
  • RuntimeError – If not connected.

  • FileNotFoundError – If the source path doesn’t exist.

  • ValueError – If fit is FIT_PAGE and the profile has no max_rows.

Return type:

None

Discovery#

BLE device discovery for Phomemo printers.

Wraps bleak.BleakScanner to find nearby BLE devices, with optional regex filtering on advertised names.

async phomemo.discovery.discover(name_pattern=None, timeout=5.0)[source]#

Discover nearby BLE devices, optionally filtered by name pattern.

Parameters:
  • name_pattern (str | None) – If provided, only return devices whose advertised name matches this regex pattern (searched, not full-match).

  • timeout (float) – Scan duration in seconds.

Returns:

List of discovered BLEDevice objects, sorted by name.

Raises:

ConnectionError – If the Bluetooth adapter is unavailable or the scan fails at the OS/adapter level.

Return type:

list[BLEDevice]

Profiles#

Printer profile registry.

Each profile captures the physical and protocol characteristics of a specific Phomemo printer model: print width, BLE UUIDs, timing constraints, and supported features. The profile system is extensible — add new models by registering a PrinterProfile instance via register_profile.

class phomemo.registry.PrinterProfile(name, ble_name_pattern, print_width_px, dpi=203, max_chunk_bytes=244, negotiate_mtu=True, write_with_response=True, chunk_delay_s=0.0, band_height=256, sheet_length_mm=None, top_margin_mm=0.0, bottom_margin_mm=0.0, service_uuid='0000ff00-0000-1000-8000-00805f9b34fb', write_uuid='0000ff02-0000-1000-8000-00805f9b34fb', notify_uuid='0000ff01-0000-1000-8000-00805f9b34fb', status_uuid='0000ff03-0000-1000-8000-00805f9b34fb', density_levels=None, supports_escpos_queries=False, make='Phomemo', model_name='', media=(), default_media=None)[source]#

Hardware and protocol parameters for a Phomemo printer model.

Parameters:
  • name (str)

  • ble_name_pattern (str)

  • print_width_px (int)

  • dpi (int)

  • max_chunk_bytes (int)

  • negotiate_mtu (bool)

  • write_with_response (bool)

  • chunk_delay_s (float)

  • band_height (int)

  • sheet_length_mm (float | None)

  • top_margin_mm (float)

  • bottom_margin_mm (float)

  • service_uuid (str)

  • write_uuid (str)

  • notify_uuid (str)

  • status_uuid (str)

  • density_levels (tuple[int, int, int] | None)

  • supports_escpos_queries (bool)

  • make (str)

  • model_name (str)

  • media (tuple[str, ...])

  • default_media (str | None)

name#

Lower-case model identifier (e.g. "m08f"), also the name of the model’s manifest directory.

Type:

str

ble_name_pattern#

Regex pattern matched against BLE advertisement names during device discovery.

Type:

str

print_width_px#

Print head width in dots/pixels.

Type:

int

dpi#

Print resolution in dots per inch.

Type:

int

max_chunk_bytes#

Maximum BLE write payload in bytes. Determined by the negotiated ATT MTU minus 3 bytes of ATT overhead.

Type:

int

negotiate_mtu#

If True (default), the transport will attempt to read the negotiated BLE MTU at connect time and derive the effective chunk size from it (MTU minus 3 bytes ATT overhead). Falls back to max_chunk_bytes if the MTU cannot be determined.

Type:

bool

write_with_response#

Use ATT Write Request (True, default) rather than Write Command for every write. The printer delays its response while its receive buffer is full, so the stream is paced by the print head and never overflows. Unacknowledged writes need chunk_delay_s and still overflow on long jobs.

Type:

bool

chunk_delay_s#

Minimum delay between BLE writes in seconds. 0.0 (default) with write_with_response; only unacknowledged writes need a delay.

Type:

float

band_height#

Maximum rows per GS v 0 raster command.

Type:

int

sheet_length_mm#

Length of one sheet in the feed direction, or None for continuous roll media.

Type:

float | None

top_margin_mm#

Unprintable lead from the paper edge to the first printed row, set by the distance between the paper sensor and the print head.

Type:

float

bottom_margin_mm#

Rows kept free at the sheet end so the raster plus trailing feed never runs the sheet out of the sensor mid-job; an overrun leaves the printer in an error state.

Type:

float

service_uuid#

BLE GATT service UUID for the print data service.

Type:

str

write_uuid#

BLE characteristic UUID for writing commands/data.

Type:

str

notify_uuid#

BLE characteristic UUID for device events (ff01). All meaningful notifications arrive here: sensor state changes, query responses, and print completion signals.

Type:

str

status_uuid#

BLE characteristic UUID for the status echo channel (ff03). Echoes 01 01 for most writes and sends a fixed connect greeting on startup. Does not carry query responses or actionable device state; useful only for low-level debugging.

Type:

str

density_levels#

ESC N 04 concentration values for the low, medium and high Density levels, or None when the model has no density control.

Type:

tuple[int, int, int] | None

supports_escpos_queries#

Whether standard ESC/POS queries return meaningful data (False for M08F — use Phomemo queries instead).

Type:

bool

make#

Manufacturer as shown to users.

Type:

str

model_name#

Model as the vendor writes it ("M08F"); name is the lower-case identifier.

Type:

str

media#

PWG names of the sheet sizes the model takes.

Type:

tuple[str, …]

default_media#

The medium jobs get when they name none.

Type:

str | None

rows_for(length_mm)[source]#

Dot rows that fit between the margins of a sheet of the given length.

Parameters:

length_mm (float | None) – Sheet length in the feed direction, or None for roll media.

Returns:

Printable rows, or None for roll media.

Raises:

ValueError – If the margins leave no printable length.

Return type:

int | None

property max_rows: int | None#

Printable rows of the default sheet, None for roll media.

property lead_rows: int#

the sheet lead the head cannot reach.

Type:

Rows covered by the top margin

property width_bytes: int#

Print width in bytes (8 pixels per byte, MSB-first).

density_value(level)[source]#

The model’s concentration value for a density level.

Parameters:

level (Density) – The requested density.

Returns:

The ESC N 04 value, or None without density control.

Return type:

int | None

phomemo.registry.get_profile(name)[source]#

Look up a registered printer profile by name.

Parameters:

name (str) – Profile key (e.g. "m08f").

Returns:

The matching PrinterProfile.

Raises:

KeyError – If no profile is registered under that name.

Return type:

PrinterProfile

phomemo.registry.list_profiles()[source]#

Return the names of all registered printer profiles.

Returns:

Sorted list of profile name strings.

Return type:

list[str]

phomemo.registry.register_profile(profile)[source]#

Register a custom printer profile.

Allows third-party code to add profiles for printer models not shipped with this library.

Parameters:

profile (PrinterProfile) – The profile to register. Overwrites any existing profile with the same name.

Return type:

None

phomemo.registry.profile_for_name(ble_name)[source]#

The registered profile whose name pattern matches an advertised name.

Parameters:

ble_name (str | None) – Name the printer advertises over Bluetooth.

Returns:

The first matching profile in identifier order, or None.

Return type:

PrinterProfile | None

phomemo.registry.load_manifest(model_dir)[source]#

Build a profile from a model manifest directory.

The directory holds model.json and the model’s icons; its name is the model identifier.

Parameters:

model_dir (Path) – The model’s manifest directory.

Returns:

The profile the manifest defines.

Raises:
  • ValueError – If model.json does not parse, or a field is missing, of the wrong type or inconsistent; the message names file and field.

  • OSError – If model.json cannot be read.

Return type:

PrinterProfile

Images and pages#

Image preparation pipeline for thermal printing.

Converts arbitrary images into 1-bit raster bitmaps suitable for Phomemo printers. The pipeline handles resizing, padding/cropping, grayscale conversion, and dithering.

Bitmap format (from the M08F Protocol Reference): - 1 bit per pixel, packed into bytes. - 1 = black (ink on), 0 = white (no ink). - Most significant bit of each byte is the leftmost pixel. - Rows are sequential top-to-bottom.

class phomemo.imaging.DitherMode(*values)[source]#

Image dithering algorithm for 1-bit conversion.

FLOYD_STEINBERG produces the best visual quality for photographic content. THRESHOLD applies a simple threshold at value 128 with no dithering.

class phomemo.imaging.ToneMode(*values)[source]#

How 8-bit gray becomes the head’s 1-bit output.

THRESHOLD splits at mid gray (true black and white, crisp text). ORDERED uses an 8x8 Bayer pattern (even tone, cheap). DIFFUSION uses Floyd-Steinberg error diffusion (photos).

class phomemo.imaging.ImageFit(*values)[source]#

Strategy for fitting an image to the print area.

phomemo.imaging.prepare_image(img, target_width, fit=ImageFit.FIT_WIDTH, dither=DitherMode.FLOYD_STEINBERG, target_height=None, max_height=None)[source]#

Resize and dither an image for thermal printing.

Processes the image through the full pipeline: resize to target dimensions, pad or crop to exact width (multiple of 8), convert to grayscale, and dither to 1-bit.

Parameters:
  • img (Image) – Source PIL Image (any mode).

  • target_width (int) – Desired width in pixels. Rounded down to the nearest multiple of 8.

  • fit (ImageFit) – How to fit the image to the target dimensions.

  • dither (DitherMode) – Dithering algorithm for 1-bit conversion.

  • target_height (int | None) – Required for FIT_HEIGHT and STRETCH modes.

  • max_height (int | None) – Required for FIT_PAGE: the image is scaled to fit within target_width x max_height preserving aspect ratio.

Returns:

A 1-bit (mode "1") PIL Image ready for bitmap conversion.

Raises:

ValueError – If target_height or max_height is required by the fit mode but not provided.

Return type:

Image

phomemo.imaging.image_to_bitmap(img)[source]#

Convert a 1-bit PIL Image to packed bitmap bytes.

Each row is packed into width / 8 bytes. Black pixels (value 0) map to bit value 1 (ink on), white pixels (value 255) map to bit value 0 (no ink). MSB of each byte is the leftmost pixel.

Parameters:

img (Image) – A mode "1" PIL Image.

Returns:

Packed bitmap bytes, row by row.

Raises:

ValueError – If the image mode is not "1".

Return type:

bytes

phomemo.imaging.prepare_page(img, profile, length_mm, tone, rotate=False)[source]#

Map a client page raster onto the printable area of a sheet.

The raster is optionally rotated a quarter turn counter-clockwise (an A5 portrait page printed on a sheet fed long edge first), centred on the head width when narrower and cropped when wider, stripped of the rows that fall into the unprintable top lead, and clipped to the rows the sheet can hold. Gray input is converted with tone; 1-bit input keeps its pixels.

Parameters:
  • img (Image) – Page raster, mode L (0 = black) or mode 1.

  • profile (PrinterProfile) – Printer geometry: head width, dpi, margins.

  • length_mm (float | None) – Sheet length, or None for roll media (no clipping).

  • tone (ToneMode) – Conversion for gray input; ignored for 1-bit input.

  • rotate (bool) – Rotate the raster 90 degrees counter-clockwise first.

Returns:

A mode 1 image profile.print_width_px wide.

Return type:

Image

Events#

Device event parsing for Phomemo printer notifications.

All events arrive on the ff01 BLE characteristic as 1a <sub-type> <data> messages. Events can be spontaneous (triggered by physical state changes) or solicited (responses to 1f 11 XX queries). Both use the same 1a-prefixed format.

Multiple 3-byte events can arrive concatenated in a single BLE notification. The parser consumes 3 bytes at a time.

class phomemo.events.EventKind(*values)[source]#

Event sub-type byte in 1a <sub-type> <data> messages.

Maps the second byte of each event to its semantic meaning.

class phomemo.events.LidState(*values)[source]#

Physical lid position.

class phomemo.events.PaperState(*values)[source]#

Paper presence as reported by the rear optical sensor.

class phomemo.events.DeviceEvent(kind, raw=b'')[source]#

Base class for all device events.

Used directly as a fallback for unrecognised event sub-types.

Parameters:
  • kind (int)

  • raw (bytes)

kind#

The event sub-type byte.

Type:

int

raw#

The original raw bytes from the notification.

Type:

bytes

class phomemo.events.LidEvent(kind, raw=b'', lid=LidState.CLOSED)[source]#

Lid state change event.

Parameters:
lid#

Current lid state.

Type:

phomemo.events.LidState

class phomemo.events.PaperEvent(kind, raw=b'', paper=PaperState.ABSENT)[source]#

Paper presence change event.

Parameters:
paper#

Current paper state.

Type:

phomemo.events.PaperState

class phomemo.events.BatteryEvent(kind, raw=b'', percent=0)[source]#

Battery level report.

Parameters:
  • kind (int)

  • raw (bytes)

  • percent (int)

percent#

Battery charge percentage (0-100).

Type:

int

class phomemo.events.FirmwareEvent(kind, raw=b'', major=0, minor=0, patch=0)[source]#

Firmware version report.

The version bytes map to major.minor.patch. Example: 1a 07 01 01 03 → v1.1.3.

Parameters:
  • kind (int)

  • raw (bytes)

  • major (int)

  • minor (int)

  • patch (int)

major#

Major version number.

Type:

int

minor#

Minor version number.

Type:

int

patch#

Patch version number.

Type:

int

class phomemo.events.SerialEvent(kind, raw=b'', value='')[source]#

Serial number report (ASCII or short form).

Parameters:
  • kind (int)

  • raw (bytes)

  • value (str)

value#

The serial string or hex representation.

Type:

str

class phomemo.events.TimerEvent(kind, raw=b'', value=0, minutes=0)[source]#

Auto-off timer setting report.

Parameters:
  • kind (int)

  • raw (bytes)

  • value (int)

  • minutes (int)

value#

Raw timer byte. 0 = disabled, non-zero = timeout in 5-minute increments.

Type:

int

minutes#

Computed timeout in minutes (0 = disabled). Derived from value * 5; cannot be set independently.

Type:

int

class phomemo.events.MotorStopEvent(kind, raw=b'')[source]#

Motor stop / print complete signal.

Fires when the motor stops after a print job. The third byte (0x0c) is invariant across all tested conditions.

Parameters:
  • kind (int)

  • raw (bytes)

phomemo.events.parse_notification(data)[source]#

Parse a BLE notification payload into typed events.

Notifications from ff01 can contain one or more concatenated 3-byte events. This function splits the payload and parses each event individually.

Parameters:

data (bytes) – Raw bytes from a BLE notification on ff01.

Returns:

List of parsed DeviceEvent instances. Empty list if the data doesn’t start with 0x1a or has invalid length.

Return type:

list[DeviceEvent]

Bridge client#

Client for the bridge socket, used by the CLI and by printer-application tests.

exception phomemo.bridge.client.BridgeError[source]#

The bridge refused or failed a command; the message is its error.

class phomemo.bridge.client.BridgeClient(socket_path, on_event=None)[source]#

One connection to the bridge.

Parameters:
  • socket_path (Path) – The bridge’s unix socket.

  • on_event (Callable[[dict[str, Any]], None] | None) – Called with each pushed event while a reply is pending.

async connect()[source]#

Open the socket connection.

Return type:

None

async disconnect()[source]#

Close the socket connection.

Return type:

None

async send(obj, payload=b'')[source]#

Send a command without waiting for its reply.

Parameters:
  • obj (dict[str, Any])

  • payload (bytes)

Return type:

None

async reply()[source]#

Read the reply to the pending command, dispatching events on the way.

The bridge acknowledges an abort sent while another command is pending before it answers that command; the acknowledgement is skipped here.

Raises:

BridgeError – If the reply reports ok: false.

Return type:

dict[str, Any]

async call(obj, payload=b'')[source]#

Send a command and return its reply.

Parameters:
  • obj (dict[str, Any])

  • payload (bytes)

Return type:

dict[str, Any]

async open(address=None)[source]#

Bring the printer connection up.

Parameters:

address (str | None) – Printer to connect to; None keeps the bridge’s choice (configured address, the last printer, or a name scan).

Returns:

connected and, when that is false, the reason.

Return type:

dict[str, Any]

Drop the printer connection now instead of after the idle timeout.

Return type:

None

async info()[source]#

Device details and sensors of the linked printer.

Return type:

dict[str, Any]

async auto_off(minutes=None)[source]#

Read the linked printer’s auto-off timer, setting it first if given.

Parameters:

minutes (int | None) – New timeout, a multiple of 5; 0 = never switch off.

Returns:

The timer in minutes as the printer reports it.

Return type:

int | None

async status(address=None)[source]#

Connection state and sensors; last_seen values without a connection.

Parameters:

address (str | None) – Printer to report on; None means the linked or last linked one.

Return type:

dict[str, Any]

async identify()[source]#

Eject a loaded sheet, then pulse the feed three times.

Return type:

None

async test_page(versions=None)[source]#

Render the test page and return the PNG’s path.

Parameters:

versions (dict[str, str] | None) – Component versions to print, by name.

Return type:

Path

async print_test_page(quality='normal')[source]#

Render the test page and print it through the bridge.

The page covers the area below the sheet’s unprintable lead, so it gets that lead as white rows on top before the normal page preparation drops them again.

Parameters:

quality (str) – draft, normal or high.

Returns:

Impressions printed (1).

Return type:

int

async print_image(path, media='a4', tone='auto', quality='normal', content='auto', abort_after=None)[source]#

Print an image file as one page: open, wait for a sheet, stream, end.

Parameters:
  • path (Path) – Image file readable by Pillow.

  • media (str) – a4, a5 (fed long edge first, page rotated) or roll.

  • tone (str) – auto or a ToneMode value.

  • quality (str) – draft, normal or high (maps to density).

  • content (str) – auto, text, graphic or photo.

  • abort_after (float | None) – Seconds after page_end to send abort; for testing.

Returns:

Impressions printed (1).

Raises:
  • BridgeError – If any step fails.

  • KeyError – If media or quality is not known.

Return type:

int

Bridge configuration#

Bridge configuration: TOML files layered under environment overrides.

exception phomemo.bridge.config.ConfigError[source]#

A configuration file named explicitly does not exist.

phomemo.bridge.config.default_paths(env)[source]#

System file first, then the user’s XDG config file.

Parameters:

env (Mapping[str, str]) – Environment to read XDG_CONFIG_HOME and HOME from.

Returns:

Candidate paths in override order; missing files are skipped later.

Return type:

list[Path]

phomemo.bridge.config.default_socket(env, system_socket=None)[source]#

The system bridge’s socket when it exists, else one under XDG_RUNTIME_DIR.

The system service listens at /run/phomemo/bridge.sock; a user’s command line talks to it when it is there. Without it (development, a user service) the socket lives in the user’s runtime directory.

Parameters:
  • env (Mapping[str, str]) – Environment providing XDG_RUNTIME_DIR.

  • system_socket (Path | None) – The system bridge’s socket; SYSTEM_SOCKET by default.

Returns:

The socket path to listen on or connect to.

Return type:

Path

phomemo.bridge.config.load_config(paths=None, env=environ({'ACCEPT_EULA': 'Y', 'ACTIONS_ORCHESTRATION_ID': 'f41a3352-3a67-8e14-ab2d-1ce5b22a710f.build.__default', 'ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE': '/opt/actionarchivecache', 'ACTIONS_RUNNER_RETURN_JOB_RESULT_FOR_HOSTED': '1', 'AGENT_TOOLSDIRECTORY': '/opt/hostedtoolcache', 'ANDROID_HOME': '/usr/local/lib/android/sdk', 'ANDROID_NDK': '/usr/local/lib/android/sdk/ndk/27.3.13750724', 'ANDROID_NDK_HOME': '/usr/local/lib/android/sdk/ndk/27.3.13750724', 'ANDROID_NDK_LATEST_HOME': '/usr/local/lib/android/sdk/ndk/30.0.16248370', 'ANDROID_NDK_ROOT': '/usr/local/lib/android/sdk/ndk/27.3.13750724', 'ANDROID_SDK_ROOT': '/usr/local/lib/android/sdk', 'ANT_HOME': '/usr/share/ant', 'AZURE_EXTENSION_DIR': '/opt/az/azcliextensions', 'BOOTSTRAP_HASKELL_NONINTERACTIVE': '1', 'CHROMEWEBDRIVER': '/usr/local/share/chromedriver-linux64', 'CHROME_BIN': '/usr/bin/google-chrome', 'CI': 'true', 'DEBIAN_FRONTEND': 'noninteractive', 'DOCS_VERSION': '0.4.0', 'DOTNET_MULTILEVEL_LOOKUP': '0', 'DOTNET_NOLOGO': '1', 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE': '1', 'EDGEWEBDRIVER': '/usr/local/share/edge_driver', 'ENABLE_RUNNER_TRACING': 'true', 'GECKOWEBDRIVER': '/usr/local/share/gecko_driver', 'GHCUP_INSTALL_BASE_PREFIX': '/usr/local', 'GITHUB_ACTION': '__run', 'GITHUB_ACTIONS': 'true', 'GITHUB_ACTION_REF': '', 'GITHUB_ACTION_REPOSITORY': '', 'GITHUB_ACTOR': 'PassionateBytes', 'GITHUB_ACTOR_ID': '184142773', 'GITHUB_API_URL': 'https://api.github.com', 'GITHUB_ARTIFACTS': '/home/runner/work/_temp/_runner_file_commands/artifacts_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_ARTIFACTS_LIST': '/home/runner/work/_temp/_runner_file_commands/artifacts_list_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_BASE_REF': '', 'GITHUB_ENV': '/home/runner/work/_temp/_runner_file_commands/set_env_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_EVENT_NAME': 'push', 'GITHUB_EVENT_PATH': '/home/runner/work/_temp/_github_workflow/event.json', 'GITHUB_GRAPHQL_URL': 'https://api.github.com/graphql', 'GITHUB_HEAD_REF': '', 'GITHUB_JOB': 'build', 'GITHUB_OUTPUT': '/home/runner/work/_temp/_runner_file_commands/set_output_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_PATH': '/home/runner/work/_temp/_runner_file_commands/add_path_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_REF': 'refs/tags/v0.4.0', 'GITHUB_REF_NAME': 'v0.4.0', 'GITHUB_REF_PROTECTED': 'false', 'GITHUB_REF_TYPE': 'tag', 'GITHUB_REPOSITORY': 'PassionateBytes/phomemo-driver', 'GITHUB_REPOSITORY_ID': '1196991004', 'GITHUB_REPOSITORY_OWNER': 'PassionateBytes', 'GITHUB_REPOSITORY_OWNER_ID': '184142773', 'GITHUB_RETENTION_DAYS': '90', 'GITHUB_RUN_ATTEMPT': '1', 'GITHUB_RUN_ID': '42132868498', 'GITHUB_RUN_NUMBER': '35', 'GITHUB_SERVER_URL': 'https://github.com', 'GITHUB_SHA': 'a8f85160b85cc7aeaa626a8abf98083ff492fe94', 'GITHUB_STATE': '/home/runner/work/_temp/_runner_file_commands/save_state_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_STEP_SUMMARY': '/home/runner/work/_temp/_runner_file_commands/step_summary_0f3662a6-8b35-4708-b69e-b0ce50f38737', 'GITHUB_TRIGGERING_ACTOR': 'PassionateBytes', 'GITHUB_WORKFLOW': 'Docs', 'GITHUB_WORKFLOW_REF': 'PassionateBytes/phomemo-driver/.github/workflows/docs.yml@refs/tags/v0.4.0', 'GITHUB_WORKFLOW_SHA': 'a8f85160b85cc7aeaa626a8abf98083ff492fe94', 'GITHUB_WORKSPACE': '/home/runner/work/phomemo-driver/phomemo-driver', 'GOROOT_1_24_X64': '/opt/hostedtoolcache/go/1.24.13/x64', 'GOROOT_1_25_X64': '/opt/hostedtoolcache/go/1.25.14/x64', 'GOROOT_1_26_X64': '/opt/hostedtoolcache/go/1.26.10/x64', 'GRADLE_HOME': '/usr/share/gradle-9.8.2', 'HCA_CLOUD_PROVIDER': 'azure', 'HOME': '/home/runner', 'HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS': '3650', 'HOMEBREW_NO_AUTO_UPDATE': '1', 'INVOCATION_ID': '7ce7ac92393044e1b07e1bd0c2dc3b37', 'ImageOS': 'ubuntu26', 'ImageVersion': '20261101.186.1', 'JAVA_HOME': '/usr/lib/jvm/temurin-25-jdk-amd64', 'JAVA_HOME_11_X64': '/usr/lib/jvm/temurin-11-jdk-amd64', 'JAVA_HOME_17_X64': '/usr/lib/jvm/temurin-17-jdk-amd64', 'JAVA_HOME_21_X64': '/usr/lib/jvm/temurin-21-jdk-amd64', 'JAVA_HOME_25_X64': '/usr/lib/jvm/temurin-25-jdk-amd64', 'JOURNAL_STREAM': '10:15765', 'LANG': 'C.UTF-8', 'LOGNAME': 'runner', 'MEMORY_PRESSURE_WATCH': '/sys/fs/cgroup/system.slice/hosted-compute-agent.service/memory.pressure', 'MEMORY_PRESSURE_WRITE': 'c29tZSAyMDAwMDAgMjAwMDAwMAA=', 'NVM_DIR': '/home/runner/.nvm', 'PATH': '/home/runner/work/phomemo-driver/phomemo-driver/.venv/bin:/opt/hostedtoolcache/uv/0.13.7/x86_64:/home/runner/.local/bin:/snap/bin:/home/runner/.local/bin:/opt/pipx_bin:/home/runner/.cargo/bin:/home/runner/.config/composer/vendor/bin:/usr/local/.ghcup/bin:/home/runner/.dotnet/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin', 'PIPX_BIN_DIR': '/opt/pipx_bin', 'PIPX_HOME': '/opt/pipx', 'POWERSHELL_DISTRIBUTION_CHANNEL': 'GitHub-Actions-Linux', 'PSModulePath': '/root/.local/share/powershell/Modules:/usr/local/share/powershell/Modules:/opt/microsoft/powershell/7/Modules:/usr/share/az_15.6.1', 'PWD': '/home/runner/work/phomemo-driver/phomemo-driver', 'RUNNER_ARCH': 'X64', 'RUNNER_ENVIRONMENT': 'github-hosted', 'RUNNER_NAME': 'GitHub Actions 1000001586', 'RUNNER_OS': 'Linux', 'RUNNER_TEMP': '/home/runner/work/_temp', 'RUNNER_TOOL_CACHE': '/opt/hostedtoolcache', 'RUNNER_TRACKING_ID': 'github_b95437ad-bf92-4fd1-8140-9b564a84b017', 'RUNNER_WORKSPACE': '/home/runner/work/phomemo-driver', 'SELENIUM_JAR_PATH': '/usr/share/java/selenium-server.jar', 'SGX_AESM_ADDR': '1', 'SHELL': '/bin/bash', 'SHLVL': '1', 'SWIFT_PATH': '/usr/share/swift/usr/bin', 'SYSTEMD_EXEC_PID': '2154', 'USER': 'runner', 'USE_BAZEL_FALLBACK_VERSION': 'silent:', 'UV': '/opt/hostedtoolcache/uv/0.13.7/x86_64/uv', 'UV_CACHE_DIR': '/home/runner/work/_temp/setup-uv-cache', 'UV_RUN_RECURSION_DEPTH': '1', 'VCPKG_INSTALLATION_ROOT': '/usr/local/share/vcpkg', 'VIRTUAL_ENV': '/home/runner/work/phomemo-driver/phomemo-driver/.venv', 'XDG_CONFIG_HOME': '/home/runner/.config', 'XDG_RUNTIME_DIR': '/run/user/1001', '_': '/opt/hostedtoolcache/uv/0.13.7/x86_64/uv', 'DOCUTILSCONFIG': '/home/runner/work/phomemo-driver/phomemo-driver/docs/docutils.conf'}), require_files=False)[source]#

Build the bridge configuration.

Later files override earlier ones; PHOMEMO_BRIDGE_SOCKET overrides the socket path from any file.

Parameters:
  • paths (list[Path] | None) – Files to read in override order; None means the system file then the user’s XDG file.

  • env (Mapping[str, str]) – Environment for the socket override and default locations.

  • require_files (bool) – Treat a missing file in paths as an error (for a path the user named explicitly).

Returns:

The merged configuration.

Return type:

BridgeConfig

Problems inside a file (unknown keys, wrong types, out-of-range values, a file that does not parse) are logged as warnings and the affected setting keeps its default.

Raises:

ConfigError – If a file in paths is missing and require_files is set.

Parameters:
  • paths (list[Path] | None)

  • env (Mapping[str, str])

  • require_files (bool)

Return type:

BridgeConfig