Skip to content

Latest commit

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Thermal Printer Scripts

A collection of small Python scripts that render and print 384px-wide receipts on a Phomemo / DP-S01–class BLE thermal printer. Each script fetches or generates something, lays it out as a 1-bit "card," and sends it to the printer over Bluetooth (or USB).

Everything shares one tiny toolkit (print_common.py) and a common set of CLI flags, so they all behave the same way.


How it works

  • print_common.py — the shared toolkit. A Card builder lays out content top-down (title, heading, line, para, kv, divider, image, footer) onto a 384px canvas, then finish() crops, frames, pads (top/bottom feed so the printer doesn't clip edges), and prints it. Also provides get_json / get_bytes (HTTP) and fit_dither (decode + scale + Floyd–Steinberg dither any image to 1-bit).
  • s1_print.py — print dispatcher; picks the transport.
  • direct_s1_ble_print.py / direct_s1_usb_print.py — the BLE / USB transports. print_s1_ble.py, s1_ble_console.py, ble_probe.py are pairing and diagnostic helpers.
  • toy_thermal_printer.py, usb_camera_verify.py, make_gradient_test.py, sitecustomize.py — utilities and test rigs.
  • s01_printer_config.json — printer identity / connection config.

Common flags

Almost every *_print.py accepts:

FlagMeaning
--no-printrender the PNG only; don't send to the printer
--out PATHwhere to write the PNG
--darkness 1-5print darkness (default 3)
--bottom-feed Nblank feed at the bottom (default 24)

Generators take a --seed (printed on the card) so any result can be reproduced. Run any script with -h for its full options.

python pokemon_print.py --gen1 # random Gen-1 Pokémon card
python moon_print.py --no-print # just render moon.png
python forecast_print.py --city Tokyo # 7-day forecast with chart

Scripts by category

Daily & live data

ScriptPrints
calendar_month.pyMonth calendar grid; circles today
daylight_print.pySunrise/sunset/solar-noon with a sun-arc diagram
moon_print.pyTonight's moon phase, drawn with the correct lit fraction
onthisday_print.py"On this day" historical events (Wikipedia) + photo
weather_print.pyLocal current weather
forecast_print.py7-day forecast for any city, with a temp chart
airquality_print.pyAir quality index + pollutants with a gauge
iss_print.pyCurrent position of the ISS
earthquake_print.pyRecent quakes (USGS) on a world dot-map
launches_print.pyUpcoming rocket launches with countdowns
epic_print.pyFull-disc Earth photo from NASA EPIC / DSCOVR
apod_print.pyNASA Astronomy Picture of the Day
world_clock_print.pyAnalog local clock + time in major cities

Finance

ScriptPrints
crypto_print.pyCrypto price + 7-day chart (CoinGecko)
forex_print.pyCurrency exchange rates + 30-day trend (ECB)

Reference & learning

ScriptPrints
word_of_day_print.pyWord of the day with pronunciation & definitions
element_print.pyPeriodic-table element tile + properties
quote_print.pyInspirational quote, typeset
wiki_print.pyRandom Wikipedia article summary
ascii_print.pyASCII table (printable chars, dec/hex)
roman_print.pyRoman numeral converter + reference
multiplication_print.pyTimes-table grid

Fun content (web APIs)

ScriptPrints
meme_print.pyRandom meme (Reddit via meme-api)
dark_joke_print.pyDark-humour joke (JokeAPI)
joke_print.pyRandom two-line joke
insult_print.pyRandom insult (Evil Insult Generator)
facts_print.py"Did you know?" mix of fact/joke APIs
bored_print.pyA random activity to beat boredom
cocktail_print.pyCocktail recipe + photo (TheCocktailDB)
meal_print.pyMeal recipe + photo (TheMealDB)
pokemon_print.pyPokémon trading card (PokeAPI)
pokemon_item_print.pyLive random Pokemon item from PokeAPI
anime_character_print.pyAnime character card (MyAnimeList)
starwars_print.pyStar Wars character dossier (SWAPI)
album_print.pyAlbum card + cover art (iTunes)
met_print.pyMet Museum artwork (open collection)
country_print.pyCountry facts + flag
cat_print.pyRandom cat photo
xkcd_print.pyRandom xkcd comic
fake_human_print.py"Dossier" for a GAN face that doesn't exist
randomuser_print.pyRandom fake person profile
hacker_news_print.pyTop Hacker News stories
color_print.pyColour of the day: name, codes, dithered swatch
internet_story_print.pyInternet story from a live story API or Gutendex
news_feed_print.pyContinuous live RSS headline tape
x_following_feed_print.pyPrint only new tweets from watched X/Twitter RSS feeds

Personalised

ScriptPrints
numerology_print.pyName numerology (Destiny / Soul / Personality)
biorhythm_print.pyPhysical/emotional/intellectual cycle chart

Divination

ScriptPrints
tarot_print.pyTarot card draw
iching_print.pyI Ching hexagram with changing lines
fortune_print.pyA fortune

Puzzles & games

ScriptPrints
maze_print.pyMaze (with optional solution)
sudoku_print.pySudoku (with optional answer key)
wordsearch_print.pyThemed word search (with answer key)
nonogram_print.pyPicross puzzle (logic-verified unique solution)
magicsquare_print.pyMagic square (rows/cols/diagonals sum equal)
bingo_print.py75-ball bingo cards
trivia_print.pyMultiple-choice trivia quiz + answer key
wheel_print.pyDecision wheel — picks one of your options

Tabletop / RPG

ScriptPrints
dungeon_print.pyRoguelike dungeon map
dnd_print.pyRandom D&D character (stats, race, class)
tavern_print.pyFantasy tavern (menu, patrons, rumour)
magicitem_print.pyRandom magic item / loot
superhero_print.pySilly superhero origin card
dispatch_print.pyFictional field dispatch / incident report
artifact_print.pyFictional recovered artifact museum label
book_pick_print.pyReal book recommendation from Open Library
trail_report_print.pyGenerated trail report with elevation profile
seed_packet_print.pyFictional seed packet with growth profile
paper_digest_print.pyCurated digest of real research papers
poem_print.pyShort poem card from PoetryDB or fallback classics
arxiv_brief_print.pyarXiv paper brief with fallback summaries
abstract_snapshot_print.pyPaper abstract with text-derived pulse chart
etymology_snapshot_print.pyDictionary entry with origin and definition-length chart
focus_plan_print.pyGenerated focus plan with schedule timeline
micro_lesson_print.pyShort factual lesson with quiz
decision_memo_print.pyGenerated decision memo with scored options
git_activity_print.pyLive Git worktree and commit activity brief
repo_metrics_print.pyLive repository file metrics and extension chart
process_snapshot_print.pyLive Windows process snapshot with memory chart
python_env_print.pyLive Python environment package inventory
event_log_print.pyLive Windows event log brief with level chart
github_events_print.pyLive GitHub public-events pulse
dog_card_print.pyLive random dog photo card from Dog CEO
duck_card_print.pyLive random duck photo card from Random-d.uk
fox_card_print.pyLive random fox photo card from RandomFox
card_hand_print.pyLive playing-card hand from Deck of Cards API
mtg_card_print.pyLive random Magic card from Scryfall
rickmorty_print.pyLive Rick and Morty character card
dnd_monster_print.pyLive D&D monster card from 5e API
yugioh_card_print.pyLive random Yu-Gi-Oh card from YGOPRODeck
random_anime_print.pyLive random anime card from Jikan
random_manga_print.pyLive random manga card from Jikan
space_crew_print.pyLive people-in-space crew card

Generative art

ScriptPrints
cellular_print.pyWolfram elementary cellular automaton
life_print.pyConway's Game of Life, 2×2 evolution montage
fractal_print.pyMandelbrot / Julia set
attractor_print.pyClifford / de Jong strange attractor
hilbert_print.pyHilbert space-filling curve
dragon_print.pyHeighway dragon curve
koch_print.pyKoch snowflake
spirograph_print.pyHypotrochoid rosettes
truchet_print.pyTruchet arc tiling
phyllotaxis_print.pySunflower seed spiral (golden angle)
voronoi_print.pyVoronoi tessellation
mandala_print.pyN-fold symmetric mandala (colouring-book)
flowfield_print.pyParticle flow field
tree_print.pyRecursive fractal tree
dna_print.pyDNA double helix from a random sequence
constellation_print.pyInvented constellation + mythology
invaders_print.pySwarm of random symmetric pixel sprites
aquarium_print.pyLittle fish-tank scene
pcb_print.pyGenerative circuit-board art
metro_print.pyGenerative metro map of an invented city
sort_print.pySorting-algorithm visualization

Music

ScriptPrints
chord_print.pyGuitar chord diagram
piano_print.pyPiano scale diagram

Novelty & utility

ScriptPrints
barcode_print.pyScannable Code 39 barcode
morse_print.pyMessage in Morse code + key
eyechart_print.pySnellen eye chart
boardingpass_print.pyFake boarding pass
habit_tracker_print.pyMonthly habit tracker grid
binaryclock_print.pyCurrent time as binary-coded decimal
paper_print.pyBlank templates (graph / dots / lined / staff / iso)
photo_print.pyPrint any image file (dithered)
map_print.pyMap of a location
terrain_map_print.pyTerrain / topographic map

Dev dashboards

ScriptPrints
github_print.pyGitHub user stats
repo_print.pyGitHub repository stats
claude_session_print.pyClaude Code session summary
codex_status_print.pyCodex status
neofetch_print.pySystem info, neofetch-style
terminal_print.pyReal command output or simulated terminal transcript
live_stream_print.pyContinuous live Wikimedia edit stream tape

Setup

python -m venv .venv
.venv\Scripts\activate # Windows
pip install pillow bleak # Pillow for rendering, bleak for BLE

Configure your printer target with S1_BLUETOOTH_TARGET or pass --bluetooth; S1_BLUETOOTH_ADDRESS can provide an optional fallback BLE address. The scripts also accept S1_TRANSPORT / PRINT_TRANSPORT to force a transport.

Writing a new script

fromprint_commonimportROOT, Card, fit_dither, get_json, get_bytesdata=get_json("https://api.example.com/thing")
card=Card()
card.title("MY THING")
card.image(fit_dither(get_bytes(data["image"]), card.inner_w, 320))
card.para(data["text"])
card.footer("example.com")
card.finish(ROOT/"thing.png", do_print=True)

Card handles wrapping, dithering, the border, and the top/bottom feed so the printer doesn't clip the edges.


Docker GP-58 USB printer API

This repo also includes a small Linux HTTP sidecar for GP-58 / ESC-POS-style USB thermal printers that appear as /dev/usb/lp0.

The API is intentionally narrow: it owns the raw printer device and exposes only health checks plus text/random/image print endpoints. This is useful when an agent or another container should print without getting direct access to the USB device.

Run with Docker Compose

docker compose -f docker-compose.example.yml up --build

The example maps the host USB printer device class into the container:

volumes:
- /dev/usb:/dev/usbdevice_cgroup_rules:
- "c 180:* rwm"group_add:
- "7"

On many Linux hosts /dev/usb/lp0 is owned by root:lp with group id 7, so group_add: ["7"] lets the non-root container user write to the printer.

For standalone local testing, uncomment the loopback-only port mapping in the compose file and call:

curl http://127.0.0.1:8787/health
curl -X POST http://127.0.0.1:8787/print/text \
-H 'Content-Type: application/json' \
-d '{"title":"Hermes","text":"hello from the tiny printer","dry_run":true}'

Remove "dry_run": true to actually print.

Client helper

thermal_printer_client.py is a small stdlib-only client for the HTTP sidecar. The Docker image installs it as thermal-printer, and Hermes can install or copy the same file as /opt/data/bin/thermal-printer.

thermal-printer health
thermal-printer text --dry-run --title Hermes "hello from Hermes"
thermal-printer random
thermal-printer image card.png --dry-run

By default the client calls http://127.0.0.1:8787. Set THERMAL_PRINTER_API_URL=http://thermal-printer:8787 when calling it from another container on the same Docker network.

Presence guard

The API can refuse real prints unless another machine is reachable by ICMP ping. This is useful for agents that should not print while you are away from your computer. Dry-runs still work.

environment:
THERMAL_PRINTER_REQUIRE_PRESENCE: "1"THERMAL_PRINTER_PRESENCE_HOST: "<TAILSCALE_DESKTOP_IP>"THERMAL_PRINTER_PRESENCE_TIMEOUT: "2"

Check the configured guard with:

thermal-printer presence

If the guard is enabled and the target is not reachable, real print requests return HTTP 409 Conflict and nothing is written to the printer.

API endpoints

  • GET /health returns printer device status and presence guard config without pinging.
  • GET /presence pings the configured presence target and returns guard status.
  • POST /print/text prints text with optional title, footer, and feed_lines.
  • POST /print/random prints a short built-in random message, or a supplied text.
  • POST /print/image prints an image using ESC/POS raster mode from either a container-local path or an image_base64 body.

Example internal call from another container on the same Docker network:

curl -X POST http://thermal-printer:8787/print/random \
-H 'Content-Type: application/json' \
-d '{}'

Direct device CLI

Inside the container or on a Linux host with direct access to /dev/usb/lp0:

python gp58_print.py status
python gp58_print.py text "hello" --title Hermes --dry-run
python gp58_print.py random
python gp58_print.py image card.png

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages