@lox-audio-server/sonos 0.1.0

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.
Files changed (60) hide show
  1. package/README.md +57 -0
  2. package/dist/esm/index.js +27 -0
  3. package/dist/esm/package.json +1 -0
  4. package/dist/esm/sonos/api/namespace.js +38 -0
  5. package/dist/esm/sonos/api/namespaces/audioClip.js +25 -0
  6. package/dist/esm/sonos/api/namespaces/groupVolume.js +25 -0
  7. package/dist/esm/sonos/api/namespaces/groups.js +31 -0
  8. package/dist/esm/sonos/api/namespaces/homeTheater.js +13 -0
  9. package/dist/esm/sonos/api/namespaces/playback.js +56 -0
  10. package/dist/esm/sonos/api/namespaces/playbackMetadata.js +16 -0
  11. package/dist/esm/sonos/api/namespaces/playbackSession.js +67 -0
  12. package/dist/esm/sonos/api/namespaces/playerVolume.js +41 -0
  13. package/dist/esm/sonos/api/websocket.js +310 -0
  14. package/dist/esm/sonos/client.js +193 -0
  15. package/dist/esm/sonos/constants.js +18 -0
  16. package/dist/esm/sonos/errors.js +49 -0
  17. package/dist/esm/sonos/group.js +231 -0
  18. package/dist/esm/sonos/models.js +63 -0
  19. package/dist/esm/sonos/player.js +112 -0
  20. package/dist/esm/sonos/types.js +46 -0
  21. package/dist/esm/sonos/utils.js +18 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.js +27 -0
  24. package/dist/sonos/api/namespace.d.ts +16 -0
  25. package/dist/sonos/api/namespace.js +38 -0
  26. package/dist/sonos/api/namespaces/audioClip.d.ts +16 -0
  27. package/dist/sonos/api/namespaces/audioClip.js +25 -0
  28. package/dist/sonos/api/namespaces/groupVolume.d.ts +10 -0
  29. package/dist/sonos/api/namespaces/groupVolume.js +25 -0
  30. package/dist/sonos/api/namespaces/groups.d.ts +10 -0
  31. package/dist/sonos/api/namespaces/groups.js +31 -0
  32. package/dist/sonos/api/namespaces/homeTheater.d.ts +5 -0
  33. package/dist/sonos/api/namespaces/homeTheater.js +13 -0
  34. package/dist/sonos/api/namespaces/playback.d.ts +17 -0
  35. package/dist/sonos/api/namespaces/playback.js +56 -0
  36. package/dist/sonos/api/namespaces/playbackMetadata.d.ts +7 -0
  37. package/dist/sonos/api/namespaces/playbackMetadata.js +16 -0
  38. package/dist/sonos/api/namespaces/playbackSession.d.ts +30 -0
  39. package/dist/sonos/api/namespaces/playbackSession.js +67 -0
  40. package/dist/sonos/api/namespaces/playerVolume.d.ts +12 -0
  41. package/dist/sonos/api/namespaces/playerVolume.js +41 -0
  42. package/dist/sonos/api/websocket.d.ts +51 -0
  43. package/dist/sonos/api/websocket.js +310 -0
  44. package/dist/sonos/client.d.ts +44 -0
  45. package/dist/sonos/client.js +193 -0
  46. package/dist/sonos/constants.d.ts +14 -0
  47. package/dist/sonos/constants.js +18 -0
  48. package/dist/sonos/errors.d.ts +25 -0
  49. package/dist/sonos/errors.js +49 -0
  50. package/dist/sonos/group.d.ts +58 -0
  51. package/dist/sonos/group.js +231 -0
  52. package/dist/sonos/models.d.ts +20 -0
  53. package/dist/sonos/models.js +63 -0
  54. package/dist/sonos/player.d.ts +33 -0
  55. package/dist/sonos/player.js +112 -0
  56. package/dist/sonos/types.d.ts +279 -0
  57. package/dist/sonos/types.js +46 -0
  58. package/dist/sonos/utils.d.ts +6 -0
  59. package/dist/sonos/utils.js +18 -0
  60. package/package.json +49 -0
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SonosWebSocketApi = void 0;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const node_crypto_1 = require("node:crypto");
9
+ const constants_1 = require("../constants");
10
+ const errors_1 = require("../errors");
11
+ const audioClip_1 = require("./namespaces/audioClip");
12
+ const groups_1 = require("./namespaces/groups");
13
+ const groupVolume_1 = require("./namespaces/groupVolume");
14
+ const playback_1 = require("./namespaces/playback");
15
+ const playbackMetadata_1 = require("./namespaces/playbackMetadata");
16
+ const playbackSession_1 = require("./namespaces/playbackSession");
17
+ const playerVolume_1 = require("./namespaces/playerVolume");
18
+ const homeTheater_1 = require("./namespaces/homeTheater");
19
+ class SonosWebSocketApi {
20
+ websocketUrl;
21
+ ws;
22
+ resultFutures = new Map();
23
+ stopCalled = false;
24
+ heartbeatTimer;
25
+ heartbeatIntervalMs;
26
+ retryDelayMs;
27
+ retryJitterMs;
28
+ maxReconnects;
29
+ audioClip;
30
+ groups;
31
+ groupVolume;
32
+ playback;
33
+ playbackMetadata;
34
+ playbackSession;
35
+ playerVolume;
36
+ homeTheater;
37
+ logger = console;
38
+ onConnect;
39
+ onDisconnect;
40
+ constructor(websocketUrl, opts = {}) {
41
+ this.websocketUrl = websocketUrl;
42
+ this.audioClip = new audioClip_1.AudioClipNamespace(this);
43
+ this.groups = new groups_1.GroupsNamespace(this);
44
+ this.groupVolume = new groupVolume_1.GroupVolumeNamespace(this);
45
+ this.playback = new playback_1.PlaybackNamespace(this);
46
+ this.playbackMetadata = new playbackMetadata_1.PlaybackMetadataNamespace(this);
47
+ this.playbackSession = new playbackSession_1.PlaybackSessionNamespace(this);
48
+ this.playerVolume = new playerVolume_1.PlayerVolumeNamespace(this);
49
+ this.homeTheater = new homeTheater_1.HomeTheaterNamespace(this);
50
+ this.heartbeatIntervalMs = opts.heartbeatIntervalMs ?? 30000;
51
+ this.retryDelayMs = opts.retryDelayMs ?? 2000;
52
+ this.retryJitterMs = opts.retryJitterMs ?? 500;
53
+ this.maxReconnects = opts.maxReconnects;
54
+ }
55
+ get connected() {
56
+ return Boolean(this.ws && this.ws.readyState === ws_1.default.OPEN);
57
+ }
58
+ async connect() {
59
+ if (this.ws) {
60
+ throw new errors_1.InvalidState('Already connected');
61
+ }
62
+ this.logger.debug?.(`Connecting to Sonos WebSocket ${this.websocketUrl}`);
63
+ this.stopCalled = false;
64
+ try {
65
+ this.ws = new ws_1.default(this.websocketUrl, {
66
+ headers: {
67
+ 'X-Sonos-Api-Key': constants_1.LOCAL_API_TOKEN,
68
+ 'Sec-WebSocket-Protocol': 'v1.api.smartspeaker.audio',
69
+ },
70
+ rejectUnauthorized: false,
71
+ perMessageDeflate: true,
72
+ maxPayload: 0,
73
+ });
74
+ }
75
+ catch (err) {
76
+ throw new errors_1.CannotConnect('Failed to create websocket', err);
77
+ }
78
+ await new Promise((resolve, reject) => {
79
+ if (!this.ws)
80
+ return reject(new errors_1.CannotConnect('No websocket'));
81
+ this.ws.once('open', () => resolve());
82
+ this.ws.once('error', (err) => reject(new errors_1.CannotConnect('Failed to connect', err)));
83
+ });
84
+ this.ws.on('message', (data) => this.handleRawMessage(data));
85
+ this.ws.on('close', () => {
86
+ this.clearHeartbeat();
87
+ });
88
+ this.ws.on('error', (err) => {
89
+ this.logger.warn?.('WebSocket error', err);
90
+ });
91
+ this.ws.on('pong', () => {
92
+ // noop; pong receipt confirms liveness
93
+ });
94
+ this.startHeartbeat();
95
+ await this.onConnect?.();
96
+ }
97
+ async startListening() {
98
+ this.stopCalled = false;
99
+ while (!this.stopCalled) {
100
+ let attempts = 0;
101
+ try {
102
+ if (!this.connected) {
103
+ await this.connect();
104
+ }
105
+ const reason = await this.waitForClose();
106
+ if (this.stopCalled)
107
+ break;
108
+ await this.onDisconnect?.(reason);
109
+ this.logger.warn?.('WebSocket closed, reconnecting...', reason);
110
+ attempts += 1;
111
+ if (this.maxReconnects && attempts > this.maxReconnects) {
112
+ this.logger.error?.('Max reconnect attempts reached');
113
+ break;
114
+ }
115
+ await this.delay(this.computeRetryDelay(attempts));
116
+ }
117
+ catch (err) {
118
+ if (this.stopCalled)
119
+ break;
120
+ this.logger.warn?.('WebSocket listen error, reconnecting...', err);
121
+ await this.onDisconnect?.(err instanceof Error ? err.message : undefined);
122
+ attempts += 1;
123
+ if (this.maxReconnects && attempts > this.maxReconnects) {
124
+ this.logger.error?.('Max reconnect attempts reached after error');
125
+ break;
126
+ }
127
+ await this.delay(this.computeRetryDelay(attempts));
128
+ }
129
+ }
130
+ }
131
+ async disconnect() {
132
+ this.stopCalled = true;
133
+ this.clearHeartbeat();
134
+ for (const pending of this.resultFutures.values()) {
135
+ pending.reject(new errors_1.ConnectionClosed('Connection closed'));
136
+ }
137
+ this.resultFutures.clear();
138
+ if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
139
+ await new Promise((resolve) => {
140
+ this.ws?.once('close', () => resolve());
141
+ this.ws?.close();
142
+ });
143
+ }
144
+ this.ws = undefined;
145
+ }
146
+ async sendCommand(namespace, command, options, pathParams) {
147
+ if (!this.connected || !this.ws)
148
+ throw new errors_1.InvalidState('Not connected');
149
+ const cmdId = (0, node_crypto_1.randomUUID)();
150
+ const cmdMessage = {
151
+ namespace: `${namespace}:${constants_1.API_VERSION}`,
152
+ command,
153
+ cmdId,
154
+ ...(pathParams ?? {}),
155
+ };
156
+ const payload = [cmdMessage, options ?? {}];
157
+ const resultPromise = new Promise((resolve, reject) => {
158
+ this.resultFutures.set(cmdId, { resolve, reject });
159
+ });
160
+ await this.send(payload);
161
+ return resultPromise.finally(() => this.resultFutures.delete(cmdId));
162
+ }
163
+ sendCommandNoWait(namespace, command, options, pathParams) {
164
+ if (!this.connected || !this.ws)
165
+ throw new errors_1.NotConnected('Not connected');
166
+ const cmdMessage = {
167
+ namespace: `${namespace}:${constants_1.API_VERSION}`,
168
+ command,
169
+ cmdId: (0, node_crypto_1.randomUUID)(),
170
+ ...(pathParams ?? {}),
171
+ };
172
+ const payload = [cmdMessage, options ?? {}];
173
+ void this.send(payload);
174
+ }
175
+ async send(payload) {
176
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
177
+ throw new errors_1.NotConnected('Not connected');
178
+ }
179
+ if (this.logger && this.logger.log && constants_1.LOG_LEVEL_VERBOSE) {
180
+ this.logger.log?.(constants_1.LOG_LEVEL_VERBOSE, 'Publishing message', payload);
181
+ }
182
+ const data = JSON.stringify(payload);
183
+ await new Promise((resolve, reject) => {
184
+ this.ws?.send(data, (err) => {
185
+ if (err)
186
+ reject(err);
187
+ else
188
+ resolve();
189
+ });
190
+ });
191
+ }
192
+ handleRawMessage(raw) {
193
+ try {
194
+ const parsed = JSON.parse(raw.toString());
195
+ try {
196
+ this.handleIncoming(parsed);
197
+ }
198
+ catch (err) {
199
+ this.logger.error?.('Failed to handle Sonos message', err);
200
+ }
201
+ }
202
+ catch (err) {
203
+ this.logger.error?.('Invalid JSON from Sonos', err);
204
+ }
205
+ }
206
+ handleIncoming(raw) {
207
+ if (!Array.isArray(raw) || raw.length !== 2) {
208
+ this.logger.error?.('Invalid Sonos message shape', raw);
209
+ return;
210
+ }
211
+ const [msg, msgData] = raw;
212
+ if (!msg || typeof msg !== 'object') {
213
+ throw new errors_1.InvalidMessage('Received malformed message');
214
+ }
215
+ // error response
216
+ if ('errorCode' in msgData) {
217
+ const errData = msgData;
218
+ if ('cmdId' in msg && msg.cmdId) {
219
+ const future = this.resultFutures.get(msg.cmdId);
220
+ future?.reject(new errors_1.FailedCommand(errData.errorCode, errData.reason));
221
+ }
222
+ else {
223
+ this.logger.error?.('Unhandled error', msgData);
224
+ }
225
+ return;
226
+ }
227
+ // command result
228
+ if ('success' in msg) {
229
+ if (msg.cmdId && this.resultFutures.has(msg.cmdId)) {
230
+ const pending = this.resultFutures.get(msg.cmdId);
231
+ if (msg.success) {
232
+ pending?.resolve(msgData);
233
+ }
234
+ else {
235
+ pending?.reject(new errors_1.FailedCommand(String(msgData['_objectType'] ?? 'unknown')));
236
+ }
237
+ }
238
+ return;
239
+ }
240
+ // event
241
+ if (msg.type) {
242
+ const namespaces = [
243
+ this.audioClip,
244
+ this.groups,
245
+ this.groupVolume,
246
+ this.playbackMetadata,
247
+ this.playbackSession,
248
+ this.playback,
249
+ this.playerVolume,
250
+ this.homeTheater,
251
+ ];
252
+ const target = namespaces.find((ns) => ns.eventType === msg.type);
253
+ if (target) {
254
+ target.handleEvent(msg, msgData);
255
+ }
256
+ else {
257
+ this.logger.debug?.(`Unhandled event type ${msg.type}`);
258
+ }
259
+ return;
260
+ }
261
+ this.logger.debug?.('Unhandled message', raw);
262
+ }
263
+ async waitForClose() {
264
+ return new Promise((resolve, reject) => {
265
+ const ws = this.ws;
266
+ if (!ws)
267
+ return reject(new errors_1.NotConnected('No websocket'));
268
+ const onClose = (code, reason) => {
269
+ ws.removeListener('error', onError);
270
+ resolve(`${code}:${reason.toString()}`);
271
+ };
272
+ const onError = (err) => {
273
+ ws.removeListener('close', onClose);
274
+ reject(new errors_1.ConnectionFailed(err));
275
+ };
276
+ ws.once('close', onClose);
277
+ ws.once('error', onError);
278
+ });
279
+ }
280
+ startHeartbeat() {
281
+ this.clearHeartbeat();
282
+ if (!this.heartbeatIntervalMs)
283
+ return;
284
+ this.heartbeatTimer = setInterval(() => {
285
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN)
286
+ return;
287
+ try {
288
+ this.ws.ping();
289
+ }
290
+ catch (err) {
291
+ this.logger.warn?.('Heartbeat ping failed', err);
292
+ this.ws.terminate();
293
+ }
294
+ }, this.heartbeatIntervalMs);
295
+ }
296
+ clearHeartbeat() {
297
+ if (this.heartbeatTimer)
298
+ clearInterval(this.heartbeatTimer);
299
+ this.heartbeatTimer = undefined;
300
+ }
301
+ async delay(ms) {
302
+ await new Promise((resolve) => setTimeout(resolve, ms));
303
+ }
304
+ computeRetryDelay(attempt) {
305
+ const base = this.retryDelayMs * Math.min(2 ** (attempt - 1), 8);
306
+ const jitter = Math.random() * this.retryJitterMs;
307
+ return base + jitter;
308
+ }
309
+ }
310
+ exports.SonosWebSocketApi = SonosWebSocketApi;
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SonosClient = void 0;
4
+ const websocket_1 = require("./api/websocket");
5
+ const constants_1 = require("./constants");
6
+ const errors_1 = require("./errors");
7
+ const group_1 = require("./group");
8
+ const player_1 = require("./player");
9
+ const utils_1 = require("./utils");
10
+ class SonosClient {
11
+ playerIp;
12
+ api;
13
+ playerId;
14
+ householdId;
15
+ player = null;
16
+ groupMap = new Map();
17
+ subscribers = [];
18
+ logger;
19
+ bootstrapPromise = null;
20
+ connectedPlayerIds = new Set();
21
+ constructor(playerIp, options = {}) {
22
+ this.playerIp = playerIp;
23
+ this.logger = options.logger ?? console;
24
+ }
25
+ get groups() {
26
+ return Array.from(this.groupMap.values());
27
+ }
28
+ subscribe(cb, eventFilter, objectIdFilter) {
29
+ const eventSet = eventFilter == null
30
+ ? undefined
31
+ : Array.isArray(eventFilter)
32
+ ? new Set(eventFilter)
33
+ : new Set([eventFilter]);
34
+ const objectSet = objectIdFilter == null
35
+ ? undefined
36
+ : Array.isArray(objectIdFilter)
37
+ ? new Set(objectIdFilter)
38
+ : new Set([objectIdFilter]);
39
+ const sub = {
40
+ cb,
41
+ eventFilter: eventSet,
42
+ objectIdFilter: objectSet,
43
+ };
44
+ this.subscribers.push(sub);
45
+ return () => {
46
+ this.subscribers = this.subscribers.filter((s) => s !== sub);
47
+ };
48
+ }
49
+ signalEvent(event) {
50
+ for (const sub of this.subscribers) {
51
+ if (sub.eventFilter && !sub.eventFilter.has(event.eventType))
52
+ continue;
53
+ if (sub.objectIdFilter && event.objectId && !sub.objectIdFilter.has(event.objectId)) {
54
+ continue;
55
+ }
56
+ sub.cb(event);
57
+ }
58
+ }
59
+ async connect(options) {
60
+ const discovery = await (0, utils_1.getDiscoveryInfo)(this.playerIp, options);
61
+ this.playerId = discovery.playerId;
62
+ this.householdId = discovery.householdId;
63
+ this.api = new websocket_1.SonosWebSocketApi(discovery.websocketUrl, {
64
+ heartbeatIntervalMs: options?.heartbeatIntervalMs,
65
+ retryDelayMs: options?.retryDelayMs,
66
+ retryJitterMs: options?.retryJitterMs,
67
+ maxReconnects: options?.maxReconnects,
68
+ });
69
+ if (options?.logger)
70
+ this.api.logger = options.logger;
71
+ this.api.onConnect = () => this.handleSocketConnected();
72
+ this.api.onDisconnect = (reason) => this.handleSocketDisconnected(reason);
73
+ await this.api.connect();
74
+ }
75
+ async disconnect() {
76
+ for (const group of this.groupMap.values()) {
77
+ group.cleanup();
78
+ }
79
+ await this.api.disconnect();
80
+ }
81
+ async start() {
82
+ await this.bootstrap();
83
+ await this.api.startListening();
84
+ }
85
+ async bootstrap(reconnect = false) {
86
+ if (this.bootstrapPromise)
87
+ return this.bootstrapPromise;
88
+ this.bootstrapPromise = (async () => {
89
+ if (!this.api || !this.api.connected) {
90
+ await this.connect();
91
+ }
92
+ if (reconnect || this.groupMap.size) {
93
+ for (const group of this.groupMap.values())
94
+ group.cleanup();
95
+ this.groupMap.clear();
96
+ }
97
+ const groupsData = await this.api.groups.getGroups(this.householdId, true);
98
+ for (const groupData of groupsData.groups) {
99
+ await this.setupGroup(groupData);
100
+ }
101
+ const playerData = groupsData.players.find((p) => p.id === this.playerId);
102
+ if (!playerData) {
103
+ throw new errors_1.FailedCommand('playerNotFound', 'Local player not returned in groups response');
104
+ }
105
+ this.player = new player_1.SonosPlayer(this, playerData);
106
+ await this.player.init();
107
+ this.connectedPlayerIds = new Set(groupsData.players.map((p) => p.id));
108
+ await this.api.groups.subscribe(this.householdId, (data) => this.handleGroupsEvent(data));
109
+ this.signalEvent({ eventType: constants_1.EventType.CONNECTED, objectId: this.playerId });
110
+ })();
111
+ try {
112
+ await this.bootstrapPromise;
113
+ }
114
+ finally {
115
+ this.bootstrapPromise = null;
116
+ }
117
+ }
118
+ async createGroup(playerIds, musicContextGroupId) {
119
+ await this.api.groups.createGroup(this.householdId, playerIds, musicContextGroupId);
120
+ }
121
+ async setupGroup(groupData) {
122
+ const group = new group_1.SonosGroup(this, groupData);
123
+ this.groupMap.set(group.id, group);
124
+ await group.init();
125
+ if (this.player)
126
+ this.player.checkActiveGroup();
127
+ }
128
+ handleGroupsEvent(groupsData) {
129
+ for (const groupData of groupsData.groups) {
130
+ const existing = this.groupMap.get(groupData.id);
131
+ if (existing) {
132
+ existing.updateData(groupData);
133
+ continue;
134
+ }
135
+ void this.setupGroup(groupData);
136
+ }
137
+ const removedIds = new Set(this.groupMap.keys());
138
+ for (const g of groupsData.groups)
139
+ removedIds.delete(g.id);
140
+ for (const removedId of removedIds) {
141
+ const group = this.groupMap.get(removedId);
142
+ group?.cleanup();
143
+ this.groupMap.delete(removedId);
144
+ this.signalEvent({
145
+ eventType: constants_1.EventType.GROUP_REMOVED,
146
+ objectId: removedId,
147
+ data: group,
148
+ });
149
+ }
150
+ const incomingPlayers = new Set(groupsData.players.map((p) => p.id));
151
+ // removals
152
+ for (const prev of Array.from(this.connectedPlayerIds)) {
153
+ if (!incomingPlayers.has(prev)) {
154
+ this.signalEvent({
155
+ eventType: constants_1.EventType.PLAYER_REMOVED,
156
+ objectId: prev,
157
+ data: prev,
158
+ });
159
+ this.connectedPlayerIds.delete(prev);
160
+ }
161
+ }
162
+ // updates/additions
163
+ for (const playerData of groupsData.players) {
164
+ if (playerData.id === this.playerId && this.player) {
165
+ this.player.updateData(playerData);
166
+ }
167
+ if (!this.connectedPlayerIds.has(playerData.id)) {
168
+ this.connectedPlayerIds.add(playerData.id);
169
+ this.signalEvent({
170
+ eventType: constants_1.EventType.PLAYER_ADDED,
171
+ objectId: playerData.id,
172
+ data: playerData,
173
+ });
174
+ }
175
+ }
176
+ }
177
+ async handleSocketConnected() {
178
+ try {
179
+ await this.bootstrap(true);
180
+ }
181
+ catch (err) {
182
+ this.logger.error?.('Failed to bootstrap after reconnect', err);
183
+ }
184
+ }
185
+ handleSocketDisconnected(reason) {
186
+ this.signalEvent({
187
+ eventType: constants_1.EventType.DISCONNECTED,
188
+ objectId: this.playerId,
189
+ data: reason,
190
+ });
191
+ }
192
+ }
193
+ exports.SonosClient = SonosClient;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventType = exports.API_VERSION = exports.LOG_LEVEL_VERBOSE = exports.LOCAL_API_TOKEN = void 0;
4
+ exports.LOCAL_API_TOKEN = '123e4567-e89b-12d3-a456-426655440000';
5
+ exports.LOG_LEVEL_VERBOSE = 5;
6
+ exports.API_VERSION = 1;
7
+ var EventType;
8
+ (function (EventType) {
9
+ EventType["GROUP_ADDED"] = "group_added";
10
+ EventType["GROUP_UPDATED"] = "group_updated";
11
+ EventType["GROUP_REMOVED"] = "group_removed";
12
+ EventType["PLAYER_ADDED"] = "player_added";
13
+ EventType["PLAYER_UPDATED"] = "player_updated";
14
+ EventType["PLAYER_REMOVED"] = "player_removed";
15
+ EventType["CONNECTED"] = "connected";
16
+ EventType["DISCONNECTED"] = "disconnected";
17
+ EventType["MATCH_ALL"] = "match_all";
18
+ })(EventType || (exports.EventType = EventType = {}));
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FailedCommand = exports.InvalidMessage = exports.InvalidState = exports.NotConnected = exports.ConnectionFailed = exports.CannotConnect = exports.ConnectionClosed = exports.TransportError = exports.SonosError = void 0;
4
+ class SonosError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = this.constructor.name;
8
+ }
9
+ }
10
+ exports.SonosError = SonosError;
11
+ class TransportError extends SonosError {
12
+ causeError;
13
+ constructor(message, error) {
14
+ super(message);
15
+ this.causeError = error;
16
+ }
17
+ }
18
+ exports.TransportError = TransportError;
19
+ class ConnectionClosed extends TransportError {
20
+ }
21
+ exports.ConnectionClosed = ConnectionClosed;
22
+ class CannotConnect extends TransportError {
23
+ }
24
+ exports.CannotConnect = CannotConnect;
25
+ class ConnectionFailed extends TransportError {
26
+ constructor(error) {
27
+ super(error ? `${error.message}` : 'Connection failed.', error);
28
+ }
29
+ }
30
+ exports.ConnectionFailed = ConnectionFailed;
31
+ class NotConnected extends SonosError {
32
+ }
33
+ exports.NotConnected = NotConnected;
34
+ class InvalidState extends SonosError {
35
+ }
36
+ exports.InvalidState = InvalidState;
37
+ class InvalidMessage extends SonosError {
38
+ }
39
+ exports.InvalidMessage = InvalidMessage;
40
+ class FailedCommand extends SonosError {
41
+ errorCode;
42
+ details;
43
+ constructor(errorCode, details) {
44
+ super(`Command failed: ${details ?? errorCode}`);
45
+ this.errorCode = errorCode;
46
+ this.details = details;
47
+ }
48
+ }
49
+ exports.FailedCommand = FailedCommand;