Skip to content

Commit a76f53a

Browse files
clubandersonclaude
andcommitted
Improve Sonos widget error handling and click-to-start
- Add offline detection with "Click to start" functionality - Clicking widget when offline opens the server launcher - Differentiate between offline, no-speakers, and starting states - Compact error messages with helpful hints - Auto-minimize Terminal when starting server Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
1 parent e35fd6d commit a76f53a

1 file changed

Lines changed: 81 additions & 16 deletions

File tree

examples/sonos-control.jsx

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@
44
// Prerequisites: node-sonos-http-api running on port 5005
55
// https://github.com/jishi/node-sonos-http-api
66

7-
import { css } from "uebersicht";
7+
import { css, run } from "uebersicht";
88

99
// ============ CONFIGURATION ============
1010
const SONOS_API_URL = "http://localhost:5005";
1111
// =======================================
1212

1313
export const command = `
14-
echo '{"zones":' && curl -s "${SONOS_API_URL}/zones" 2>/dev/null && \
15-
echo ',"favorites":' && curl -s "${SONOS_API_URL}/favorites/detailed" 2>/dev/null && \
16-
echo ',"playlists":' && curl -s "${SONOS_API_URL}/playlists" 2>/dev/null && \
17-
echo '}'
14+
zones=$(curl -s --connect-timeout 1 "${SONOS_API_URL}/zones" 2>/dev/null)
15+
curlrc=$?
16+
if [ $curlrc -ne 0 ]; then
17+
echo '{"error":"offline"}'
18+
elif [ -z "$zones" ] || [ "$zones" = "[]" ]; then
19+
echo '{"error":"no-speakers"}'
20+
else
21+
favs=$(curl -s --connect-timeout 2 "${SONOS_API_URL}/favorites/detailed" 2>/dev/null || echo '[]')
22+
lists=$(curl -s --connect-timeout 2 "${SONOS_API_URL}/playlists" 2>/dev/null || echo '[]')
23+
echo "{\\"zones\\":$zones,\\"favorites\\":$favs,\\"playlists\\":$lists}"
24+
fi
1825
`;
1926

2027
export const refreshFrequency = 3000;
@@ -1078,29 +1085,85 @@ function ZoneControl({ zone, allZones, favorites, expanded, onToggleExpand, onOp
10781085

10791086
let expandedZones = new Set();
10801087

1088+
const connectingStyle = {
1089+
background: "rgba(0, 0, 0, 0.80)",
1090+
backdropFilter: "blur(24px)",
1091+
WebkitBackdropFilter: "blur(24px)",
1092+
borderRadius: "16px",
1093+
padding: "16px 20px",
1094+
fontSize: "12px",
1095+
color: "rgba(255, 255, 255, 0.8)",
1096+
textAlign: "center",
1097+
position: "absolute",
1098+
top: "20px",
1099+
right: "20px",
1100+
pointerEvents: "auto",
1101+
maxWidth: "200px",
1102+
};
1103+
1104+
const startServer = () => {
1105+
run(`open ~/Desktop/Start\\ Sonos\\ Server.command`);
1106+
};
1107+
10811108
export const render = ({ output, error }) => {
1082-
if (error || !output) {
1109+
if (error || !output || output.trim() === "") {
10831110
return (
1084-
<div style={errorStyle}>
1085-
Cannot reach Sonos API<br/>
1086-
<span style={{fontSize: "10px", opacity: 0.7}}>
1087-
Ensure node-sonos-http-api is running on port 5005
1111+
<div
1112+
style={{...connectingStyle, cursor: "pointer"}}
1113+
onClick={startServer}
1114+
title="Click to start server"
1115+
>
1116+
🔊 Sonos Offline<br/>
1117+
<span style={{fontSize: "10px", opacity: 0.6, lineHeight: 1.4, display: "block", marginTop: "8px"}}>
1118+
Click to start
10881119
</span>
10891120
</div>
10901121
);
10911122
}
10921123

10931124
try {
1094-
const cleanOutput = output.replace(/\n/g, '');
1125+
const cleanOutput = output.replace(/\n/g, '').trim();
10951126
const data = JSON.parse(cleanOutput);
1127+
1128+
// Handle offline state - server not running
1129+
if (data.error === "offline") {
1130+
return (
1131+
<div
1132+
style={{...connectingStyle, cursor: "pointer"}}
1133+
onClick={startServer}
1134+
title="Click to start server"
1135+
>
1136+
🔊 Sonos Offline<br/>
1137+
<span style={{fontSize: "10px", opacity: 0.6, lineHeight: 1.4, display: "block", marginTop: "8px"}}>
1138+
Click to start
1139+
</span>
1140+
</div>
1141+
);
1142+
}
1143+
1144+
// Handle no speakers state
1145+
if (data.error === "no-speakers") {
1146+
return (
1147+
<div style={connectingStyle}>
1148+
🔊 Sonos<br/>
1149+
<span style={{fontSize: "10px", opacity: 0.6, lineHeight: 1.4, display: "block", marginTop: "6px"}}>
1150+
No speakers found
1151+
</span>
1152+
</div>
1153+
);
1154+
}
1155+
10961156
const zones = data.zones || [];
10971157
const favorites = data.favorites || [];
10981158
const playlists = data.playlists || [];
10991159

11001160
if (!zones || zones.length === 0) {
11011161
return (
1102-
<div style={errorStyle}>
1103-
No Sonos speakers found
1162+
<div style={connectingStyle}>
1163+
🔊 Sonos<br/>
1164+
<span style={{fontSize: "10px", opacity: 0.6, lineHeight: 1.4, display: "block", marginTop: "6px"}}>
1165+
No speakers found
1166+
</span>
11041167
</div>
11051168
);
11061169
}
@@ -1275,9 +1338,11 @@ export const render = ({ output, error }) => {
12751338
);
12761339
} catch (e) {
12771340
return (
1278-
<div style={errorStyle}>
1279-
Error: {e.message}<br/>
1280-
<span style={{fontSize: "9px", opacity: 0.5}}>{output?.substring(0, 100)}</span>
1341+
<div style={connectingStyle}>
1342+
🔊 Sonos<br/>
1343+
<span style={{fontSize: "10px", opacity: 0.6, lineHeight: 1.4, display: "block", marginTop: "6px"}}>
1344+
Starting up...
1345+
</span>
12811346
</div>
12821347
);
12831348
}

0 commit comments

Comments
 (0)