Display devices
SkillCommunicationUse when inspecting, previewing, drawing, messaging, capturing, publishing, or diagnosing the BUSY Bar or TRMNL OG/X displays from the declarative dotfiles configuration.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Display devices skill
What this skill tells your AI
The instructions your AI receives, as published by edmundmiller/dotfiles in skills/catalog/display-devices/SKILL.md and read by ahel’s review.
Use this skill for display work in the dotfiles repository. Keep the device
inventory and behavior in version control, and use displayctl as the single
agent-facing boundary. Do not send ad-hoc HTTP requests when the CLI supports
the operation.
Discover and diagnose
Start with the public inventory and live checks:
displayctl inventory --json
displayctl doctor --json
For BUSY, inspect the user-visible frame with a read-only capture:
displayctl busy-bar capture --target usb --display 0 --output /tmp/busy-front.png
For TRMNL, use displayctl trmnl-og devices, displayctl trmnl-og current,
or the corresponding trmnl-x command. A webhook response only proves that
the service accepted data; TRMNL devices pull rendered images later. Confirm
the result with current-screen data, device telemetry, or an authorized TRMNL
MCP screenshot after allowing for the device poll.
Keep account/plugin/device IDs, webhook URLs, and tokens out of prose, committed files, fixtures, and ordinary logs. Device network addresses belong only in the declarative inventory. Credential values may come only from the runtime environment or a 1Password-backed secret source.
Preview, then apply
BUSY draws, messages, clears, and TRMNL webhook messages are previews by
default. Review the JSON request and use --apply for the actual remote write;
--dry-run is an explicit spelling of the preview mode. Never infer write
authority from a successful validation or a HTTP response alone.
BUSY-specific invariants:
- Put every agent-owned draw in an explicit application namespace such as
agent_*; preserve the existing household namespaces. - Keep agent priority at or below 50. Do not use an agent overlay to outrank the device's built-in or household display sessions.
- Give transient messages a bounded element lifetime/TTL of at most 3,600
seconds. The live BUSY API 25.0.0 OpenAPI schema accepts an integer timeout
with
minimum: 0but publishes no upper bound; 3,600 seconds is this client's one-hour agent policy, not an invented server limit. If a manifest cannot express a bounded lifetime, stop and report that instead of leaving an agent message indefinitely active. - Clear only a named application namespace with
displayctl busy-bar clear --application NAME; never issue an unscoped clear. Capture the frame after an applied draw or clear when visual proof is required.
TRMNL-specific invariants:
- Send messages through the configured private-plugin webhook with
displayctl; honor the documented webhook rate limit (12 requests/hour on the normal plan, 30 requests/hour on TRMNL+). Coalesce or preview updates instead of retrying until the limit is exhausted. - Treat TRMNL as a pull display. Publishing data, receiving HTTP 2xx, or seeing a plugin update is not a user-visible proof; re-read the current display or device telemetry, or use the plugin's authorized MCP screenshot.
- Before a plugin publish, verify the account and intended settings target in the TRMNL UI/API. Do not invent an ID or create a duplicate plugin. Re-read the server-side settings after the write.
Edit and preview TRMNLP
Route source edits to config/trmnl/agent-message. Use the pinned rendering
environment before publishing:
nix develop .#display-devices
cd config/trmnl/agent-message
./test_project.sh
./bin/trmnlp lint
./bin/trmnlp build
Use the checked-in fixture for local preview data. Keep plugin IDs, webhook secrets, and account-specific values external. The project publish guard is intentional; do not bypass it to create an unknown remote target.
Handoff contract
End every display task with a machine-readable envelope (JSON or YAML) that separates preview from applied state:
status: success | preview | failure | blocked
operation: inspect | diagnose | draw | message | capture | publish
target: busy-bar | trmnl-og | trmnl-x
applied: true | false
changed_paths: []
verification: []
landing_state: local | rendered | live-readback | blocked
next_action: "one bounded action, or none"
For applied: true, verification must name the authoritative BUSY capture
or TRMNL current-screen/telemetry/MCP evidence. For a preview, say that no
remote state changed. If credentials, account target, rendering dependencies,
or readback are unavailable, return blocked or failure with the exact safe
recovery action; do not claim completion.
Signals
- GitHub stars
- 80
- Forks
- 6
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
display-devices- Source
- github.com/edmundmiller/dotfiles