Reference
Python SDK reference
Every class, function and method in slowapp_kit, the Python SDK for slowapps, and the slowapp_sdk checking tools.
The Python SDK, slowapp_kit, implements the container contract for Python apps. It reads the run’s environment, checks every card before writing it, and turns exceptions into the right exit codes. It ships in the base images at /opt/slowapp.
import slowapp_kit as kit
from slowapp_kit import Card, BLACK, WHITE, YELLOW, RED
Jobs
| Name | What it does |
|---|---|
@kit.job(name) | Registers a function fn(ctx) as the job name. |
kit.main(argv=None) | Reads the command line and runs it: job <name> runs that job and exits with its code; serve starts the app’s web page. Call it at the bottom of your entry module. |
@kit.route(method, pattern) | Registers a web page route for serve. pattern is a regular expression matched against the whole path; the handler is fn(request, *groups) and returns (status, headers, body). See Settings, schedules and pages. |
kit.log(*args) | Prints to stderr, where the hub and the checking tools collect it. |
Errors and exit codes
Raise these from a job to end it with a specific outcome:
| Exception | Exit code | Meaning for the hub |
|---|---|---|
| (job returns normally) | 0 | Store what the job wrote. |
kit.Unchanged | 3 | Nothing changed: keep the previous cards. |
kit.Transient | 4 | Temporary failure, such as a network error: retry later. |
kit.NeedsConfig | 5 | A setting, secret or sign-in is missing or was refused: wait until it changes. |
kit.ContractError | 64 | The app broke the contract, for example a card in the wrong size. |
| any other exception | 1 | A bug: the traceback goes to stderr, and the hub retries with backoff. |
The constants EXIT_OK, EXIT_UNCHANGED, EXIT_TRANSIENT, EXIT_NEEDS_CONFIG and EXIT_CONTRACT hold the codes.
The run context: ctx
Inputs
| Name | What it is |
|---|---|
ctx.app, ctx.job, ctx.run_id | The app id, job name and run id. |
ctx.reason | Why the job runs: planner, schedule, manual, event or fixture. |
ctx.mode | live or fixture. ctx.fixture is True in a test render. |
ctx.now | The hub’s current time (fixed in a test render). |
ctx.for_ | The time the cards will be shown. Draw for this. |
ctx.deadline | When the job must be finished, or None. ctx.remaining() returns the seconds left, or None. |
ctx.tz | The screen’s time zone, as a ZoneInfo. |
ctx.feeds | The feeds wanted this run, as a list of Surface. |
ctx.feed(name) | The Surface for one feed, or None if this run doesn’t want it. |
ctx.settings | Your app’s settings, with the manifest’s defaults filled in. |
ctx.layout | The screen’s layout, as a dictionary (/in/layout.json). |
ctx.events() | For an event job: the events to apply, as a list of dictionaries. |
ctx.data, ctx.cache, ctx.out | Paths of /data, /cache and /out. |
ctx.manifest | Your slowapp.toml, as a dictionary. |
A Surface has feed, w, h, inks ("k4"), slot and key, and name gives the surface as text, for example "452x444.k4".
Drawing
| Name | What it does |
|---|---|
ctx.canvas(surface, fill=WHITE) | A new RGB Pillow image of the surface’s exact size. |
kit.font(name, size) | Loads a font from the base image, for example kit.font("DejaVuSans-Bold.ttf", 34). |
BLACK, WHITE, YELLOW, RED | The four inks as RGB tuples. Draw only in these, and set fontmode = "1" on every ImageDraw so text has no grey edges. |
Writing output
| Name | What it does |
|---|---|
ctx.write_feed(feed, cards, valid_from=None, valid_until=None) | Writes one feed’s cards. cards is a list of Card, home card first. valid_from defaults to ctx.now, valid_until to an hour later. |
ctx.write_prompts(prompts, surface_feed="prompts") | Writes questions and their answer cards. Needs the prompts grant. See Questions and answers. |
ctx.write_summary(status="ok", lines=(), actions=()) | Sets what the hub’s app card shows. Each action is {"label": …, "job": …} and runs that job. |
ctx.write_timing(next_change_at) | Tells the hub when the content will next change. |
ctx.write_cache_key(key) | Writes a key; if it equals the last run’s, the hub keeps the previous cards. |
kit.atomic_write(path, data) | Writes a file safely (temporary file, sync, rename). Use it for anything you keep in /data. |
Card(id, image, title, peek="", kind="", keep_s=None):
id: lowercase letters, digits,-and_, unique in the feed.image: a Pillow image of the surface’s size, in the four inks.title: required; shown in the strip when this card is next.peek: an optional second line for the strip.kind: leave empty. The first card ishomeand the rest aredetail.keep_s: how long a detail card stays before the slot goes back home.
write_feed raises ContractError if a card has the wrong size, a colour outside the inks, a bad or repeated id, or no title; if there are no cards or more than 32; or if valid_until isn’t after valid_from.
Fetching and secrets
| Name | What it does |
|---|---|
ctx.fetch(name, fn) | Live: calls fn(), saves the result to /data/snapshots/<name> and returns it. Test render: returns the saved copy from the fixture instead. A .json name stores and returns JSON; otherwise str or bytes. |
ctx.exit_if_unchanged() | Raises Unchanged if every input fetched so far, and the settings, are the same as at the last successful run. |
ctx.http_get(url, timeout=60, headers=None) | Returns the body as bytes. The timeout is cut to fit the deadline. Network errors raise Transient; HTTP 401 and 403 raise NeedsConfig. Refuses the network in a test render. |
ctx.http_json(url, timeout=60, headers=None) | As http_get, parsed as JSON. |
ctx.secret(name, optional=False) | Reads /run/secrets/<name>. Missing: raises NeedsConfig, or returns None if optional. In a test render returns fixture-<name>. |
The Weather tutorial uses these together.
Lower-level helpers
kit.check_inks(img), kit.to_indexed(img), kit.encode_png(img), kit.check_png(data, size) and kit.blob_id(img) check and encode images the way write_feed does. kit.blob_id returns the id the hub will give a card’s pixels, which is useful in unit tests.
Running outside a container
Every path the SDK uses can be moved with an environment variable: SLOWAPP_STATE (/data), SLOWAPP_OUT (/out), SLOWAPP_IN (/in), SLOWAPP_CACHE (/cache), SLOWAPP_SECRETS (/run/secrets), SLOWAPP_MANIFEST (/app/slowapp.toml), SLOWAPP_FONTS (/opt/slowapp/fonts) and SLOWAPP_JOB_LOCK. Test without hardware shows a full command.
The checking tools: slowapp_sdk
python3 -m slowapp_sdk validate [DIR ...]
python3 -m slowapp_sdk fixture [DIR] --image IMAGE [--repeat] [--out DIR]
validatechecksslowapp.tomlagainst the schema and the cross-field rules. Unknown keys are errors, so a typo can’t silently drop a grant. It printsokorFAILfor each folder, with any errors and warnings.fixturerenders every example in a container and checks the output; see Test without hardware.--repeatrenders twice and requires identical pictures;--outsets the previews folder (defaultout/previews). It needs Pillow, and Podman or Docker.
Environment: SLOWAPP_ENGINE (podman or docker), SLOWAPP_PLATFORM (for example linux/arm/v7), SLOWAPP_TIMEOUT_SCALE.