homebridge-sonos-scenes 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ The goal is not general Sonos control. The goal is a clean way to trigger multi-
|
|
|
20
20
|
|
|
21
21
|
## What Is Implemented
|
|
22
22
|
|
|
23
|
-
- A singular Homebridge dynamic platform plugin with switch accessories for each configured scene.
|
|
23
|
+
- A singular Homebridge dynamic platform plugin with switch accessories for each configured scene plus companion volume controls for quick level adjustments in Apple Home.
|
|
24
24
|
- A normalized scene model that stores stable Sonos IDs instead of room-name strings.
|
|
25
25
|
- A local-first `SonosTransport` abstraction with live discovery through the `sonos` package and fixture fallback for UI/testing.
|
|
26
26
|
- A `SceneRunner` that validates scenes, serializes execution per coordinator, retries transient failures, and emits structured logs.
|
|
@@ -7,12 +7,13 @@ export declare class SceneSpeakerAccessory {
|
|
|
7
7
|
private service;
|
|
8
8
|
private scene;
|
|
9
9
|
private lastKnownVolume;
|
|
10
|
+
private lastKnownActiveVolume;
|
|
10
11
|
private lastKnownMuted;
|
|
11
12
|
constructor(platform: SonosScenesPlatform, accessory: PlatformAccessory, scene: SceneDefinition);
|
|
12
13
|
updateScene(scene: SceneDefinition): void;
|
|
13
14
|
private displayNameFor;
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
private
|
|
15
|
+
private handleBrightnessGet;
|
|
16
|
+
private handleBrightnessSet;
|
|
17
|
+
private handleOnGet;
|
|
18
|
+
private handleOnSet;
|
|
18
19
|
}
|
|
@@ -7,76 +7,122 @@ class SceneSpeakerAccessory {
|
|
|
7
7
|
service;
|
|
8
8
|
scene;
|
|
9
9
|
lastKnownVolume;
|
|
10
|
+
lastKnownActiveVolume;
|
|
10
11
|
lastKnownMuted = false;
|
|
11
12
|
constructor(platform, accessory, scene) {
|
|
12
13
|
this.platform = platform;
|
|
13
14
|
this.accessory = accessory;
|
|
14
15
|
this.scene = scene;
|
|
15
|
-
this.lastKnownVolume = scene.coordinatorVolume ??
|
|
16
|
+
this.lastKnownVolume = scene.coordinatorVolume ?? 30;
|
|
17
|
+
this.lastKnownActiveVolume = this.lastKnownVolume > 0 ? this.lastKnownVolume : 30;
|
|
16
18
|
this.accessory.context.sceneId = scene.id;
|
|
17
|
-
this.accessory.context.kind = "
|
|
19
|
+
this.accessory.context.kind = "volume";
|
|
18
20
|
this.accessory.displayName = this.displayNameFor(scene);
|
|
19
|
-
this.accessory.category =
|
|
21
|
+
this.accessory.category = 5 /* this.platform.api.hap.Categories.LIGHTBULB */;
|
|
22
|
+
const legacySpeakerService = this.accessory.getService(this.platform.Service.Speaker);
|
|
23
|
+
if (legacySpeakerService) {
|
|
24
|
+
this.accessory.removeService(legacySpeakerService);
|
|
25
|
+
}
|
|
20
26
|
this.service =
|
|
21
|
-
this.accessory.getService(this.platform.Service.
|
|
22
|
-
?? this.accessory.addService(this.platform.Service.
|
|
23
|
-
if (!this.service.testCharacteristic(this.platform.Characteristic.
|
|
24
|
-
this.service.addCharacteristic(this.platform.Characteristic.
|
|
27
|
+
this.accessory.getService(this.platform.Service.Lightbulb)
|
|
28
|
+
?? this.accessory.addService(this.platform.Service.Lightbulb);
|
|
29
|
+
if (!this.service.testCharacteristic(this.platform.Characteristic.Brightness)) {
|
|
30
|
+
this.service.addCharacteristic(this.platform.Characteristic.Brightness);
|
|
25
31
|
}
|
|
26
32
|
this.service.setCharacteristic(this.platform.Characteristic.Name, this.displayNameFor(scene));
|
|
27
|
-
this.service.getCharacteristic(this.platform.Characteristic.
|
|
28
|
-
.onGet(this.
|
|
29
|
-
.onSet(this.
|
|
30
|
-
this.service.getCharacteristic(this.platform.Characteristic.
|
|
31
|
-
.onGet(this.
|
|
32
|
-
.onSet(this.
|
|
33
|
+
this.service.getCharacteristic(this.platform.Characteristic.On)
|
|
34
|
+
.onGet(this.handleOnGet.bind(this))
|
|
35
|
+
.onSet(this.handleOnSet.bind(this));
|
|
36
|
+
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
|
|
37
|
+
.onGet(this.handleBrightnessGet.bind(this))
|
|
38
|
+
.onSet(this.handleBrightnessSet.bind(this));
|
|
33
39
|
const accessoryInformation = this.accessory.getService(this.platform.Service.AccessoryInformation)
|
|
34
40
|
?? this.accessory.addService(this.platform.Service.AccessoryInformation);
|
|
35
41
|
accessoryInformation
|
|
36
42
|
.setCharacteristic(this.platform.Characteristic.Manufacturer, "homebridge-sonos-scenes")
|
|
37
|
-
.setCharacteristic(this.platform.Characteristic.Model, "Sonos Scene
|
|
38
|
-
.setCharacteristic(this.platform.Characteristic.SerialNumber, `${scene.id}:
|
|
43
|
+
.setCharacteristic(this.platform.Characteristic.Model, "Sonos Scene Volume Dimmer")
|
|
44
|
+
.setCharacteristic(this.platform.Characteristic.SerialNumber, `${scene.id}:volume`);
|
|
39
45
|
}
|
|
40
46
|
updateScene(scene) {
|
|
41
47
|
this.scene = scene;
|
|
42
|
-
|
|
48
|
+
const configuredVolume = scene.coordinatorVolume;
|
|
49
|
+
if (configuredVolume !== undefined) {
|
|
50
|
+
this.lastKnownVolume = configuredVolume;
|
|
51
|
+
if (configuredVolume > 0) {
|
|
52
|
+
this.lastKnownActiveVolume = configuredVolume;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
43
55
|
this.accessory.context.sceneId = scene.id;
|
|
44
|
-
this.accessory.context.kind = "
|
|
56
|
+
this.accessory.context.kind = "volume";
|
|
45
57
|
this.accessory.displayName = this.displayNameFor(scene);
|
|
46
58
|
this.service.setCharacteristic(this.platform.Characteristic.Name, this.displayNameFor(scene));
|
|
47
59
|
}
|
|
48
60
|
displayNameFor(scene) {
|
|
49
61
|
return `${scene.name} Volume`;
|
|
50
62
|
}
|
|
51
|
-
async
|
|
63
|
+
async handleBrightnessGet() {
|
|
52
64
|
try {
|
|
53
65
|
this.lastKnownVolume = await this.platform.getSceneVolume(this.scene.id);
|
|
66
|
+
if (this.lastKnownVolume > 0) {
|
|
67
|
+
this.lastKnownActiveVolume = this.lastKnownVolume;
|
|
68
|
+
}
|
|
54
69
|
}
|
|
55
70
|
catch {
|
|
56
71
|
void 0;
|
|
57
72
|
}
|
|
58
73
|
return this.lastKnownVolume;
|
|
59
74
|
}
|
|
60
|
-
async
|
|
75
|
+
async handleBrightnessSet(value) {
|
|
61
76
|
const nextVolume = Math.max(0, Math.min(100, Math.round(Number(value))));
|
|
62
77
|
await this.platform.setSceneVolume(this.scene.id, nextVolume);
|
|
63
78
|
this.lastKnownVolume = nextVolume;
|
|
64
|
-
this.service.updateCharacteristic(this.platform.Characteristic.
|
|
79
|
+
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, nextVolume);
|
|
80
|
+
if (nextVolume > 0) {
|
|
81
|
+
this.lastKnownActiveVolume = nextVolume;
|
|
82
|
+
if (this.lastKnownMuted) {
|
|
83
|
+
await this.platform.setSceneMuted(this.scene.id, false);
|
|
84
|
+
this.lastKnownMuted = false;
|
|
85
|
+
}
|
|
86
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, true);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
await this.platform.setSceneMuted(this.scene.id, true);
|
|
90
|
+
this.lastKnownMuted = true;
|
|
91
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, false);
|
|
65
92
|
}
|
|
66
|
-
async
|
|
93
|
+
async handleOnGet() {
|
|
67
94
|
try {
|
|
68
95
|
this.lastKnownMuted = await this.platform.getSceneMuted(this.scene.id);
|
|
96
|
+
this.lastKnownVolume = await this.platform.getSceneVolume(this.scene.id);
|
|
97
|
+
if (this.lastKnownVolume > 0) {
|
|
98
|
+
this.lastKnownActiveVolume = this.lastKnownVolume;
|
|
99
|
+
}
|
|
69
100
|
}
|
|
70
101
|
catch {
|
|
71
102
|
void 0;
|
|
72
103
|
}
|
|
73
|
-
return this.lastKnownMuted;
|
|
104
|
+
return !this.lastKnownMuted && this.lastKnownVolume > 0;
|
|
74
105
|
}
|
|
75
|
-
async
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
106
|
+
async handleOnSet(value) {
|
|
107
|
+
const nextOn = value === true;
|
|
108
|
+
if (nextOn) {
|
|
109
|
+
const restoredVolume = this.lastKnownVolume > 0 ? this.lastKnownVolume : this.lastKnownActiveVolume;
|
|
110
|
+
if (this.lastKnownVolume <= 0 && restoredVolume > 0) {
|
|
111
|
+
await this.platform.setSceneVolume(this.scene.id, restoredVolume);
|
|
112
|
+
this.lastKnownVolume = restoredVolume;
|
|
113
|
+
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, restoredVolume);
|
|
114
|
+
}
|
|
115
|
+
await this.platform.setSceneMuted(this.scene.id, false);
|
|
116
|
+
this.lastKnownMuted = false;
|
|
117
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, true);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (this.lastKnownVolume > 0) {
|
|
121
|
+
this.lastKnownActiveVolume = this.lastKnownVolume;
|
|
122
|
+
}
|
|
123
|
+
await this.platform.setSceneMuted(this.scene.id, true);
|
|
124
|
+
this.lastKnownMuted = true;
|
|
125
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, false);
|
|
80
126
|
}
|
|
81
127
|
}
|
|
82
128
|
exports.SceneSpeakerAccessory = SceneSpeakerAccessory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneSpeaker.js","sourceRoot":"","sources":["../../../src/accessories/sceneSpeaker.ts"],"names":[],"mappings":";;;AAIA,MAAa,qBAAqB;
|
|
1
|
+
{"version":3,"file":"sceneSpeaker.js","sourceRoot":"","sources":["../../../src/accessories/sceneSpeaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -619,7 +619,7 @@
|
|
|
619
619
|
<div class="card-header scene-section-header">
|
|
620
620
|
<div class="scene-section-header-copy">
|
|
621
621
|
<strong>Scenes</strong>
|
|
622
|
-
<div class="scene-help">Each saved scene becomes a HomeKit switch plus a companion volume
|
|
622
|
+
<div class="scene-help">Each saved scene becomes a HomeKit switch plus a companion volume control.</div>
|
|
623
623
|
<div class="scene-inline-note mt-2">Save scene changes in the editor, then use Homebridge's footer Save button when you're ready to write everything to `config.json`.</div>
|
|
624
624
|
</div>
|
|
625
625
|
<div class="scene-section-actions">
|
|
@@ -651,7 +651,7 @@
|
|
|
651
651
|
<span>Scene Name</span>
|
|
652
652
|
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Scene Name">
|
|
653
653
|
?
|
|
654
|
-
<span class="scene-tooltip">The friendly name shown for this scene in HomeKit. The companion volume
|
|
654
|
+
<span class="scene-tooltip">The friendly name shown for this scene in HomeKit. The companion volume control uses the same name with "Volume" appended.</span>
|
|
655
655
|
</span>
|
|
656
656
|
</span>
|
|
657
657
|
</label>
|
|
@@ -706,19 +706,7 @@
|
|
|
706
706
|
</label>
|
|
707
707
|
<select class="form-select" id="source-target"></select>
|
|
708
708
|
</div>
|
|
709
|
-
<div class="col-md-
|
|
710
|
-
<label class="form-label" for="coordinator-volume">
|
|
711
|
-
<span class="scene-label-row">
|
|
712
|
-
<span>Lead Room Volume</span>
|
|
713
|
-
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Coordinator Volume">
|
|
714
|
-
?
|
|
715
|
-
<span class="scene-tooltip">Optional starting volume for the scene's lead room. Leave this blank to keep whatever volume that room already has.</span>
|
|
716
|
-
</span>
|
|
717
|
-
</span>
|
|
718
|
-
</label>
|
|
719
|
-
<input class="form-control" id="coordinator-volume" type="number" min="0" max="100">
|
|
720
|
-
</div>
|
|
721
|
-
<div class="col-md-3">
|
|
709
|
+
<div class="col-md-4">
|
|
722
710
|
<label class="form-label" for="settle-ms">
|
|
723
711
|
<span class="scene-label-row">
|
|
724
712
|
<span>Settle Delay (ms)</span>
|
|
@@ -730,7 +718,7 @@
|
|
|
730
718
|
</label>
|
|
731
719
|
<input class="form-control" id="settle-ms" type="number" min="0">
|
|
732
720
|
</div>
|
|
733
|
-
<div class="col-md-
|
|
721
|
+
<div class="col-md-4">
|
|
734
722
|
<label class="form-label" for="retry-count">
|
|
735
723
|
<span class="scene-label-row">
|
|
736
724
|
<span>Retry Count</span>
|
|
@@ -742,7 +730,7 @@
|
|
|
742
730
|
</label>
|
|
743
731
|
<input class="form-control" id="retry-count" type="number" min="0">
|
|
744
732
|
</div>
|
|
745
|
-
<div class="col-md-
|
|
733
|
+
<div class="col-md-4">
|
|
746
734
|
<label class="form-label" for="retry-delay-ms">
|
|
747
735
|
<span class="scene-label-row">
|
|
748
736
|
<span>Retry Delay (ms)</span>
|
|
@@ -754,7 +742,7 @@
|
|
|
754
742
|
</label>
|
|
755
743
|
<input class="form-control" id="retry-delay-ms" type="number" min="0">
|
|
756
744
|
</div>
|
|
757
|
-
<div class="col-md-
|
|
745
|
+
<div class="col-md-6">
|
|
758
746
|
<label class="form-label" for="auto-reset-ms">
|
|
759
747
|
<span class="scene-label-row">
|
|
760
748
|
<span>Auto Reset (ms)</span>
|
|
@@ -766,7 +754,7 @@
|
|
|
766
754
|
</label>
|
|
767
755
|
<input class="form-control" id="auto-reset-ms" type="number" min="0">
|
|
768
756
|
</div>
|
|
769
|
-
<div class="col-md-
|
|
757
|
+
<div class="col-md-6">
|
|
770
758
|
<label class="form-label" for="off-behavior">
|
|
771
759
|
<span class="scene-label-row">
|
|
772
760
|
<span>Off Behavior</span>
|
|
@@ -829,7 +817,6 @@
|
|
|
829
817
|
memberList: document.getElementById("member-list"),
|
|
830
818
|
sourceKind: document.getElementById("source-kind"),
|
|
831
819
|
sourceTarget: document.getElementById("source-target"),
|
|
832
|
-
coordinatorVolume: document.getElementById("coordinator-volume"),
|
|
833
820
|
settleMs: document.getElementById("settle-ms"),
|
|
834
821
|
retryCount: document.getElementById("retry-count"),
|
|
835
822
|
retryDelayMs: document.getElementById("retry-delay-ms"),
|
|
@@ -1009,7 +996,8 @@
|
|
|
1009
996
|
selectedRoomIds.unshift(draft.source.deviceId);
|
|
1010
997
|
}
|
|
1011
998
|
draft.memberPlayerIds = selectedRoomIds.filter((playerId) => playerId !== draft.coordinatorPlayerId);
|
|
1012
|
-
|
|
999
|
+
const leadVolumeInput = elements.memberList.querySelector("input[data-coordinator-volume]");
|
|
1000
|
+
draft.coordinatorVolume = leadVolumeInput && leadVolumeInput.value !== "" ? Number(leadVolumeInput.value) : undefined;
|
|
1013
1001
|
draft.settleMs = Number(elements.settleMs.value || 0);
|
|
1014
1002
|
draft.retryCount = Number(elements.retryCount.value || 0);
|
|
1015
1003
|
draft.retryDelayMs = Number(elements.retryDelayMs.value || 0);
|
|
@@ -1204,6 +1192,20 @@
|
|
|
1204
1192
|
${flags ? `<span class="scene-member-flags">${flags}</span>` : ""}
|
|
1205
1193
|
</span>
|
|
1206
1194
|
</label>
|
|
1195
|
+
${isSelected && coordinatorId === player.id ? `
|
|
1196
|
+
<div class="scene-member-volume">
|
|
1197
|
+
<label class="scene-member-volume-label" for="lead-volume-${player.id}">Lead Room Volume</label>
|
|
1198
|
+
<input
|
|
1199
|
+
class="form-control form-control-sm scene-member-volume-input"
|
|
1200
|
+
id="lead-volume-${player.id}"
|
|
1201
|
+
data-coordinator-volume="true"
|
|
1202
|
+
type="number"
|
|
1203
|
+
min="0"
|
|
1204
|
+
max="100"
|
|
1205
|
+
placeholder="Keep current"
|
|
1206
|
+
value="${state.draft.coordinatorVolume ?? ""}">
|
|
1207
|
+
</div>
|
|
1208
|
+
` : ""}
|
|
1207
1209
|
${isSelected && coordinatorId !== player.id ? `
|
|
1208
1210
|
<div class="scene-member-volume">
|
|
1209
1211
|
<label class="scene-member-volume-label" for="member-volume-${player.id}">Volume Override</label>
|
|
@@ -1397,7 +1399,6 @@
|
|
|
1397
1399
|
renderHouseholdOptions();
|
|
1398
1400
|
renderSourceControls();
|
|
1399
1401
|
renderMemberOptions();
|
|
1400
|
-
elements.coordinatorVolume.value = state.draft.coordinatorVolume === "" ? "" : state.draft.coordinatorVolume ?? "";
|
|
1401
1402
|
elements.settleMs.value = state.draft.settleMs ?? 750;
|
|
1402
1403
|
elements.retryCount.value = state.draft.retryCount ?? 3;
|
|
1403
1404
|
elements.retryDelayMs.value = state.draft.retryDelayMs ?? 750;
|
package/package.json
CHANGED