Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ jobs:
3. HIGH SEVERITY — flag any safety-class mislabeling. A command that moves
the droid or affects power must NOT be marked safety:"cosmetic". Per
the project's Code of Conduct, mislabeling can cause real hardware to
move unexpectedly. Call this out prominently.
move unexpectedly. Call this out prominently. A command that runs a
command the user stored on the board (a WCB `;C<key>`/`;SEQ<key>` run,
or an `ONFIN,<key>` callback) must be marked safety:"storedcommand",
not "cosmetic" or "config" — the library cannot see what the stored
content does, so it must not be labeled as if its effect were known.

4. For changes to src/*.js: check correctness, keep the engine DOM-free
and the UI going through buildWCBValue/parseWCBValue, and preserve the
Expand Down
9 changes: 7 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,13 @@ libraries/manifest.json ──fetch+merge──▶ loadLibrary ─
param names must exactly match the placeholders (validator enforces both
directions). `component.kind` is `device-native` (the board's own grammar) or
`wcb-verb` (interpreted by an intermediary like a WCB). `command.safety`
(`cosmetic`/`movement`/`power`/`config`) drives a confirm-before-firing warning in
the UI — anything non-`cosmetic` is treated as potentially dangerous.
(`cosmetic`/`movement`/`power`/`config`/`storedcommand`) drives a confirm-before-firing
warning in the UI — anything non-`cosmetic` is treated as potentially dangerous.
`storedcommand` is for commands that run a command the user stored on the board (a WCB
stored command, `;C<key>`) that can be any command, so the library cannot know whether
it moves hardware, switches power or changes settings; if the device itself limits what
saved content can do (a Maestro script, a Roam-a-Dome sequence), use that limit's class
instead.

A command may declare a `category` (e.g. `"Lighting"`); the component declares an
ordered `categories` array that lists every category it uses and fixes the
Expand Down
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ for a worked example), with tests. Libraries reference it by `encoder` name.
Be kind, assume good faith, and keep board data accurate. Misrepresenting a
command's `safety` class (e.g. labelling a movement command `cosmetic`) can cause
real hardware to move unexpectedly — when in doubt, choose the more cautious class.
Use `storedcommand` for commands that run a command the user stored on the board (a
WCB stored command, `;C<key>`) that can be any command, so the library cannot know
whether it moves hardware, switches power or changes settings; if the device itself
limits what saved content can do (a Maestro script, a Roam-a-Dome sequence), use that
limit's class instead.

## License

Expand Down
2 changes: 1 addition & 1 deletion assets/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--s
.dn-badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: .72rem;
background: var(--panel-2); color: var(--muted); border: 1px solid var(--edge); font-family: var(--mono); }
.dn-ver { color: var(--accent); }
.dn-safety-movement, .dn-safety-power { color: var(--warn); border-color: var(--warn); }
.dn-safety-movement, .dn-safety-power, .dn-safety-storedcommand { color: var(--warn); border-color: var(--warn); }
.dn-safety-config { color: var(--accent); border-color: var(--accent); }
.dn-conf-community { color: var(--warn); border-color: var(--warn); }
.dn-conf-low { color: var(--danger); border-color: var(--danger); }
Expand Down
10 changes: 7 additions & 3 deletions docs/BOARD_AUTHORING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Required: `id` (unique across the whole library) and `name`.
"id": "myboard.solid", // globally unique
"name": "Solid Color",
"category": "Lighting", // dropdown section — must be in the component's `categories`
"safety": "cosmetic", // cosmetic | movement | power | config
"safety": "cosmetic", // cosmetic | movement | power | config | storedcommand
"encoder": "template", // default; can omit
"template": "C{color}",
"params": [ { "name": "color", "enum": "myboard.color", "default": "1" } ],
Expand All @@ -104,8 +104,12 @@ one.

**`safety` matters.** Anything other than `cosmetic` triggers a
confirm-before-firing warning in the UI. Use `movement` for anything that moves
hardware, `power` for power switching, `config` for settings that change state.
When unsure, pick the more cautious class.
hardware, `power` for power switching, `config` for settings that change state, and
`storedcommand` for commands that run a command the user stored on the board (a WCB
stored command, `;C<key>`) that can be any command, so the library cannot know
whether it moves hardware, switches power or changes settings; if the device itself
limits what saved content can do (a Maestro script, a Roam-a-Dome sequence), use
that limit's class instead. When unsure, pick the more cautious class.

**Firmware requirements go in the name.** Neither the composer nor the hosted
reference shows a component's `firmware` or `routing.notes`, so users never see
Expand Down
4 changes: 4 additions & 0 deletions docs/INTEGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ engine being loaded first.

The composer handles add/edit/remove, drag-reorder, inline delays and notes, a
length counter, and a safety warning when any non-`cosmetic` command is present.
If you build your own UI instead of using the composer, show any `safety` value
other than `cosmetic` verbatim as a warning — including a value your integration
doesn't recognise (a future release can add a class you haven't seen yet). Don't
special-case which strings count as a warning; anything non-`cosmetic` is one.

The command dropdown in the add bar renders as ordered `<optgroup>` sections
following the selected board's `component.categories` array (uncategorized
Expand Down
56 changes: 56 additions & 0 deletions libraries/boards/droidnet-flthy-hps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"$schema": "droidnet-command-library/library/v1",
"enums": {
"flthy.designator": {
"label": "Holoprojector",
"values": [
{ "code": "F", "label": "Front" },
{ "code": "R", "label": "Rear" },
{ "code": "T", "label": "Top" },
{ "code": "X", "label": "Front & Rear" },
{ "code": "Y", "label": "Front & Top" },
{ "code": "Z", "label": "Rear & Top" },
{ "code": "A", "label": "All" }
]
}
},
"components": [
{
"id": "droidnet-flthy-hps",
"name": "FlthyHPs · DroidNet fork",
"kind": "device-native",
"confidence": "community",
"firmware": "travisccook/DroidNet-FlthyHPs @ 75755de",
"routing": {
"class": "broadcast",
"nativeWrapper": "none",
"durationSuffix": { "supported": false }
},
"categories": ["Lighting"],
"commands": [
{
"id": "flthy.led.brightness",
"name": "Brightness",
"safety": "cosmetic",
"category": "Lighting",
"encoder": "template",
"template": "H{unit}B{level}",
"params": [
{ "name": "unit", "enum": "flthy.designator", "default": "A" },
{
"name": "level",
"label": "Brightness (1–255)",
"type": "int",
"min": 1,
"max": 255,
"required": true
}
],
"supportsDuration": false,
"examples": ["HAB128", "HFB255", "HTB1"],
"commentLabel": "Holos {unit} brightness {level}"
}
]
}
]
}
52 changes: 52 additions & 0 deletions libraries/boards/droidnet-rseries-logic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"$schema": "droidnet-command-library/library/v1",
"enums": {
"rseries.unit": {
"label": "Display",
"values": [
{ "code": "", "label": "Both" },
{ "code": "F", "label": "Front" },
{ "code": "R", "label": "Rear" }
]
}
},
"components": [
{
"id": "droidnet-rseries-logic",
"name": "RSeriesLogic · DroidNet fork",
"kind": "device-native",
"confidence": "community",
"firmware": "travisccook/DroidNet-RSeriesLogics @ aa45d79",
"routing": {
"class": "broadcast",
"nativeWrapper": "none",
"durationSuffix": { "supported": false }
},
"categories": ["Lighting"],
"commands": [
{
"id": "rseries.cfg.brightness",
"name": "Brightness",
"safety": "cosmetic",
"category": "Lighting",
"encoder": "template",
"template": "#LEBRI{unit}{level}",
"params": [
{ "name": "unit", "enum": "rseries.unit", "default": "" },
{
"name": "level",
"label": "Brightness (1–255)",
"type": "int",
"min": 1,
"max": 255,
"required": true
}
],
"supportsDuration": false,
"examples": ["#LEBRI128", "#LEBRIF255", "#LEBRIR1"],
"commentLabel": "Logics {unit} brightness {level}"
}
]
}
]
}
6 changes: 3 additions & 3 deletions libraries/boards/wcb-dfp.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"template": ";D,PLAY,{track}",
"params": [ { "name": "track", "type": "int", "min": 1, "max": 2999, "required": true } ],
"examples": [";D,PLAY,5"], "commentLabel": "DFPlayer play", "category": "Sound" },
{ "id": "dfp.playCb", "name": "Play Track + Callback", "safety": "cosmetic", "encoder": "template",
{ "id": "dfp.playCb", "name": "Play Track + Callback", "safety": "storedcommand", "encoder": "template",
"template": ";D,PLAY,{track},ONFIN,{key}",
"params": [ { "name": "track", "type": "int", "min": 1, "max": 2999, "required": true },
{ "name": "key", "pattern": "[A-Za-z0-9_]+", "required": true } ],
Expand All @@ -46,7 +46,7 @@
"params": [ { "name": "folder", "type": "int", "min": 1, "max": 99, "required": true },
{ "name": "track", "type": "int", "min": 1, "max": 255, "required": true } ],
"examples": [";D,FOLDER,1,5"], "commentLabel": "DFPlayer folder", "category": "Sound" },
{ "id": "dfp.folderCb", "name": "Play Folder Track + Callback", "safety": "cosmetic", "encoder": "template",
{ "id": "dfp.folderCb", "name": "Play Folder Track + Callback", "safety": "storedcommand", "encoder": "template",
"template": ";D,FOLDER,{folder},{track},ONFIN,{key}",
"params": [ { "name": "folder", "type": "int", "min": 1, "max": 99, "required": true },
{ "name": "track", "type": "int", "min": 1, "max": 255, "required": true },
Expand All @@ -56,7 +56,7 @@
"template": ";D,MP3FOLDER,{track}",
"params": [ { "name": "track", "type": "int", "min": 1, "max": 9999, "required": true } ],
"examples": [";D,MP3FOLDER,3"], "commentLabel": "DFPlayer MP3 folder", "category": "Sound" },
{ "id": "dfp.mp3FolderCb", "name": "Play /MP3 Folder Track + Callback", "safety": "cosmetic", "encoder": "template",
{ "id": "dfp.mp3FolderCb", "name": "Play /MP3 Folder Track + Callback", "safety": "storedcommand", "encoder": "template",
"template": ";D,MP3FOLDER,{track},ONFIN,{key}",
"params": [ { "name": "track", "type": "int", "min": 1, "max": 9999, "required": true },
{ "name": "key", "pattern": "[A-Za-z0-9_]+", "required": true } ],
Expand Down
4 changes: 2 additions & 2 deletions libraries/boards/wcb-mp3.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"template": ";A,PLAY,{track}",
"params": [ { "name": "track", "type": "int", "min": 1, "max": 255, "required": true } ],
"examples": [";A,PLAY,1"], "commentLabel": "MP3 play", "category": "Sound" },
{ "id": "mp3.playCb", "name": "Play Track + Callback", "safety": "cosmetic", "encoder": "template",
{ "id": "mp3.playCb", "name": "Play Track + Callback", "safety": "storedcommand", "encoder": "template",
"template": ";A,PLAY,{track},ONFIN,{key}",
"params": [ { "name": "track", "type": "int", "min": 1, "max": 255, "required": true },
{ "name": "key", "pattern": "[A-Za-z0-9_]+", "required": true } ],
Expand All @@ -26,7 +26,7 @@
"template": ";A,PLAYFS,{track}",
"params": [ { "name": "track", "type": "int", "min": 0, "max": 255, "required": true } ],
"examples": [";A,PLAYFS,0"], "commentLabel": "MP3 play FS", "category": "Sound" },
{ "id": "mp3.playFsCb", "name": "Play Track (FS) + Callback", "safety": "cosmetic", "encoder": "template",
{ "id": "mp3.playFsCb", "name": "Play Track (FS) + Callback", "safety": "storedcommand", "encoder": "template",
"template": ";A,PLAYFS,{track},ONFIN,{key}",
"params": [ { "name": "track", "type": "int", "min": 0, "max": 255, "required": true },
{ "name": "key", "pattern": "[A-Za-z0-9_]+", "required": true } ],
Expand Down
8 changes: 4 additions & 4 deletions libraries/boards/wcb-native.json
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@
{
"id": "wcb.runSeq",
"name": "Run Sequence (short)",
"safety": "config",
"safety": "storedcommand",
"encoder": "template",
"template": ";C{key}",
"params": [
Expand All @@ -1615,7 +1615,7 @@
{
"id": "wcb.runSeqLong",
"name": "Run Sequence (long)",
"safety": "config",
"safety": "storedcommand",
"encoder": "template",
"template": ";SEQ{key}",
"params": [
Expand All @@ -1634,7 +1634,7 @@
{
"id": "wcb.runSeqLocal",
"name": "Run Sequence on This Board Only (WCB 6.2+)",
"safety": "config",
"safety": "storedcommand",
"encoder": "template",
"template": ";C{key},L",
"params": [
Expand All @@ -1653,7 +1653,7 @@
{
"id": "wcb.runSeqLongLocal",
"name": "Run Sequence on This Board Only, long (WCB 6.2+)",
"safety": "config",
"safety": "storedcommand",
"encoder": "template",
"template": ";SEQ{key},L",
"params": [
Expand Down
16 changes: 14 additions & 2 deletions libraries/manifest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
{
"$schema": "droidnet-command-library/catalog/v1",
"libraryVersion": "4.3.0",
"libraryVersion": "4.4.0",
"schemaVersion": "v1",
"generatedFrom": "C2B5 firmware + WIPDocs (2026-06); R2UppitySpinnerV3 ALT fw 3.5.3 (2026-07); Printed Droid Magic Panel 32 Wireless KB (2026-07); WCB 6.1.5_290119RJUN2026 (2026-07); WCB 6.2.1_021242RSEP2026 + WcbCmd 0.8.0 (2026-09)",
"generatedFrom": "C2B5 firmware + WIPDocs (2026-06); R2UppitySpinnerV3 ALT fw 3.5.3 (2026-07); Printed Droid Magic Panel 32 Wireless KB (2026-07); WCB 6.1.5_290119RJUN2026 (2026-07); WCB 6.2.1_021242RSEP2026 + WcbCmd 0.8.0 (2026-09); DroidNet forks (2026-09)",
"boards": [
{
"id": "flthy-hps",
"file": "boards/flthy-hps.json",
"name": "FlthyHPs Holoprojectors",
"confidence": "high"
},
{
"id": "droidnet-flthy-hps",
"file": "boards/droidnet-flthy-hps.json",
"name": "FlthyHPs · DroidNet fork",
"confidence": "community"
},
{
"id": "ia-magic-panel",
"file": "boards/ia-magic-panel.json",
Expand All @@ -28,6 +34,12 @@
"name": "RSeriesLogic (Logic Displays)",
"confidence": "high"
},
{
"id": "droidnet-rseries-logic",
"file": "boards/droidnet-rseries-logic.json",
"name": "RSeriesLogic · DroidNet fork",
"confidence": "community"
},
{
"id": "wcb-hcr",
"file": "boards/wcb-hcr.json",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "droidnet-command-library",
"version": "4.3.0",
"version": "4.4.0",
"description": "Schema-driven encoder/decoder and visual composer for building serial commands to control a vibrant community of Astromech droid components. Anyone can add their board by editing a JSON library — no code changes required.",
"license": "MPL-2.0",
"keywords": [
Expand Down
8 changes: 4 additions & 4 deletions releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
"$schema": "droidnet-command-library/releases/v1",
"_comment": "Update pointer for the 'Update Library' flow. A device fetches this file, compares latest.libraryVersion to its installed catalog, and (if newer) downloads latest.url (the catalog manifest), then fetches each board listed in manifest.boards in order, validates them, and loads them via loadLibrary(boards, { libraryVersion }).",
"latest": {
"libraryVersion": "4.3.0",
"libraryVersion": "4.4.0",
"schemaVersion": "v1",
"releasedAt": "2026-09-15",
"releasedAt": "2026-09-23",
"url": "https://raw.githubusercontent.com/travisccook/droidnet-command-library/main/libraries/manifest.json",
"notes": "Minor: WCB firmware 6.2.1 sweep. Maestro (maestro) adds the WCB 6.2+ comma forms: Run Subroutine (with and without a parameter, number and sub-verb spellings), Stop Script, Go Home, Set Servo Target/Speed/Acceleration, and Read Position/Moving State/Errors queries. Ids widen to 0-9 (9 = this WCB's Maestros) and subroutines to 0-127. Adds WCB · DFPlayer Mini board (wcb-dfp, WCB 6.2+): 21 ;D, verbs (play/folder/MP3-folder with optional ONFIN callback, stop/pause/resume/next/prev/random, loop track/all/folder, EQ, volume/up/down, reset, status). WCB · HCR Vocalizer (wcb-hcr) adds TRIGGER, all-channel VOL and VOLUP/VOLDN steps, and limits FN to the firmware's function codes. WCB (native config) (wcb-native) adds 66 commands (mesh channel, alias, controller, status LED pin/identify, WLED/DFPlayer config, MP3/DFPlayer/HCR host routing, Maestro remote/Kyber map, ;W alias routing, ;P PWM pulse, ;V/;VP variables and IF, sequence names/get and ,L local runs, timer stop, WDP, debug/track/version) and corrects ranges (board numbers 1-20, 30-minute timer, S0 routing, HCR baud/poll/get fields, ETM miss 100, multi-destination maps). WLED presets start at 1; the MP3 Trigger and WLED boards document 6.2.1 routing. FlthyHPs Holoprojectors (flthy-hps): Solid Color, Rainbow and Short Circuit now send the codes FlthyHPs v1.6+ firmware runs (005, 006, 007; 4.2.0 sent 006, 007, 005), and the X/Y/Z designators are back. Saved steps keep their exact text, but they decode by what the firmware runs: steps saved by 4.2.0 as Solid Color or Rainbow (006, 007) now show as raw text, and 4.2.0 Short Circuit steps (005) now show as Solid Color. Text fields (keys, aliases, variable names, conditions, colors) need a host that uses engine 3.1.0 or later. Generated against WCB 6.2.1_021242RSEP2026 + WcbCmd 0.8.0; board catalog 21 -> 22, commands 397 -> 500."
"notes": "Minor: DroidNet fork boards. Adds two device-native boards, each listed right after its stock board. RSeriesLogic · DroidNet fork (droidnet-rseries-logic) has Brightness (rseries.cfg.brightness): #LEBRI{unit}{level}, where the unit is none (both displays), F (front) or R (rear) and the level is 1-255. FlthyHPs · DroidNet fork (droidnet-flthy-hps) has Brightness (flthy.led.brightness): H{unit}B{level}, where the unit is F, R, T, X, Y, Z or A (all) and the level is 1-255. Both need the DroidNet fork firmware named in the board's firmware field; stock RSeriesLogic and FlthyHPs firmware ignore them. The level is RAM-only: nothing is saved on the board, and a power cycle restores the power-on brightness. The stock RSeriesLogic and FlthyHPs boards are unchanged. Board catalog 22 -> 24, commands 500 -> 502. Safety: new class `storedcommand` for commands that run a command the user saved on the WCB (;C/;SEQ run-sequence, and the DFPlayer/MP3 play+ONFIN callbacks, which were cosmetic). Queries, save and clear stay config."
},
"libraries": [
{
"id": "droidnet-astromech",
"name": "DroidNet Astromech Boards",
"libraryVersion": "4.3.0",
"libraryVersion": "4.4.0",
"schemaVersion": "v1",
"url": "https://raw.githubusercontent.com/travisccook/droidnet-command-library/main/libraries/manifest.json"
}
Expand Down
Loading
Loading