homebridge-sonos-scenes 0.1.6 → 0.1.8
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 +4 -1
- package/docs/assets/icon-1024.png +0 -0
- package/homebridge-ui/public/index.html +161 -85
- package/package.json +1 -1
- /package/docs/assets/{homebridge-sonos-scenes-icon-256x256.png → icon-256.png} +0 -0
- /package/docs/assets/{homebridge-sonos-scenes-icon-512x512.png → icon-512.png} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="docs/assets/
|
|
2
|
+
<img src="docs/assets/icon-512.png" alt="homebridge-sonos-scenes icon" width="164">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">homebridge-sonos-scenes</h1>
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
`homebridge-sonos-scenes` is a Homebridge plugin scaffold for Sonos workflow scenes.
|
|
10
10
|
|
|
11
|
+
> [!IMPORTANT]
|
|
12
|
+
> This project is in an active early-testing phase. The local-first scene workflow is usable and published to npm, but cloud-backed Sonos playback is still planned work and some edge cases are still being hardened. If you try the plugin, please share bugs, UI feedback, and Sonos compatibility notes in [GitHub Issues](https://github.com/applemanj/homebridge-sonos-scenes/issues). Real-world feedback is especially helpful right now.
|
|
13
|
+
|
|
11
14
|
The goal is not general Sonos control. The goal is a clean way to trigger multi-step Sonos workflows from Apple Home, such as:
|
|
12
15
|
|
|
13
16
|
- grouping rooms around a coordinator,
|
|
Binary file
|
|
@@ -307,12 +307,22 @@
|
|
|
307
307
|
display: block;
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
.scene-member-pill.selected {
|
|
311
|
+
border-color: rgba(10, 132, 255, 0.34);
|
|
312
|
+
background: rgba(10, 132, 255, 0.07);
|
|
313
|
+
box-shadow: inset 0 0 0 1px rgba(10, 132, 255, 0.08);
|
|
314
|
+
}
|
|
315
|
+
|
|
310
316
|
.scene-member-row {
|
|
311
317
|
display: flex;
|
|
312
318
|
align-items: flex-start;
|
|
313
319
|
gap: 0.65rem;
|
|
314
320
|
}
|
|
315
321
|
|
|
322
|
+
.scene-member-toggle {
|
|
323
|
+
cursor: pointer;
|
|
324
|
+
}
|
|
325
|
+
|
|
316
326
|
.scene-member-pill .form-check-input {
|
|
317
327
|
margin-top: 0.2rem;
|
|
318
328
|
flex: 0 0 auto;
|
|
@@ -336,6 +346,52 @@
|
|
|
336
346
|
line-height: 1.35;
|
|
337
347
|
}
|
|
338
348
|
|
|
349
|
+
.scene-member-volume {
|
|
350
|
+
display: grid;
|
|
351
|
+
gap: 0.35rem;
|
|
352
|
+
margin-top: 0.65rem;
|
|
353
|
+
padding-top: 0.65rem;
|
|
354
|
+
border-top: 1px solid rgba(22, 32, 51, 0.08);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.scene-member-volume-label {
|
|
358
|
+
color: #556579;
|
|
359
|
+
font-size: 0.78rem;
|
|
360
|
+
font-weight: 600;
|
|
361
|
+
line-height: 1.3;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.scene-member-volume-input {
|
|
365
|
+
max-width: 132px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.scene-member-flags {
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-wrap: wrap;
|
|
371
|
+
gap: 0.35rem;
|
|
372
|
+
margin-top: 0.2rem;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.scene-member-flag {
|
|
376
|
+
display: inline-flex;
|
|
377
|
+
align-items: center;
|
|
378
|
+
padding: 0.15rem 0.45rem;
|
|
379
|
+
border-radius: 999px;
|
|
380
|
+
font-size: 0.72rem;
|
|
381
|
+
font-weight: 600;
|
|
382
|
+
line-height: 1.2;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.scene-member-flag.source {
|
|
386
|
+
background: rgba(10, 132, 255, 0.12);
|
|
387
|
+
color: #0a84ff;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.scene-member-flag.primary {
|
|
391
|
+
background: rgba(168, 85, 247, 0.16);
|
|
392
|
+
color: #7c3aed;
|
|
393
|
+
}
|
|
394
|
+
|
|
339
395
|
.scene-log {
|
|
340
396
|
max-height: 280px;
|
|
341
397
|
overflow: auto;
|
|
@@ -602,7 +658,7 @@
|
|
|
602
658
|
<input class="form-control" id="scene-name" type="text">
|
|
603
659
|
</div>
|
|
604
660
|
<input id="scene-id" type="hidden">
|
|
605
|
-
<div class="col-
|
|
661
|
+
<div class="col-12">
|
|
606
662
|
<label class="form-label" for="household-select">
|
|
607
663
|
<span class="scene-label-row">
|
|
608
664
|
<span>Household</span>
|
|
@@ -614,25 +670,13 @@
|
|
|
614
670
|
</label>
|
|
615
671
|
<select class="form-select" id="household-select"></select>
|
|
616
672
|
</div>
|
|
617
|
-
<div class="col-md-6">
|
|
618
|
-
<label class="form-label" for="coordinator-select">
|
|
619
|
-
<span class="scene-label-row">
|
|
620
|
-
<span>Coordinator Room</span>
|
|
621
|
-
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Coordinator Room">
|
|
622
|
-
?
|
|
623
|
-
<span class="scene-tooltip">The main room that anchors the group and receives the source change first. Choose the room that should lead the scene.</span>
|
|
624
|
-
</span>
|
|
625
|
-
</span>
|
|
626
|
-
</label>
|
|
627
|
-
<select class="form-select" id="coordinator-select"></select>
|
|
628
|
-
</div>
|
|
629
673
|
<div class="col-12">
|
|
630
674
|
<label class="form-label">
|
|
631
675
|
<span class="scene-label-row">
|
|
632
|
-
<span>
|
|
676
|
+
<span>Scene Rooms</span>
|
|
633
677
|
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Group Members">
|
|
634
678
|
?
|
|
635
|
-
<span class="scene-tooltip">
|
|
679
|
+
<span class="scene-tooltip">Pick the rooms that should be part of this scene. For line-in and TV scenes, the source room is included automatically. One selected room becomes the internal lead room behind the scenes.</span>
|
|
636
680
|
</span>
|
|
637
681
|
</span>
|
|
638
682
|
</label>
|
|
@@ -644,7 +688,7 @@
|
|
|
644
688
|
<span>Source Kind</span>
|
|
645
689
|
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Source Kind">
|
|
646
690
|
?
|
|
647
|
-
<span class="scene-tooltip">The type of thing this scene should load. The list
|
|
691
|
+
<span class="scene-tooltip">The type of thing this scene should load. The list is based on what is available in this household, such as favorites, line-in, or TV.</span>
|
|
648
692
|
</span>
|
|
649
693
|
</span>
|
|
650
694
|
</label>
|
|
@@ -665,10 +709,10 @@
|
|
|
665
709
|
<div class="col-md-3">
|
|
666
710
|
<label class="form-label" for="coordinator-volume">
|
|
667
711
|
<span class="scene-label-row">
|
|
668
|
-
<span>
|
|
712
|
+
<span>Lead Room Volume</span>
|
|
669
713
|
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Coordinator Volume">
|
|
670
714
|
?
|
|
671
|
-
<span class="scene-tooltip">Optional starting volume for the
|
|
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>
|
|
672
716
|
</span>
|
|
673
717
|
</span>
|
|
674
718
|
</label>
|
|
@@ -737,18 +781,6 @@
|
|
|
737
781
|
<option value="ungroup">Ungroup</option>
|
|
738
782
|
</select>
|
|
739
783
|
</div>
|
|
740
|
-
<div class="col-12">
|
|
741
|
-
<label class="form-label">
|
|
742
|
-
<span class="scene-label-row">
|
|
743
|
-
<span>Per-room Volumes</span>
|
|
744
|
-
<span class="scene-info-tag" tabindex="0" role="button" aria-label="Explain Per-room Volumes">
|
|
745
|
-
?
|
|
746
|
-
<span class="scene-tooltip">Optional volume overrides for specific member rooms. Leave a room blank here to keep its current volume or let the group volume behavior apply.</span>
|
|
747
|
-
</span>
|
|
748
|
-
</span>
|
|
749
|
-
</label>
|
|
750
|
-
<div id="volume-list" class="scene-member-grid"></div>
|
|
751
|
-
</div>
|
|
752
784
|
</div>
|
|
753
785
|
</div>
|
|
754
786
|
</section>
|
|
@@ -794,7 +826,6 @@
|
|
|
794
826
|
sceneName: document.getElementById("scene-name"),
|
|
795
827
|
sceneId: document.getElementById("scene-id"),
|
|
796
828
|
householdSelect: document.getElementById("household-select"),
|
|
797
|
-
coordinatorSelect: document.getElementById("coordinator-select"),
|
|
798
829
|
memberList: document.getElementById("member-list"),
|
|
799
830
|
sourceKind: document.getElementById("source-kind"),
|
|
800
831
|
sourceTarget: document.getElementById("source-target"),
|
|
@@ -804,7 +835,6 @@
|
|
|
804
835
|
retryDelayMs: document.getElementById("retry-delay-ms"),
|
|
805
836
|
autoResetMs: document.getElementById("auto-reset-ms"),
|
|
806
837
|
offBehavior: document.getElementById("off-behavior"),
|
|
807
|
-
volumeList: document.getElementById("volume-list"),
|
|
808
838
|
validationOutput: document.getElementById("validation-output"),
|
|
809
839
|
logOutput: document.getElementById("log-output"),
|
|
810
840
|
discoverySummary: document.getElementById("discovery-summary"),
|
|
@@ -942,22 +972,52 @@
|
|
|
942
972
|
return (household?.favorites || []).filter((favorite) => favorite.playable !== false);
|
|
943
973
|
}
|
|
944
974
|
|
|
975
|
+
function getSelectedSceneRoomIdsFromDraft() {
|
|
976
|
+
const selected = Array.from(new Set([state.draft.coordinatorPlayerId, ...(state.draft.memberPlayerIds || [])].filter(Boolean)));
|
|
977
|
+
const source = state.draft.source;
|
|
978
|
+
if ((source?.kind === "line_in" || source?.kind === "tv") && source.deviceId) {
|
|
979
|
+
if (!selected.includes(source.deviceId)) {
|
|
980
|
+
selected.unshift(source.deviceId);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
return selected;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function resolveCoordinatorPlayerId(selectedRoomIds, source) {
|
|
987
|
+
const uniqueRooms = Array.from(new Set((selectedRoomIds || []).filter(Boolean)));
|
|
988
|
+
|
|
989
|
+
if ((source?.kind === "line_in" || source?.kind === "tv") && source.deviceId) {
|
|
990
|
+
return source.deviceId;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (state.draft?.coordinatorPlayerId && uniqueRooms.includes(state.draft.coordinatorPlayerId)) {
|
|
994
|
+
return state.draft.coordinatorPlayerId;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
return uniqueRooms[0] || "";
|
|
998
|
+
}
|
|
999
|
+
|
|
945
1000
|
function serializeDraft() {
|
|
946
1001
|
serializeCloudConfig();
|
|
947
1002
|
const draft = clone(state.draft);
|
|
948
1003
|
draft.name = elements.sceneName.value.trim() || "New Scene";
|
|
949
1004
|
draft.householdId = elements.householdSelect.value;
|
|
950
|
-
draft.coordinatorPlayerId = elements.coordinatorSelect.value;
|
|
951
|
-
draft.memberPlayerIds = Array.from(elements.memberList.querySelectorAll("input[type='checkbox']:checked")).map((checkbox) => checkbox.value);
|
|
952
1005
|
draft.source = buildSourcePayload();
|
|
1006
|
+
const selectedRoomIds = Array.from(elements.memberList.querySelectorAll("input[type='checkbox']:checked")).map((checkbox) => checkbox.value);
|
|
1007
|
+
draft.coordinatorPlayerId = resolveCoordinatorPlayerId(selectedRoomIds, draft.source);
|
|
1008
|
+
if ((draft.source?.kind === "line_in" || draft.source?.kind === "tv") && draft.source.deviceId && !selectedRoomIds.includes(draft.source.deviceId)) {
|
|
1009
|
+
selectedRoomIds.unshift(draft.source.deviceId);
|
|
1010
|
+
}
|
|
1011
|
+
draft.memberPlayerIds = selectedRoomIds.filter((playerId) => playerId !== draft.coordinatorPlayerId);
|
|
953
1012
|
draft.coordinatorVolume = elements.coordinatorVolume.value === "" ? undefined : Number(elements.coordinatorVolume.value);
|
|
954
1013
|
draft.settleMs = Number(elements.settleMs.value || 0);
|
|
955
1014
|
draft.retryCount = Number(elements.retryCount.value || 0);
|
|
956
1015
|
draft.retryDelayMs = Number(elements.retryDelayMs.value || 0);
|
|
957
1016
|
draft.autoResetMs = Number(elements.autoResetMs.value || 0);
|
|
958
1017
|
draft.offBehavior = { kind: elements.offBehavior.value };
|
|
959
|
-
draft.playerVolumes = Array.from(elements.
|
|
1018
|
+
draft.playerVolumes = Array.from(elements.memberList.querySelectorAll("input[data-player-id]"))
|
|
960
1019
|
.filter((input) => input.value !== "")
|
|
1020
|
+
.filter((input) => selectedRoomIds.includes(input.dataset.playerId) && input.dataset.playerId !== draft.coordinatorPlayerId)
|
|
961
1021
|
.map((input) => ({
|
|
962
1022
|
playerId: input.dataset.playerId,
|
|
963
1023
|
volume: Number(input.value),
|
|
@@ -1111,46 +1171,64 @@
|
|
|
1111
1171
|
.join("");
|
|
1112
1172
|
}
|
|
1113
1173
|
|
|
1114
|
-
function renderCoordinatorOptions() {
|
|
1115
|
-
const household = getActiveHousehold();
|
|
1116
|
-
const players = household?.players || [];
|
|
1117
|
-
const currentCoordinator = players.some((player) => player.id === state.draft.coordinatorPlayerId)
|
|
1118
|
-
? state.draft.coordinatorPlayerId
|
|
1119
|
-
: players[0]?.id || "";
|
|
1120
|
-
state.draft.coordinatorPlayerId = currentCoordinator;
|
|
1121
|
-
elements.coordinatorSelect.innerHTML = players
|
|
1122
|
-
.map((player) => `<option value="${player.id}" ${player.id === currentCoordinator ? "selected" : ""}>${player.name}</option>`)
|
|
1123
|
-
.join("");
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
1174
|
function renderMemberOptions() {
|
|
1127
1175
|
const household = getActiveHousehold();
|
|
1128
|
-
const
|
|
1129
|
-
const selected = new Set(
|
|
1130
|
-
const
|
|
1176
|
+
const selectedRoomIds = getSelectedSceneRoomIdsFromDraft();
|
|
1177
|
+
const selected = new Set(selectedRoomIds);
|
|
1178
|
+
const coordinatorId = resolveCoordinatorPlayerId(selectedRoomIds, state.draft.source);
|
|
1179
|
+
state.draft.coordinatorPlayerId = coordinatorId;
|
|
1180
|
+
state.draft.memberPlayerIds = selectedRoomIds.filter((playerId) => playerId !== coordinatorId);
|
|
1181
|
+
const values = new Map((state.draft.playerVolumes || []).map((entry) => [entry.playerId, entry.volume]));
|
|
1182
|
+
const forcedSourceId = (state.draft.source?.kind === "line_in" || state.draft.source?.kind === "tv")
|
|
1183
|
+
? state.draft.source.deviceId
|
|
1184
|
+
: "";
|
|
1185
|
+
const players = household?.players || [];
|
|
1131
1186
|
elements.memberList.innerHTML = players.length === 0
|
|
1132
|
-
? `<div class="scene-help">No
|
|
1187
|
+
? `<div class="scene-help">No rooms are available in this household.</div>`
|
|
1133
1188
|
: players
|
|
1134
1189
|
.map(
|
|
1135
|
-
(player) =>
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1190
|
+
(player) => {
|
|
1191
|
+
const isForcedSource = forcedSourceId === player.id;
|
|
1192
|
+
const isSelected = isForcedSource || selected.has(player.id);
|
|
1193
|
+
const flags = [
|
|
1194
|
+
isForcedSource ? `<span class="scene-member-flag source">Source Room</span>` : "",
|
|
1195
|
+
isSelected && coordinatorId === player.id ? `<span class="scene-member-flag primary">Lead Room</span>` : "",
|
|
1196
|
+
].filter(Boolean).join("");
|
|
1197
|
+
return `
|
|
1198
|
+
<div class="scene-member-pill ${isSelected ? "selected" : ""}">
|
|
1199
|
+
<label class="scene-member-row scene-member-toggle">
|
|
1200
|
+
<input class="form-check-input" data-member-checkbox type="checkbox" value="${player.id}" ${isSelected ? "checked" : ""} ${isForcedSource ? "disabled" : ""}>
|
|
1139
1201
|
<span class="scene-member-copy">
|
|
1140
1202
|
<span class="scene-member-title">${player.name}</span>
|
|
1141
1203
|
<span class="scene-member-meta">${player.model || "Unknown model"} - ${((player.sourceOptions || []).join(", ") || "favorite").replaceAll("_", " ")}</span>
|
|
1204
|
+
${flags ? `<span class="scene-member-flags">${flags}</span>` : ""}
|
|
1142
1205
|
</span>
|
|
1143
|
-
</
|
|
1144
|
-
|
|
1145
|
-
|
|
1206
|
+
</label>
|
|
1207
|
+
${isSelected && coordinatorId !== player.id ? `
|
|
1208
|
+
<div class="scene-member-volume">
|
|
1209
|
+
<label class="scene-member-volume-label" for="member-volume-${player.id}">Volume Override</label>
|
|
1210
|
+
<input
|
|
1211
|
+
class="form-control form-control-sm scene-member-volume-input"
|
|
1212
|
+
id="member-volume-${player.id}"
|
|
1213
|
+
data-player-id="${player.id}"
|
|
1214
|
+
type="number"
|
|
1215
|
+
min="0"
|
|
1216
|
+
max="100"
|
|
1217
|
+
placeholder="Keep current"
|
|
1218
|
+
value="${values.get(player.id) ?? ""}">
|
|
1219
|
+
</div>
|
|
1220
|
+
` : ""}
|
|
1221
|
+
</div>
|
|
1222
|
+
`;
|
|
1223
|
+
},
|
|
1146
1224
|
)
|
|
1147
1225
|
.join("");
|
|
1148
1226
|
}
|
|
1149
1227
|
|
|
1150
1228
|
function renderSourceControls() {
|
|
1151
1229
|
const household = getActiveHousehold();
|
|
1152
|
-
const
|
|
1153
|
-
const supportedKinds = ["favorite", ...new Set((
|
|
1230
|
+
const players = household?.players || [];
|
|
1231
|
+
const supportedKinds = ["favorite", ...new Set(players.flatMap((player) => player.sourceOptions || []).filter((kind) => kind !== "favorite"))];
|
|
1154
1232
|
const sourceKind = supportedKinds.includes(state.draft.source?.kind) ? state.draft.source.kind : supportedKinds[0] || "favorite";
|
|
1155
1233
|
state.draft.source = state.draft.source || { kind: sourceKind, favoriteId: "" };
|
|
1156
1234
|
state.draft.source.kind = sourceKind;
|
|
@@ -1201,22 +1279,6 @@
|
|
|
1201
1279
|
.join("");
|
|
1202
1280
|
}
|
|
1203
1281
|
|
|
1204
|
-
function renderVolumeControls() {
|
|
1205
|
-
const household = getActiveHousehold();
|
|
1206
|
-
const selectedMembers = state.draft.memberPlayerIds || [];
|
|
1207
|
-
const values = new Map((state.draft.playerVolumes || []).map((entry) => [entry.playerId, entry.volume]));
|
|
1208
|
-
elements.volumeList.innerHTML = selectedMembers.length === 0
|
|
1209
|
-
? `<div class="scene-help">Select one or more member rooms to configure per-room volume overrides.</div>`
|
|
1210
|
-
: selectedMembers
|
|
1211
|
-
.map((playerId) => `
|
|
1212
|
-
<label class="scene-member-pill">
|
|
1213
|
-
<span class="scene-member-title d-block mb-1">${getPlayerName(playerId)}</span>
|
|
1214
|
-
<input class="form-control" data-player-id="${playerId}" type="number" min="0" max="100" value="${values.get(playerId) ?? ""}">
|
|
1215
|
-
</label>
|
|
1216
|
-
`)
|
|
1217
|
-
.join("");
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
1282
|
function renderValidation() {
|
|
1221
1283
|
if (!state.validation && !state.lastRun) {
|
|
1222
1284
|
elements.validationOutput.innerHTML = `<div class="scene-help">Validation messages and test results will appear here.</div>`;
|
|
@@ -1333,16 +1395,14 @@
|
|
|
1333
1395
|
elements.sceneName.value = state.draft.name || "";
|
|
1334
1396
|
elements.sceneId.value = state.draft.id || "";
|
|
1335
1397
|
renderHouseholdOptions();
|
|
1336
|
-
renderCoordinatorOptions();
|
|
1337
|
-
renderMemberOptions();
|
|
1338
1398
|
renderSourceControls();
|
|
1399
|
+
renderMemberOptions();
|
|
1339
1400
|
elements.coordinatorVolume.value = state.draft.coordinatorVolume === "" ? "" : state.draft.coordinatorVolume ?? "";
|
|
1340
1401
|
elements.settleMs.value = state.draft.settleMs ?? 750;
|
|
1341
1402
|
elements.retryCount.value = state.draft.retryCount ?? 3;
|
|
1342
1403
|
elements.retryDelayMs.value = state.draft.retryDelayMs ?? 750;
|
|
1343
1404
|
elements.autoResetMs.value = state.draft.autoResetMs ?? 1000;
|
|
1344
1405
|
elements.offBehavior.value = state.draft.offBehavior?.kind || "none";
|
|
1345
|
-
renderVolumeControls();
|
|
1346
1406
|
}
|
|
1347
1407
|
|
|
1348
1408
|
function render() {
|
|
@@ -1510,6 +1570,23 @@
|
|
|
1510
1570
|
});
|
|
1511
1571
|
});
|
|
1512
1572
|
|
|
1573
|
+
elements.memberList.addEventListener("change", (event) => {
|
|
1574
|
+
const target = event.target;
|
|
1575
|
+
if (!(target instanceof HTMLInputElement)) {
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
if (target.matches("input[data-member-checkbox]")) {
|
|
1580
|
+
serializeDraft();
|
|
1581
|
+
renderMemberOptions();
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
if (target.matches("input[data-player-id]")) {
|
|
1586
|
+
serializeDraft();
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1513
1590
|
elements.householdSelect.addEventListener("change", () => {
|
|
1514
1591
|
serializeDraft();
|
|
1515
1592
|
state.draft.coordinatorPlayerId = "";
|
|
@@ -1518,12 +1595,6 @@
|
|
|
1518
1595
|
render();
|
|
1519
1596
|
});
|
|
1520
1597
|
|
|
1521
|
-
elements.coordinatorSelect.addEventListener("change", () => {
|
|
1522
|
-
serializeDraft();
|
|
1523
|
-
state.draft.memberPlayerIds = state.draft.memberPlayerIds.filter((playerId) => playerId !== state.draft.coordinatorPlayerId);
|
|
1524
|
-
render();
|
|
1525
|
-
});
|
|
1526
|
-
|
|
1527
1598
|
elements.sourceKind.addEventListener("change", () => {
|
|
1528
1599
|
serializeDraft();
|
|
1529
1600
|
state.draft.source = elements.sourceKind.value === "favorite"
|
|
@@ -1532,6 +1603,11 @@
|
|
|
1532
1603
|
render();
|
|
1533
1604
|
});
|
|
1534
1605
|
|
|
1606
|
+
elements.sourceTarget.addEventListener("change", () => {
|
|
1607
|
+
serializeDraft();
|
|
1608
|
+
render();
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1535
1611
|
homebridge.addEventListener("scene-test-result", (event) => {
|
|
1536
1612
|
state.lastRun = event.data;
|
|
1537
1613
|
render();
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|