feat: configurable volume increment for Volume Up/Down actions#4
Open
rodbegbie wants to merge 10 commits into
Open
feat: configurable volume increment for Volume Up/Down actions#4rodbegbie wants to merge 10 commits into
rodbegbie wants to merge 10 commits into
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use ?? instead of || when saving per-button override so a value of 1 isn't treated as falsy and silently discarded - Clamp global increment to Math.max(1, value) before persisting since HTML min="1" doesn't block Vue's v-model binding in Electron's webview Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
r-teller
added a commit
that referenced
this pull request
May 4, 2026
CLEANROOM authoring. The load-bearing fix for the prd-what.md §3.3
regression where one-element households surface as
"Failed to get devices: <name> is not iterable".
src/modules/common/sonosController.js — adds:
- `getDevices({setAsPrimary})` walks ZoneGroupState topology returning
DeviceRecord[]. Every iteration over ZoneGroup, ZoneGroupMember, and
Satellite goes through `asArray` — the discipline that prevents the
regression class. Wraps with translateSonosError({op: "get devices"})
so network/timeout/parse/safety-net failures all surface as
"Failed to get devices: ..." per prd-what.md §7.7.
- Static `SonosController.getDeviceLocation(member)` extracts host from
member._attributes.Location URL.
- Private `_memberToRecord(member, isSatellite, setAsPrimary)` produces
the canonical DeviceRecord shape per data-model.md.
Fixtures: tests/fixtures/sonos/zonegroupstate-{stereo-pair, home-theater,
multi-group}.xml — three new SOAP envelopes covering the AC#2/#3/#4
topologies. The single-group-single-member fixture from h75.2 covers AC#1
(the regression case).
Tests: 101 → 114 (13 new in sonosController.discovery.test.js covering
all 7 ACs: single-group regression, stereo pair, home theater
1+3 satellites, multi-group 4 members across 3 groups, primary marking
on/off, UUID stability, network-failure translation, safety-net
translation, getDeviceLocation host extraction).
Closes: streamdeck-sonoscontroller-h75.3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
r-teller
added a commit
that referenced
this pull request
May 5, 2026
…very Phase 4 (Property Inspector) advances from 8% → 92% in one push. Plugin is now manually testable end-to-end against a real Sonos household: type IP → click Save and Connect → see speakers populate → bind a key to a speaker → configure per-action toggles. orw.2: BootstrapAccordeon + BootstrapAccordeonItem reusable primitives. forceExpanded prop drives initial show/collapse + aria-expanded; user clicks override via Bootstrap Collapse JS; reactive watcher syncs prop changes via .show()/.hide(). Listens to shown.bs.collapse / hidden.bs.collapse to track user-driven state. provide/inject for accordeonId. Establishes Vue component testing pattern via @vue/test-utils + jsdom — 11 tests cover forceExpanded, click toggle, aria, slot, id composition. orw.10: errorMessage ref + Bootstrap alert-danger inside Global Settings accordion. Hidden when null (no empty shell). X dismisses; second failure re-renders. Verbatim message text — no scrubbing (Phase 2 boundary translation owns the wrapping). orw.11: src/modules/pi/actionSettingsSchema.js — buildActionSettingsPayload owns the 16-field per-context schema with action-specific gating. PR #4 ?? null preserves value 1; base64-decode utility for favorite metadata round-trip. Allow-list constants exported (DISPLAY_*_ACTIONS, PLAY_MODE_DEFAULTS, INPUT_SOURCE_DEFAULTS). orw.12: src/modules/pi/globalSettingsSchema.js — buildGlobalSettingsPayload with Math.max(1, value) clamp on adjustVolumeIncrement at write site (HTML min="1" is bypassable in the Stream Deck Electron webview's v-model.number binding, so input-side validation alone is insufficient). orw.3: src/components/SonosSelection.vue — speaker dropdown (size=5) with case-insensitive sort by '<zoneName> (<host>) [🛰️]' label, grey hint line, free-text filter (label OR UUID, case-insensitive). v-model UUID + emits selection-saved on change. orw.5: 4 presentation toggles in PiComponent (Display State Based Title / Marquee Title / Marquee Album Title / Album Art). Each gated on per-action allow-list (allow-list constants from actionSettingsSchema). Auto-save on change. orw.6: 4 action-specific sections — Play Mode(s) / Input Source(s) / Equalizer Target / Sonos Favorite(s). Six play-mode switches with verbatim labels (Normal, Shuffle No Repeat, Shuffle Repeat One, Shuffle, Repeat One, Repeat All), three input-source switches, EQ select (Volume/Bass/Treble), favorites select keyed by URI. All auto-save. orw.7: Volume Increment override input (volume-up / volume-down only). Verbatim labels and helper text per prd-what.md §5.8. Placeholder reflects current global default. PR #4 ?? null preserves entered value 1. orw.8: Global Settings accordion built on orw.2 primitive with forceExpanded={!isConnected}. Four form fields with verbatim labels and hint text per prd-what.md §6. Reactive bindings to globalSettings ref. Math.max(1, value) clamp lives at the saveGlobalSettings write site (orw.12), not on the input. orw.9: Save and Connect / Save and Reconnect button. Label flips on connectionState. Spinner during flight. Disabled when address empty or in flight. Click handler: SonosController.connect → Promise.all(getDevices, getFavorites) sharing one controller (so zoneGroupState memoizes), persists via saveGlobalSettings, builds speakers picker. Brand-new actions default to discovery seed primary. Sonos Speakers accordion with embedded SonosSelection bound to settings.uuid via @selection-saved → saveSettings. Tests: +180 (210 → 390 total). 13 new test files cover schemas, primitives, every per-action visibility matrix, PR #3 tolerance fixtures (single-Move / stereo-pair / home-theater), error path, button state machine. Closes: streamdeck-sonoscontroller-orw.2 Closes: streamdeck-sonoscontroller-orw.3 Closes: streamdeck-sonoscontroller-orw.5 Closes: streamdeck-sonoscontroller-orw.6 Closes: streamdeck-sonoscontroller-orw.7 Closes: streamdeck-sonoscontroller-orw.8 Closes: streamdeck-sonoscontroller-orw.9 Closes: streamdeck-sonoscontroller-orw.10 Closes: streamdeck-sonoscontroller-orw.11 Closes: streamdeck-sonoscontroller-orw.12 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human writing: This is a change I made to allow setting the increment value for the volume buttons, since the default of 10 was too jumpy for me. (I didn’t change the default from the existing 10). I included the “design doc” that Claude generated so you can understand the approach I guided it to take. (Everything after this is Claude writing, not me!)
Summary
How it works
deviceCheckInterval,deviceTimeoutDuration).Design doc
See
docs/superpowers/specs/2026-04-05-volume-increment-setting-design.mdfor full design rationale.Test plan
🤖 Generated with Claude Code