Commit a41a31c
feat(touchstrip): per-segment icon and background on Stream Deck + / + XL (#20)
## Summary
Closes the loop on the Stream Deck + / + XL hardware. The MCP can now
write per-segment touchstrip art — icon overlays and 200×100 backgrounds
— that survives an Elgato app restart and renders on the physical
device.
Ships a minimal companion Stream Deck plugin inside the Python package.
It's the only path Elgato exposes for this: per-instance
`Action.Encoder.Icon` and `Action.Encoder.background` writes get
stripped on quit unless the action's plugin declares `Controllers:
["Encoder"]`. The bundled plugin is that declaration, nothing more — no
CodePath business logic.
Also a small correctness fix on `icon_scale` semantics that made the
glyph sizing visibly undersized vs native Elgato icons on the strip.
## What's new
- **`streamdeck_create_icon`** gained `shape="button" | "touchstrip"`
(200×100 for strip backgrounds) and `transparent_bg=True` (RGBA canvas
for dial Icons that compose over a strip background). Default
`icon_scale` bumped `0.7 → 1.0` so the glyph bounding box fills the
canvas edge-to-edge, matching how Elgato's own icons fill the touchstrip
slot.
- **`streamdeck_write_page`** button schema adds `strip_background_path`
(encoder-only; validation error on keypad). Encoder buttons route
`icon_path` to `Action.Encoder.Icon` and `strip_background_path` to
`Action.Encoder.background`. Encoder buttons with no explicit action
spec default to the bundled MCP dial plugin, so a full dial is now just
`{controller: "encoder", key: N, icon_path: ..., strip_background_path:
..., title: ...}`.
- **`streamdeck_install_mcp_plugin`** MCP tool. Idempotent (`force=true`
to reinstall). `streamdeck_write_page` auto-installs the plugin the
first time an encoder button needs it, so direct use is rarely
necessary.
- **Bundled plugin** at
`streamdeck_plugin/io.github.verygoodplugins.streamdeck-mcp.sdPlugin/`
with a minimal HTML/JS shell (~30 lines of JS, SDK registration only),
correctly-sized plugin/action icons per SDK spec, and no
`Encoder.layout` — that omission gives us Elgato's default layout, which
lets the full-strip `Controllers[Encoder].Background` show through
segments whose per-segment `background` is unset.
## Scope tightening / lessons from the SDK docs
I probed this empirically for several app-restart cycles before
@JGArturo pointed at https://docs.elgato.com/streamdeck/sdk/ which
predicts exactly what we found (image dimensions, the six built-in
layouts, the plugin-vs-profile field split). SKILL.md now links the docs
at the top, includes the authoritative image-dimensions table, and names
the six layouts so future sessions start there. Saved AutoMem entries so
this lesson persists.
## On-device verification (Stream Deck + XL, 20GBX9901)
- Six encoder segments with distinct MDI glyphs + transparent icon
overlays + per-segment 200×100 backgrounds render the intended art on
the physical strip, not just the app preview
- Full-strip `Controllers[Encoder].Background` shows through segments
whose per-segment background is cleared
- Glyph size at `icon_scale=1.0` matches Elgato's native icon sizing
(~22px visible)
## Test plan
- [x] `uv run pytest tests/` — **64 passed** (7 new: touchstrip shape,
shape validation, plugin install idempotency, encoder
`Encoder.Icon`/`background` writes, keypad strip_background rejection,
write_page auto-install)
- [x] `uv run ruff check .` — clean
- [x] `uv build --wheel` — plugin bundle ships inside the wheel
correctly
## Known follow-ups (out of scope)
- **`icon_scale` default on keypad**: 1.0 fills the button edge-to-edge.
On keypad buttons with a bottom title, the glyph touches the title
overlay. Callers that want breathing room pass `icon_scale=0.75-0.85`
explicitly. Worth revisiting if this becomes annoying in practice.
- **Layout flexibility**: `Encoder.layout` is currently unset (default
layout). Future PR could expose `$X1` / `$A1` / `$B1` / custom JSON
layouts on the plugin side to support richer touchstrip UIs (progress
bars, dual icons).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jack-arturo <13076544+jack-arturo@users.noreply.github.com>1 parent 0655a44 commit a41a31c
14 files changed
Lines changed: 568 additions & 23 deletions
File tree
- skills/streamdeck-profile
- streamdeck_plugin
- io.github.verygoodplugins.streamdeck-mcp.sdPlugin
- Images
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | | - | |
| 68 | + | |
69 | 69 | | |
70 | 70 | | |
| 71 | + | |
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
| |||
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
| 80 | + | |
79 | 81 | | |
80 | 82 | | |
81 | 83 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
| |||
312 | 314 | | |
313 | 315 | | |
314 | 316 | | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
315 | 365 | | |
316 | 366 | | |
317 | 367 | | |
| |||
585 | 635 | | |
586 | 636 | | |
587 | 637 | | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
588 | 646 | | |
589 | 647 | | |
590 | 648 | | |
| |||
595 | 653 | | |
596 | 654 | | |
597 | 655 | | |
598 | | - | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
599 | 659 | | |
600 | 660 | | |
601 | 661 | | |
| |||
641 | 701 | | |
642 | 702 | | |
643 | 703 | | |
| 704 | + | |
644 | 705 | | |
645 | 706 | | |
646 | 707 | | |
| |||
649 | 710 | | |
650 | 711 | | |
651 | 712 | | |
652 | | - | |
| 713 | + | |
653 | 714 | | |
654 | 715 | | |
655 | 716 | | |
656 | 717 | | |
| 718 | + | |
| 719 | + | |
657 | 720 | | |
658 | | - | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
659 | 734 | | |
660 | 735 | | |
661 | 736 | | |
| |||
683 | 758 | | |
684 | 759 | | |
685 | 760 | | |
686 | | - | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
687 | 769 | | |
688 | 770 | | |
689 | 771 | | |
| |||
700 | 782 | | |
701 | 783 | | |
702 | 784 | | |
| 785 | + | |
| 786 | + | |
703 | 787 | | |
704 | 788 | | |
705 | | - | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
706 | 793 | | |
707 | 794 | | |
708 | 795 | | |
709 | | - | |
| 796 | + | |
| 797 | + | |
710 | 798 | | |
711 | 799 | | |
712 | 800 | | |
713 | 801 | | |
714 | | - | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
715 | 814 | | |
716 | 815 | | |
717 | 816 | | |
718 | 817 | | |
719 | 818 | | |
720 | 819 | | |
721 | 820 | | |
722 | | - | |
723 | | - | |
| 821 | + | |
| 822 | + | |
724 | 823 | | |
725 | 824 | | |
726 | 825 | | |
727 | 826 | | |
728 | 827 | | |
729 | 828 | | |
730 | | - | |
731 | | - | |
| 829 | + | |
| 830 | + | |
732 | 831 | | |
733 | 832 | | |
734 | 833 | | |
| |||
737 | 836 | | |
738 | 837 | | |
739 | 838 | | |
740 | | - | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
741 | 842 | | |
742 | 843 | | |
743 | 844 | | |
| |||
1028 | 1129 | | |
1029 | 1130 | | |
1030 | 1131 | | |
1031 | | - | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
| 1135 | + | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
1032 | 1139 | | |
1033 | 1140 | | |
1034 | | - | |
| 1141 | + | |
1035 | 1142 | | |
1036 | 1143 | | |
1037 | 1144 | | |
| |||
1076 | 1183 | | |
1077 | 1184 | | |
1078 | 1185 | | |
1079 | | - | |
1080 | | - | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
1081 | 1207 | | |
1082 | 1208 | | |
1083 | 1209 | | |
1084 | 1210 | | |
1085 | 1211 | | |
1086 | | - | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
1087 | 1215 | | |
1088 | 1216 | | |
1089 | 1217 | | |
| |||
1112 | 1240 | | |
1113 | 1241 | | |
1114 | 1242 | | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
1115 | 1249 | | |
1116 | 1250 | | |
1117 | 1251 | | |
1118 | 1252 | | |
1119 | 1253 | | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
| 1310 | + | |
| 1311 | + | |
| 1312 | + | |
| 1313 | + | |
| 1314 | + | |
1120 | 1315 | | |
1121 | 1316 | | |
1122 | 1317 | | |
| |||
0 commit comments