Skip to content

Resources · field notes · member preview

Scripting the Stream Deck: what the docs don’t tell you

The Elgato Stream Deck stores every profile as plain JSON on disk. That means you can generate buttons, duplicate pages, and rebuild whole layouts with a script — no clicking through the app 32 times. It also means there are two traps that will quietly eat an afternoon, because the app never shows an error for either of them. This is the short version of a field-notes document built from roughly fourteen script iterations against a Stream Deck XL and a Stream Deck +.

The five-step safe workflow

  1. 1Quit AND kill the app first. Quit it properly, then force-kill, then wait three seconds. If the app is still running when you edit files, it rewrites the profile from memory on exit and your changes vanish.
  2. 2Back up ProfilesV3 in full. It lives at ~/Library/Application Support/com.elgato.StreamDeck/ProfilesV3. Timestamped copy, hashed, every time. Rollback is just copying it back.
  3. 3Confirm the deck by Model ID. Read Device.Model from the backup’s manifest — 20GBX9901 is the XL, 20GBD9901 is the +. Never trust the device name string.
  4. 4Write under a fresh UUID. A brand-new profile, or a new page appended to Pages[]. Never edit an existing page in the active profile.
  5. 5Verify before relaunching. Parse the JSON back, check button counts, confirm untouched buttons are byte-identical. Then relaunch and select the new profile.

The two bugs

Bug one: the wrong deck. With two decks connected, device-name strings are ambiguous enough that a generator can deliver buttons to hardware that doesn’t have those key positions. The profile registers, renders empty, and nothing complains. Targeting by Model ID from a live manifest ends this class of bug permanently.

Bug two: the invisible edit. The app caches the active profile in memory and ignores manifest edits made to it on disk — perfectly valid JSON that never renders. Editing the active profile directly is a dead end; content written under a fresh profile or page UUID gets picked up cleanly on relaunch, because there is no cache entry to fight.

Bonus gotcha: a button pointing at a .sh script opens it in TextEdit rather than running it. Wrap it in a .command file and macOS runs it in Terminal.

Grab the kit

The full field notes — filesystem layout, action-JSON idioms, icon naming rules, a debug ladder for buttons that silently don’t fire — plus a hash-verified backup script, AI-agnostic (works with any LLM/agent that can read and write files): github.com/mindspanner/streamdeck-llm-field-kit

Watch the companion episode: Your Stream Deck can set itself up — let Claude do it