---
name: wignet
description: Drive a Wignet, a remote screen, for its owner — publish messages and pictures (one, a slideshow, or for a while), keep its content fresh yourself (the server never refreshes anything), manage prepared display events and supported alarms, inspect delivery, and answer live shop questions. Use when the owner mentions their Wignet or gives a app.wignet.app/w/… link.
---

# Wignet

Wignets are remote screens: you publish to one, and it shows the content
when it next syncs.

The platform accepts explicit agent requests. You, the agent, own recurring
retrieval, composition and publication of weather, transit or any other data:
the server never fetches or recomposes anything on its own.
There is no service catalogue and no periodic server renderer. MessageBoard,
Custom and PushImages are direct publication actions. Welcome Page and
`event_message` are internal local renders, retained for onboarding and events.

## 1. One Wignet, one link

The owner link is `https://app.wignet.app/w/<id>`. Its unique identifier grants access
to that Wignet. Ask for the link if missing; never guess it or write it into
public documents, images or commits. A link-enabled screen needs no additional
account or token; respect its access restrictions if it requires a signed-in owner.

## 2. Connect

Prefer MCP `https://mcp.wignet.app/<id>`, or the generic connector `https://app.wignet.app/mcp`
with a `device` argument on each device action. See `references/connecting.md`.
For HTTP clients, read `https://app.wignet.app/api/owner/<id>/openapi.json` and
`https://app.wignet.app/api/owner/<id>/guide`. Without either capability, explain the
steps on the owner's page. The agent must arrange its own recurring execution;
do not promise an automation unless the client actually supports and enables it.

## 3. The loop

1. Read `get_state` and `get_capabilities`: selected `content`, the standing
   `slides`, a `pushed` content and its end, `active_event`, power, last physical contact and hardware profile.
2. Before composing images read `get_orientation` for logical width and height.
   Follow the design guides in `get_capabilities` → `skills`, through `read_skill`
   or the installed skills. The panel is monochrome unless explicitly Spectra 6.
3. Publish. A successful response says `published` and gives a publication
   identifier and preview. On an error the previous content stays available.
4. Verify the returned preview and state. This confirms server availability,
   not physical display. `last_seen_at` is updated only by the screen's contact.
5. Explain delivery at the next wake. Powered screens poll every
   `power.powered_sync_interval_seconds` (10 s by default, the minimum; the owner can ask
   for up to 3600 s with `set_powered_sync_interval`, shorter meaning faster
   delivery for more requests); battery screens follow their sync interval and event/alarm boundaries. No
   server command can interrupt a sleep already in progress. Offline delivery
   has no guaranteed deadline.

## 4. Actions

Before publishing, decide how it sits with what the screen shows:

1. **Replace the slides: the default.** "Show this", "put this on the
   screen": it becomes what the screen shows until the next publication.
2. **Ask the owner** when they could mean replace or add: typically
   `get_state` shows slides of their own (a slideshow, another agent's
   content) and the request says neither "instead" nor "also". One short
   question: replace the slides, or add a slide?
3. **Add a slide** (`add_slide`) only when the owner says so ("add it",
   "also show", "to the slideshow").
4. **For a while** only when the request is clearly time-bound: shown now
   for a given period, or at a given time, after which the screen returns to
   what it showed (a recipe while cooking, a message until tonight, an
   event, an alarm). Now: `push_event` with `duration_seconds`. At a set
   time: a planned event.

