play/pause
Deploy HEOS panel / deploy (push) Successful in 24s

This commit is contained in:
2026-09-15 01:03:25 +02:00
parent d2d7677519
commit dc7f09052d
9 changed files with 160 additions and 8 deletions
+34 -2
View File
@@ -60,13 +60,16 @@ els('.room').forEach((node) => {
bar: el('[data-role="bar"]', node),
toggle: el('[data-role="group"]', node),
toggleLabel: el('[data-role="group-label"]', node),
play: el('[data-role="play"]', node),
steps: els('.step', node),
volume: null,
playState: null,
grouped: false,
available: false,
taps: 0, // button presses not yet sent
inflight: false,
busy: false, // a grouping change is in flight
playBusy: false,
};
rooms[key] = room;
@@ -76,14 +79,21 @@ els('.room').forEach((node) => {
});
room.toggle.addEventListener('click', () => setGrouped(key, !room.grouped));
room.play.addEventListener('click', () => togglePlay(key));
});
function paintRoom(room) {
const known = room.volume !== null && room.volume !== undefined;
room.level.textContent = known ? room.volume : '—';
room.level.textContent = known ? room.volume + '%' : '—';
room.bar.style.width = `${known ? room.volume : 0}%`;
room.node.classList.toggle('offline', !room.available);
room.steps.forEach((button) => { button.disabled = !room.available; });
const playing = room.playState === 'play';
room.play.classList.toggle('playing', playing);
room.play.disabled = !room.available || room.playState === null;
room.play.setAttribute(
'aria-label', `${room.node.querySelector('h2').textContent}: ${playing ? 'pause' : 'play'}`);
room.toggle.disabled = !room.available;
room.toggle.classList.toggle('busy', room.busy);
room.toggle.setAttribute('aria-pressed', String(room.grouped));
@@ -195,6 +205,26 @@ async function setGrouped(key, joined) {
}
}
/* Sends the state it wants rather than "toggle", so a stale idea of what
the room is doing cannot flip it the wrong way. */
async function togglePlay(key) {
const room = rooms[key];
if (!room.available || room.playBusy) return;
const wanted = room.playState === 'play' ? 'pause' : 'play';
room.playBusy = true;
room.playState = wanted;
paintRoom(room);
try {
const data = await api('/api/playback', { target: key, state: wanted });
room.playState = data.state;
} catch (error) {
toast(error.message);
} finally {
room.playBusy = false;
paintRoom(room);
}
}
function applyJoined(joined) {
Object.values(rooms).forEach((room) => {
room.grouped = joined.includes(room.key);
@@ -266,6 +296,7 @@ function render(state) {
room.grouped = incoming.grouped;
// Do not stomp on a volume the user is in the middle of changing.
if (!room.taps && !room.inflight) room.volume = incoming.volume;
if (!room.playBusy) room.playState = incoming.play_state;
paintRoom(room);
});
paintGrouping();
@@ -297,7 +328,8 @@ async function refresh() {
}
function busy() {
return sheetOpen() || Object.values(rooms).some((r) => r.taps || r.inflight || r.busy);
return sheetOpen()
|| Object.values(rooms).some((r) => r.taps || r.inflight || r.busy || r.playBusy);
}
ui.refresh.addEventListener('click', () => {