| Intent | Action |
|---|---|
| A text, a list, a schedule, a scoreboard (the default) | `push_event` with `text`, in Markdown (below): it replaces the slides; the server lays it out |
| Photo, generated picture or composed chart | `push_image`, one source only; with a shell, `curl -F image=@file.png <REST base>/push_image`; it replaces the slides |
| Photo the owner has at hand (attached to the chat, on their phone), no shell | `request_photo`: a one-use link (15 min) where they pick it, straight to the screen, never through the chat; choose `as` first (slides, slide, push). On ChatGPT, `image_file` instead |
| Fetch a public image URL once | `set_custom_image` with `image_url`; it replaces the slides |
| Several pictures or texts in turn | `set_slides` (the whole list, in order; `{"publication_id"}` keeps one already sent); `set_slide_interval` (`get_capabilities` → `slides` gives the bounds, 600 s by default) |
| Add to the slides, when the owner asks to add | `add_slide` (at the end, or at `position`); `remove_slide` drops one |
| A text or image for a while, a time-bound request only | `push_event` with `duration_seconds` (60–86400) from when the screen downloads it; the slides pause, then resume |
| Stop a text/image pushed for a while early | `end_push`: the slides return at the next screen contact; this does not cancel a sound |
| Check before publishing | add `dry_run: true` to `push_event` (text or image, no sound) or `push_image`: you get `preview_url`, the screen is untouched |
| Repeat data updates | You retrieve, compose and republish, on your own schedule |
| A text or image at a set time, for its window (an agenda for the morning, a birthday) | `add_event` with a `display`; the slides come back when the window closes |
| Add one planned display window or supported sound | Read `get_events`, then `add_event` with one `event`; the others are kept |
| Replace the whole planned schedule intentionally | Read `get_events`, then `set_events` with the entire intended list; omitted events are deleted |
| Remove one planned event, or only its sound | `delete_event` with its `id`; `update_event` with `{"changes": {"sound": null}}` |
| Chime now, optionally with a text/image | `push_event`, see `wignet-notify` |
| Alarm | `wignet-alarm`; melodies: `wignet-tunes` |
| Something the screen plays by itself: buttons, motion, a sprite, a question with answers | Only if `get_capabilities` has `scene`: `push_scene`, then `get_inputs` for the answers and `send_scene_event` to poke it; read `references/scenes.md` first |
| Battery cadence | `set_battery_sync_interval` (`seconds: 43200` for 12 hours), or `reset_battery_sync_interval` for 3600 s |
| Mains cadence | `set_powered_sync_interval` (10–3600 s, default 10) |
| The owner gives the Wignet a name ("call it Kooleur") | `set_name`: their page, `describe_device` and other agents use it from then on; `null` clears it; the link stays the same |
| Orientation | `set_orientation`: local sources are rerendered, URLs are not fetched |
| Background of text messages (colour screens) | `set_message_background`: `yellow` (Spectra 6 default), `white` (full-colour default), `red` or `black`; text is redrawn |
| State power when hardware does not report it | `set_power_source` |
| Something did not turn out as expected | `get_device_log` (`errors_only: true` for refusals): the calls made to this Wignet over 30 days, by you, another agent or the owner's page, without their content; with `get_state` (last contact, delivery). Still unexplained: offer the owner to write to contact@wignet.app with the Wignet's name, link, times and errors |

The standing content is a list of slides; one slide simply shows alone.
`push_image`, `set_custom_image`, `push_event` without `duration_seconds` and
`set_slides` replace the whole list (the answer says how many slides it
replaced). They never interrupt: an event window open or a push showing
keeps the screen until it ends, and the new content shows after it (the
answer's `shows_after` says what and until when; `end_push` ends a push now). Which slide shows is decided by
the clock, and a new list starts at slide 1: a run on a timer must not resend
the list, or the screen never gets past slide 1. Refresh one slide with
`add_slide` at its position, then `remove_slide` on the old one. On battery, a short interval wakes
the screen at every change: the answer says what it costs. A `push_event` text or
image with `duration_seconds` shows above everything for that long, then the slides
come back on their own, on the slide they had. The duration starts when the screen
downloads the push, so it shows that long even after a long sleep; one not
downloaded within 24 hours is dropped. Planned event windows pause the slides
the same way. On a screen
with nothing of its own yet (no slides, or only the Welcome page) even that
stays, as standing content: there is nothing to return to (`standing: true` in
the answer). Use planned
events when a display belongs at a set time.

The server stores what you send and never fetches or recomposes it again.
A weekly event with an image shows that same image every week: fresh data
(weather, agenda, scores) means you compose a new image and call `update_event` with its `id` and
`changes.display` before the window opens (planned time minus `lead_seconds`).
The other events are preserved. If you cannot run at a set
time, say so to the owner rather than promising a daily refresh. Per Wignet:
32 planned events, 16 of them with an image, 500 characters of event text.

A display window opens `lead_seconds` before the planned time (at most 1 h,
default 5 min) and stays `hold_seconds` after it (at most 24 h; default the
whole ring, or 15 min without a sound). Set `hold_seconds` whenever the owner
means more than a glimpse: a message for the day is not a 15-minute one. An
event can be planned for any future date and keeps its prepared image until
it is removed. Never send service assignments, service rules or admin-only
width, height or hardware profile in owner actions.

A `push_event` text is Markdown, and plain text works as typed: each line
stays a line. The server sets it flush left, as large as it fits: on a
colour e-paper screen black on yellow by default, on a full-colour screen
(`rgb`) black on white (`set_message_background` changes either), on a
black-and-white screen black on white. The corner shows the day and
time it was sent ("Friday, 18:00", the Wignet's timezone).

| Write | Shows |
|---|---|
| `# Title`, `## Subtitle` | a large bold title, a bold line |
| `**bold**` | bold (italic, `code` and links keep only their text) |
| `- item`, `1. item` | a list with a hanging indent |
| `\| Gym \| 18:00 \|` lines | a table, last column right-aligned: schedules, scores |
| `> words` | a quote with a bar |
| `---` | a rule |
| a blank line | half a line of space |

On a colour screen, `background` and `text_color` override the inks for one
text in `push_event`, `add_slide` or `set_slides` (different inks from black,
white, yellow, red, green, blue; on a full-colour screen white text is drawn
in the Wignet's cream, #F7F2E8). Without overrides the screen's defaults
apply; monochrome stays black on white.

At most 1000 characters and 12 list items and table rows. A text that does
not fit the screen at 16 px is refused, saying how many lines fit: shorten it.
Emoji are left out (the font has none; the reply's `rendering.warnings` says
so). `rendering.body_px` tells a poster from a dense page. `"format":
"plain"` shows `#` and `*` as typed. Prefer this to drawing an image for
anything that is words; keep it readable from 2–3 m.

## 5. Images

Choose one source for `push_image`:

- `image_url`: public HTTP(S), downloaded once; a temporary signed URL is fine.
- `image_file`: conversation file reference containing `download_url`.
- `image_base64`: exact image bytes, with `image_sha256` for integrity.

A photo the owner has at hand rather than you (attached to the chat, in their
camera roll) is not yours to send: call `request_photo` with what it becomes
(`as`: slides, slide, or push with `duration_seconds`) and give them the
one-use link. They pick the photo there; it goes straight to the screen,
never through the conversation. The upload page scales large photos to 1600 px
on the long side before sending them. Hosts that show MCP Apps display
a drop zone in the chat. On ChatGPT an attached photo goes as `image_file`.

With HTTP, `scripts/compose.py image <file> --upload <Wignet link>` uploads a
file directly. Without sandbox network, run `scripts/compose.py image <file>
--base64 --print-base64`, then pass the printed base64 and sha256 exactly.
Give the script `--width` / `--height` from `get_orientation` logical dimensions,
`--profile spectra6` or `--profile rgb` only for that hardware. Leave orientation out: the screen's applies.
The server defaults to `mode: auto`: flat graphics (64 colours or fewer) use
the nearest inks; photos are dithered. RGB preserves full colour. Force `threshold` or `dither` when
needed, and choose `fit: contain` or `cover`. Size limit: 8 MiB, decoded limit: 16 million pixels.

Published sources persist while referenced by slides, a push or events; only
unreferenced artifacts older than 3 days are cleaned up, weekly. That is not a
publication expiry.
For safe publication retries use a stable `request_id` (deduplication 24 h).
The same key with different arguments gives a conflict. On a concurrency
conflict read the state and decide whether to retry; never overwrite blindly.

## 6. Prices and stock

Live products: MCP `https://wignet.app/api/shop/mcp` (`list_products`, `get_product`)
or JSON `https://wignet.app/api/shop/products`. Read before quoting price or stock.
Prices include VAT and shipping. The showroom no longer offers checkout or
product pages. For compatible boards and setup, link https://wignet.app/build.html.
For purchase enquiries or other questions, contact contact@wignet.app; offer an email
draft. You cannot order for the owner.

## 7. Patterns, and recipes

See `references/publishing.md` for the usual publications. When the owner
wants a use case rather than a single action (a photo slideshow, a smart alarm
clock…), `read_recipe` lists recipes: its outcome, what it requires, and which
of these tools make it. Read one once and build with what your host has. Verify publication results, current event selection
and last physical contact when an owner reports an unchanged screen. Keep the
previous successful content when data retrieval fails; never invent measurements.
