yoto-nodejs-client 0.0.5 → 0.0.7

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 (52) hide show
  1. package/README.md +73 -40
  2. package/bin/auth.js +4 -3
  3. package/bin/device-model.js +25 -5
  4. package/bin/device-tui.js +25 -3
  5. package/bin/devices.js +25 -9
  6. package/bin/lib/cli-helpers.d.ts.map +1 -1
  7. package/bin/lib/cli-helpers.js +3 -1
  8. package/bin/lib/token-helpers.d.ts +4 -2
  9. package/bin/lib/token-helpers.d.ts.map +1 -1
  10. package/bin/lib/token-helpers.js +9 -8
  11. package/bin/refresh-token.js +4 -2
  12. package/bin/token-info.js +2 -2
  13. package/lib/api-client.d.ts +11 -10
  14. package/lib/api-client.d.ts.map +1 -1
  15. package/lib/api-client.js +12 -14
  16. package/lib/api-endpoints/auth.test.js +4 -4
  17. package/lib/api-endpoints/content.test.js +32 -32
  18. package/lib/api-endpoints/devices.d.ts +4 -4
  19. package/lib/api-endpoints/devices.js +2 -2
  20. package/lib/api-endpoints/devices.test.js +45 -45
  21. package/lib/api-endpoints/endpoint-test-helpers.d.ts +3 -4
  22. package/lib/api-endpoints/endpoint-test-helpers.d.ts.map +1 -1
  23. package/lib/api-endpoints/endpoint-test-helpers.js +21 -5
  24. package/lib/api-endpoints/family-library-groups.d.ts +3 -3
  25. package/lib/api-endpoints/family-library-groups.d.ts.map +1 -1
  26. package/lib/api-endpoints/family-library-groups.js +3 -3
  27. package/lib/api-endpoints/family-library-groups.test.js +29 -29
  28. package/lib/api-endpoints/family.test.js +11 -11
  29. package/lib/api-endpoints/icons.test.js +14 -14
  30. package/lib/mqtt/client.d.ts +123 -48
  31. package/lib/mqtt/client.d.ts.map +1 -1
  32. package/lib/mqtt/client.js +131 -49
  33. package/lib/mqtt/factory.d.ts +12 -5
  34. package/lib/mqtt/factory.d.ts.map +1 -1
  35. package/lib/mqtt/factory.js +39 -11
  36. package/lib/mqtt/index.js +2 -1
  37. package/lib/mqtt/mqtt.test.js +25 -22
  38. package/lib/test-helpers/device-model-test-helpers.d.ts +29 -0
  39. package/lib/test-helpers/device-model-test-helpers.d.ts.map +1 -0
  40. package/lib/test-helpers/device-model-test-helpers.js +116 -0
  41. package/lib/token.d.ts +44 -2
  42. package/lib/token.d.ts.map +1 -1
  43. package/lib/token.js +142 -2
  44. package/lib/yoto-account.d.ts +339 -9
  45. package/lib/yoto-account.d.ts.map +1 -1
  46. package/lib/yoto-account.js +411 -39
  47. package/lib/yoto-account.test.js +139 -0
  48. package/lib/yoto-device.d.ts +418 -30
  49. package/lib/yoto-device.d.ts.map +1 -1
  50. package/lib/yoto-device.js +670 -104
  51. package/lib/yoto-device.test.js +88 -0
  52. package/package.json +1 -1
package/lib/token.js CHANGED
@@ -2,14 +2,26 @@ import { EventEmitter } from 'node:events'
2
2
  import { jwtDecode } from 'jwt-decode'
3
3
  import { exchangeToken } from './api-endpoints/auth.js'
4
4
 
5
+ const AUTO_REFRESH_RETRY_BASE_SECONDS = 5
6
+ const AUTO_REFRESH_RETRY_MAX_SECONDS = 300
7
+
5
8
  /**
6
9
  * @typedef {Object} RefreshableTokenOpts
7
10
  * @property {string} clientId - OAuth client ID
8
11
  * @property {string} refreshToken - OAuth refresh token
9
12
  * @property {string} accessToken - Initial OAuth access token (JWT)
13
+ * @property {OnTokenRefreshHandler} onTokenRefresh - A function that will receive the refreshed token info and perisist it for future use
10
14
  * @property {number} [bufferSeconds=30] - Seconds before expiration to consider token expired
11
15
  */
12
16
 
17
+ /**
18
+ * This is a REQUIRED function. It gets called after the token is refreshed with new tokens.
19
+ * It should save the new tokens somewhere. When starting back up, re-use the newly perissted tokens.
20
+ * If you loose track of these new tokens, you will effectively be logged out.
21
+ *
22
+ * @typedef {(refreshSuccessEvent: RefreshSuccessEvent) => void | Promise<void>} OnTokenRefreshHandler
23
+ */
24
+
13
25
  /**
14
26
  * @typedef {Object} RefreshSuccessEvent
15
27
  * @property {string} clientId - The OAuth client ID
@@ -37,12 +49,13 @@ import { exchangeToken } from './api-endpoints/auth.js'
37
49
  *
38
50
  * Events:
39
51
  * - 'refresh:start' - Emitted when token refresh begins
40
- * - 'refresh:success' - Emitted when token refresh succeeds, passes { clientId, accessToken, refreshToken, expiresAt }
52
+ * - 'refresh:success' - Emitted when token refresh succeeds, passes { clientId, updatedAccessToken, updatedRefreshToken, updatedExpiresAt, prevAccessToken, prevRefreshToken, prevExpiresAt }
41
53
  * - 'refresh:error' - Emitted when token refresh fails (transient errors), passes error
42
54
  * - 'invalid' - Emitted when refresh token is permanently invalid, passes error
43
55
  *
44
56
  * @extends {EventEmitter<RefreshableTokenEventMap>}
45
57
  */
58
+
46
59
  export class RefreshableToken extends EventEmitter {
47
60
  /** @type {string} */
48
61
  #clientId
@@ -58,16 +71,25 @@ export class RefreshableToken extends EventEmitter {
58
71
  #invalid = false
59
72
  /** @type {Promise<RefreshSuccessEvent> | null} */
60
73
  #inFlightRefresh = null
74
+ /** @type {boolean} */
75
+ #autoRefreshEnabled = true
76
+ /** @type {ReturnType<typeof setTimeout> | null} */
77
+ #autoRefreshTimeout = null
78
+ /** @type {number} */
79
+ #autoRefreshRetryAttempt = 0
80
+ /** @type {OnTokenRefreshHandler} */
81
+ #onTokenRefresh
61
82
 
62
83
  /**
63
84
  * @param {RefreshableTokenOpts} opts
64
85
  */
65
- constructor ({ clientId, refreshToken, accessToken, bufferSeconds = 30 }) {
86
+ constructor ({ clientId, refreshToken, accessToken, bufferSeconds = 30, onTokenRefresh }) {
66
87
  super()
67
88
  this.#clientId = clientId
68
89
  this.#refreshToken = refreshToken
69
90
  this.#accessToken = accessToken
70
91
  this.#bufferSeconds = bufferSeconds
92
+ this.#onTokenRefresh = onTokenRefresh
71
93
 
72
94
  // Decode the JWT to get expiration
73
95
  try {
@@ -80,6 +102,8 @@ export class RefreshableToken extends EventEmitter {
80
102
  const error = /** @type {Error} */ (err)
81
103
  throw new Error(`Failed to decode access token: ${error.message}`)
82
104
  }
105
+
106
+ this.#scheduleAutoRefresh()
83
107
  }
84
108
 
85
109
  /**
@@ -170,8 +194,13 @@ export class RefreshableToken extends EventEmitter {
170
194
  prevExpiresAt,
171
195
  }
172
196
 
197
+ await this.#onTokenRefresh(eventPayload)
198
+
173
199
  this.emit('refresh:success', eventPayload)
174
200
 
201
+ this.#autoRefreshRetryAttempt = 0
202
+ this.#scheduleAutoRefresh()
203
+
175
204
  return eventPayload
176
205
  } catch (err) {
177
206
  const error = /** @type {any} */ (err)
@@ -188,6 +217,7 @@ export class RefreshableToken extends EventEmitter {
188
217
  if (error.body?.error && invalidRefreshErrors.includes(error.body.error)) {
189
218
  // Mark this token as permanently invalid
190
219
  this.#invalid = true
220
+ this.#clearAutoRefreshTimeout()
191
221
  const statusCode = error.statusCode ? ` (${error.statusCode})` : ''
192
222
  const invalidError = new Error(`Refresh token is invalid or expired${statusCode}: ${error.body.error}${error.body.error_description ? ` - ${error.body.error_description}` : ''}`)
193
223
  this.emit('invalid', invalidError)
@@ -196,10 +226,120 @@ export class RefreshableToken extends EventEmitter {
196
226
 
197
227
  // For other errors, rethrow without marking as invalid (might be transient)
198
228
  this.emit('refresh:error', error)
229
+ this.#scheduleAutoRefreshRetry()
199
230
  throw error
200
231
  }
201
232
  }
202
233
 
234
+ /**
235
+ * Stop background token refresh scheduling.
236
+ */
237
+ stopAutoRefresh () {
238
+ this.#autoRefreshEnabled = false
239
+ this.#clearAutoRefreshTimeout()
240
+ }
241
+
242
+ /**
243
+ * Start background token refresh scheduling (enabled by default).
244
+ */
245
+ startAutoRefresh () {
246
+ if (this.#invalid) {
247
+ return
248
+ }
249
+
250
+ this.#autoRefreshEnabled = true
251
+ this.#autoRefreshRetryAttempt = 0
252
+ this.#scheduleAutoRefresh()
253
+ }
254
+
255
+ /**
256
+ * Check if background token refresh scheduling is enabled.
257
+ * @returns {boolean}
258
+ */
259
+ isAutoRefreshEnabled () {
260
+ return this.#autoRefreshEnabled
261
+ }
262
+
263
+ /**
264
+ * Get the current OAuth client ID (synchronous).
265
+ * @returns {string}
266
+ */
267
+ get clientId () {
268
+ return this.#clientId
269
+ }
270
+
271
+ /**
272
+ * Get the latest access token value (synchronous).
273
+ * Prefer getAccessToken() if you need a guaranteed-valid token in async contexts.
274
+ * @returns {string}
275
+ */
276
+ get accessToken () {
277
+ return this.#accessToken
278
+ }
279
+
280
+ /**
281
+ * Schedule the next refresh for when the token becomes stale (expiresAt - bufferSeconds).
282
+ * Uses an unref'd timer so it doesn't keep the process alive.
283
+ */
284
+ #scheduleAutoRefresh () {
285
+ if (!this.#autoRefreshEnabled || this.#invalid) {
286
+ return
287
+ }
288
+
289
+ const now = Math.floor(Date.now() / 1000)
290
+ const refreshAt = this.#expiresAt - this.#bufferSeconds
291
+ const delayMs = Math.max(0, (refreshAt - now) * 1000)
292
+
293
+ this.#clearAutoRefreshTimeout()
294
+ this.#autoRefreshTimeout = setTimeout(() => {
295
+ this.#autoRefreshTimeout = null
296
+ if (this.#invalid) {
297
+ return
298
+ }
299
+
300
+ this.#refreshAccessToken().catch(() => {
301
+ // Errors are emitted by #performRefresh; retries are scheduled there as well.
302
+ })
303
+ }, delayMs)
304
+
305
+ this.#autoRefreshTimeout.unref?.()
306
+ }
307
+
308
+ #clearAutoRefreshTimeout () {
309
+ if (!this.#autoRefreshTimeout) {
310
+ return
311
+ }
312
+
313
+ clearTimeout(this.#autoRefreshTimeout)
314
+ this.#autoRefreshTimeout = null
315
+ }
316
+
317
+ #scheduleAutoRefreshRetry () {
318
+ if (!this.#autoRefreshEnabled || this.#invalid) {
319
+ return
320
+ }
321
+
322
+ this.#autoRefreshRetryAttempt += 1
323
+ const retrySeconds = Math.min(
324
+ AUTO_REFRESH_RETRY_MAX_SECONDS,
325
+ AUTO_REFRESH_RETRY_BASE_SECONDS * (2 ** (this.#autoRefreshRetryAttempt - 1))
326
+ )
327
+
328
+ this.#clearAutoRefreshTimeout()
329
+ this.#autoRefreshTimeout = setTimeout(() => {
330
+ this.#autoRefreshTimeout = null
331
+ if (this.#invalid) {
332
+ return
333
+ }
334
+
335
+ this.#refreshAccessToken().catch(() => {
336
+ // Errors are emitted by #performRefresh; retries are scheduled there as well.
337
+ })
338
+ }, retrySeconds * 1000)
339
+
340
+ this.#autoRefreshTimeout.unref?.()
341
+ }
342
+
203
343
  /**
204
344
  * Check if the token is currently valid (not expired and not marked invalid).
205
345
  * @returns {boolean} True if token is valid
@@ -17,14 +17,168 @@
17
17
  * @property {number} deviceCount - Number of devices managed
18
18
  * @property {string[]} devices - Array of device IDs
19
19
  */
20
+ /**
21
+ * Device event wrapper
22
+ * @template T
23
+ * @typedef {{ deviceId: string } & T} YotoAccountDeviceEvent
24
+ */
25
+ /**
26
+ * Device added event data
27
+ * @typedef {YotoAccountDeviceEvent<{}>} YotoAccountDeviceAddedEvent
28
+ */
29
+ /**
30
+ * Device removed event data
31
+ * @typedef {YotoAccountDeviceEvent<{}>} YotoAccountDeviceRemovedEvent
32
+ */
33
+ /**
34
+ * Device status update event data
35
+ * @typedef {YotoAccountDeviceEvent<{
36
+ * status: YotoDeviceStatus,
37
+ * source: string,
38
+ * changedFields: Set<keyof YotoDeviceStatus>
39
+ * }>} YotoAccountStatusUpdateEvent
40
+ */
41
+ /**
42
+ * Device config update event data
43
+ * @typedef {YotoAccountDeviceEvent<{
44
+ * config: YotoDeviceModelConfig,
45
+ * changedFields: Set<keyof YotoDeviceModelConfig>
46
+ * }>} YotoAccountConfigUpdateEvent
47
+ */
48
+ /**
49
+ * Device playback update event data
50
+ * @typedef {YotoAccountDeviceEvent<{
51
+ * playback: YotoPlaybackState,
52
+ * changedFields: Set<keyof YotoPlaybackState>
53
+ * }>} YotoAccountPlaybackUpdateEvent
54
+ */
55
+ /**
56
+ * Device online event data
57
+ * @typedef {YotoAccountDeviceEvent<{
58
+ * metadata: YotoDeviceOnlineMetadata
59
+ * }>} YotoAccountOnlineEvent
60
+ */
61
+ /**
62
+ * Device offline event data
63
+ * @typedef {YotoAccountDeviceEvent<{
64
+ * metadata: YotoDeviceOfflineMetadata
65
+ * }>} YotoAccountOfflineEvent
66
+ */
67
+ /**
68
+ * MQTT connect event data
69
+ * @typedef {YotoAccountDeviceEvent<{
70
+ * metadata: YotoMqttConnectMetadata
71
+ * }>} YotoAccountMqttConnectEvent
72
+ */
73
+ /**
74
+ * MQTT disconnect event data
75
+ * @typedef {YotoAccountDeviceEvent<{
76
+ * metadata: YotoMqttDisconnectMetadata
77
+ * }>} YotoAccountMqttDisconnectEvent
78
+ */
79
+ /**
80
+ * MQTT close event data
81
+ * @typedef {YotoAccountDeviceEvent<{
82
+ * metadata: YotoMqttCloseMetadata
83
+ * }>} YotoAccountMqttCloseEvent
84
+ */
85
+ /**
86
+ * MQTT reconnect event data
87
+ * @typedef {YotoAccountDeviceEvent<{}>} YotoAccountMqttReconnectEvent
88
+ */
89
+ /**
90
+ * MQTT offline event data
91
+ * @typedef {YotoAccountDeviceEvent<{}>} YotoAccountMqttOfflineEvent
92
+ */
93
+ /**
94
+ * MQTT end event data
95
+ * @typedef {YotoAccountDeviceEvent<{}>} YotoAccountMqttEndEvent
96
+ */
97
+ /**
98
+ * MQTT status event data
99
+ * @typedef {YotoAccountDeviceEvent<{
100
+ * topic: string,
101
+ * message: YotoStatusMessage
102
+ * }>} YotoAccountMqttStatusEvent
103
+ */
104
+ /**
105
+ * MQTT events event data
106
+ * @typedef {YotoAccountDeviceEvent<{
107
+ * topic: string,
108
+ * message: YotoEventsMessage
109
+ * }>} YotoAccountMqttEventsEvent
110
+ */
111
+ /**
112
+ * MQTT legacy status event data
113
+ * @typedef {YotoAccountDeviceEvent<{
114
+ * topic: string,
115
+ * message: YotoStatusLegacyMessage
116
+ * }>} YotoAccountMqttStatusLegacyEvent
117
+ */
118
+ /**
119
+ * MQTT response event data
120
+ * @typedef {YotoAccountDeviceEvent<{
121
+ * topic: string,
122
+ * message: YotoResponseMessage
123
+ * }>} YotoAccountMqttResponseEvent
124
+ */
125
+ /**
126
+ * MQTT unknown event data
127
+ * @typedef {YotoAccountDeviceEvent<{
128
+ * topic: string,
129
+ * message: unknown
130
+ * }>} YotoAccountMqttUnknownEvent
131
+ */
132
+ /**
133
+ * Account error event data
134
+ * @typedef {Object} YotoAccountErrorEvent
135
+ * @property {Error} error - Error instance
136
+ * @property {YotoAccountErrorContext} context - Error context
137
+ */
138
+ /**
139
+ * Device event handler set
140
+ * @typedef {Object} YotoAccountDeviceEventHandlers
141
+ * @property {(status: YotoDeviceStatus, source: string, changedFields: Set<keyof YotoDeviceStatus>) => void} statusUpdate
142
+ * @property {(config: YotoDeviceModelConfig, changedFields: Set<keyof YotoDeviceModelConfig>) => void} configUpdate
143
+ * @property {(playback: YotoPlaybackState, changedFields: Set<keyof YotoPlaybackState>) => void} playbackUpdate
144
+ * @property {(metadata: YotoDeviceOnlineMetadata) => void} online
145
+ * @property {(metadata: YotoDeviceOfflineMetadata) => void} offline
146
+ * @property {(metadata: YotoMqttConnectMetadata) => void} mqttConnect
147
+ * @property {(metadata: YotoMqttDisconnectMetadata) => void} mqttDisconnect
148
+ * @property {(metadata: YotoMqttCloseMetadata) => void} mqttClose
149
+ * @property {() => void} mqttReconnect
150
+ * @property {() => void} mqttOffline
151
+ * @property {() => void} mqttEnd
152
+ * @property {(topic: string, message: YotoStatusMessage) => void} mqttStatus
153
+ * @property {(topic: string, message: YotoEventsMessage) => void} mqttEvents
154
+ * @property {(topic: string, message: YotoStatusLegacyMessage) => void} mqttStatusLegacy
155
+ * @property {(topic: string, message: YotoResponseMessage) => void} mqttResponse
156
+ * @property {(topic: string, message: unknown) => void} mqttUnknown
157
+ */
20
158
  /**
21
159
  * Event map for YotoAccount
22
160
  * @typedef {{
23
161
  * 'started': [YotoAccountStartedMetadata],
24
162
  * 'stopped': [],
25
- * 'deviceAdded': [string, YotoDeviceModel],
26
- * 'deviceRemoved': [string],
27
- * 'error': [Error, YotoAccountErrorContext]
163
+ * 'deviceAdded': [YotoAccountDeviceAddedEvent],
164
+ * 'deviceRemoved': [YotoAccountDeviceRemovedEvent],
165
+ * 'statusUpdate': [YotoAccountStatusUpdateEvent],
166
+ * 'configUpdate': [YotoAccountConfigUpdateEvent],
167
+ * 'playbackUpdate': [YotoAccountPlaybackUpdateEvent],
168
+ * 'online': [YotoAccountOnlineEvent],
169
+ * 'offline': [YotoAccountOfflineEvent],
170
+ * 'mqttConnect': [YotoAccountMqttConnectEvent],
171
+ * 'mqttDisconnect': [YotoAccountMqttDisconnectEvent],
172
+ * 'mqttClose': [YotoAccountMqttCloseEvent],
173
+ * 'mqttReconnect': [YotoAccountMqttReconnectEvent],
174
+ * 'mqttOffline': [YotoAccountMqttOfflineEvent],
175
+ * 'mqttEnd': [YotoAccountMqttEndEvent],
176
+ * 'mqttStatus': [YotoAccountMqttStatusEvent],
177
+ * 'mqttEvents': [YotoAccountMqttEventsEvent],
178
+ * 'mqttStatusLegacy': [YotoAccountMqttStatusLegacyEvent],
179
+ * 'mqttResponse': [YotoAccountMqttResponseEvent],
180
+ * 'mqttUnknown': [YotoAccountMqttUnknownEvent],
181
+ * 'error': [YotoAccountErrorEvent]
28
182
  * }} YotoAccountEventMap
29
183
  */
30
184
  /**
@@ -33,9 +187,10 @@
33
187
  * Events:
34
188
  * - 'started' - Emitted when account starts, passes metadata with deviceCount and devices array
35
189
  * - 'stopped' - Emitted when account stops
36
- * - 'deviceAdded' - Emitted when a device is added, passes (deviceId, deviceModel)
37
- * - 'deviceRemoved' - Emitted when a device is removed, passes deviceId
38
- * - 'error' - Emitted when an error occurs, passes (error, context)
190
+ * - 'deviceAdded' - Emitted when a device is added, passes { deviceId }
191
+ * - 'deviceRemoved' - Emitted when a device is removed, passes { deviceId }
192
+ * - Device events are re-emitted with device context, see event map for signatures
193
+ * - 'error' - Emitted when an error occurs, passes { error, context }
39
194
  *
40
195
  * Note: To listen to individual device events (statusUpdate, configUpdate, playbackUpdate, online, offline, etc.),
41
196
  * access the device models directly via account.devices or account.getDevice(deviceId) and attach listeners.
@@ -145,19 +300,194 @@ export type YotoAccountStartedMetadata = {
145
300
  */
146
301
  devices: string[];
147
302
  };
303
+ /**
304
+ * Device event wrapper
305
+ */
306
+ export type YotoAccountDeviceEvent<T> = {
307
+ deviceId: string;
308
+ } & T;
309
+ /**
310
+ * Device added event data
311
+ */
312
+ export type YotoAccountDeviceAddedEvent = YotoAccountDeviceEvent<{}>;
313
+ /**
314
+ * Device removed event data
315
+ */
316
+ export type YotoAccountDeviceRemovedEvent = YotoAccountDeviceEvent<{}>;
317
+ /**
318
+ * Device status update event data
319
+ */
320
+ export type YotoAccountStatusUpdateEvent = YotoAccountDeviceEvent<{
321
+ status: YotoDeviceStatus;
322
+ source: string;
323
+ changedFields: Set<keyof YotoDeviceStatus>;
324
+ }>;
325
+ /**
326
+ * Device config update event data
327
+ */
328
+ export type YotoAccountConfigUpdateEvent = YotoAccountDeviceEvent<{
329
+ config: YotoDeviceModelConfig;
330
+ changedFields: Set<keyof YotoDeviceModelConfig>;
331
+ }>;
332
+ /**
333
+ * Device playback update event data
334
+ */
335
+ export type YotoAccountPlaybackUpdateEvent = YotoAccountDeviceEvent<{
336
+ playback: YotoPlaybackState;
337
+ changedFields: Set<keyof YotoPlaybackState>;
338
+ }>;
339
+ /**
340
+ * Device online event data
341
+ */
342
+ export type YotoAccountOnlineEvent = YotoAccountDeviceEvent<{
343
+ metadata: YotoDeviceOnlineMetadata;
344
+ }>;
345
+ /**
346
+ * Device offline event data
347
+ */
348
+ export type YotoAccountOfflineEvent = YotoAccountDeviceEvent<{
349
+ metadata: YotoDeviceOfflineMetadata;
350
+ }>;
351
+ /**
352
+ * MQTT connect event data
353
+ */
354
+ export type YotoAccountMqttConnectEvent = YotoAccountDeviceEvent<{
355
+ metadata: YotoMqttConnectMetadata;
356
+ }>;
357
+ /**
358
+ * MQTT disconnect event data
359
+ */
360
+ export type YotoAccountMqttDisconnectEvent = YotoAccountDeviceEvent<{
361
+ metadata: YotoMqttDisconnectMetadata;
362
+ }>;
363
+ /**
364
+ * MQTT close event data
365
+ */
366
+ export type YotoAccountMqttCloseEvent = YotoAccountDeviceEvent<{
367
+ metadata: YotoMqttCloseMetadata;
368
+ }>;
369
+ /**
370
+ * MQTT reconnect event data
371
+ */
372
+ export type YotoAccountMqttReconnectEvent = YotoAccountDeviceEvent<{}>;
373
+ /**
374
+ * MQTT offline event data
375
+ */
376
+ export type YotoAccountMqttOfflineEvent = YotoAccountDeviceEvent<{}>;
377
+ /**
378
+ * MQTT end event data
379
+ */
380
+ export type YotoAccountMqttEndEvent = YotoAccountDeviceEvent<{}>;
381
+ /**
382
+ * MQTT status event data
383
+ */
384
+ export type YotoAccountMqttStatusEvent = YotoAccountDeviceEvent<{
385
+ topic: string;
386
+ message: YotoStatusMessage;
387
+ }>;
388
+ /**
389
+ * MQTT events event data
390
+ */
391
+ export type YotoAccountMqttEventsEvent = YotoAccountDeviceEvent<{
392
+ topic: string;
393
+ message: YotoEventsMessage;
394
+ }>;
395
+ /**
396
+ * MQTT legacy status event data
397
+ */
398
+ export type YotoAccountMqttStatusLegacyEvent = YotoAccountDeviceEvent<{
399
+ topic: string;
400
+ message: YotoStatusLegacyMessage;
401
+ }>;
402
+ /**
403
+ * MQTT response event data
404
+ */
405
+ export type YotoAccountMqttResponseEvent = YotoAccountDeviceEvent<{
406
+ topic: string;
407
+ message: YotoResponseMessage;
408
+ }>;
409
+ /**
410
+ * MQTT unknown event data
411
+ */
412
+ export type YotoAccountMqttUnknownEvent = YotoAccountDeviceEvent<{
413
+ topic: string;
414
+ message: unknown;
415
+ }>;
416
+ /**
417
+ * Account error event data
418
+ */
419
+ export type YotoAccountErrorEvent = {
420
+ /**
421
+ * - Error instance
422
+ */
423
+ error: Error;
424
+ /**
425
+ * - Error context
426
+ */
427
+ context: YotoAccountErrorContext;
428
+ };
429
+ /**
430
+ * Device event handler set
431
+ */
432
+ export type YotoAccountDeviceEventHandlers = {
433
+ statusUpdate: (status: YotoDeviceStatus, source: string, changedFields: Set<keyof YotoDeviceStatus>) => void;
434
+ configUpdate: (config: YotoDeviceModelConfig, changedFields: Set<keyof YotoDeviceModelConfig>) => void;
435
+ playbackUpdate: (playback: YotoPlaybackState, changedFields: Set<keyof YotoPlaybackState>) => void;
436
+ online: (metadata: YotoDeviceOnlineMetadata) => void;
437
+ offline: (metadata: YotoDeviceOfflineMetadata) => void;
438
+ mqttConnect: (metadata: YotoMqttConnectMetadata) => void;
439
+ mqttDisconnect: (metadata: YotoMqttDisconnectMetadata) => void;
440
+ mqttClose: (metadata: YotoMqttCloseMetadata) => void;
441
+ mqttReconnect: () => void;
442
+ mqttOffline: () => void;
443
+ mqttEnd: () => void;
444
+ mqttStatus: (topic: string, message: YotoStatusMessage) => void;
445
+ mqttEvents: (topic: string, message: YotoEventsMessage) => void;
446
+ mqttStatusLegacy: (topic: string, message: YotoStatusLegacyMessage) => void;
447
+ mqttResponse: (topic: string, message: YotoResponseMessage) => void;
448
+ mqttUnknown: (topic: string, message: unknown) => void;
449
+ };
148
450
  /**
149
451
  * Event map for YotoAccount
150
452
  */
151
453
  export type YotoAccountEventMap = {
152
454
  "started": [YotoAccountStartedMetadata];
153
455
  "stopped": [];
154
- "deviceAdded": [string, YotoDeviceModel];
155
- "deviceRemoved": [string];
156
- "error": [Error, YotoAccountErrorContext];
456
+ "deviceAdded": [YotoAccountDeviceAddedEvent];
457
+ "deviceRemoved": [YotoAccountDeviceRemovedEvent];
458
+ "statusUpdate": [YotoAccountStatusUpdateEvent];
459
+ "configUpdate": [YotoAccountConfigUpdateEvent];
460
+ "playbackUpdate": [YotoAccountPlaybackUpdateEvent];
461
+ "online": [YotoAccountOnlineEvent];
462
+ "offline": [YotoAccountOfflineEvent];
463
+ "mqttConnect": [YotoAccountMqttConnectEvent];
464
+ "mqttDisconnect": [YotoAccountMqttDisconnectEvent];
465
+ "mqttClose": [YotoAccountMqttCloseEvent];
466
+ "mqttReconnect": [YotoAccountMqttReconnectEvent];
467
+ "mqttOffline": [YotoAccountMqttOfflineEvent];
468
+ "mqttEnd": [YotoAccountMqttEndEvent];
469
+ "mqttStatus": [YotoAccountMqttStatusEvent];
470
+ "mqttEvents": [YotoAccountMqttEventsEvent];
471
+ "mqttStatusLegacy": [YotoAccountMqttStatusLegacyEvent];
472
+ "mqttResponse": [YotoAccountMqttResponseEvent];
473
+ "mqttUnknown": [YotoAccountMqttUnknownEvent];
474
+ "error": [YotoAccountErrorEvent];
157
475
  };
158
476
  import { EventEmitter } from 'events';
159
477
  import { YotoClient } from './api-client.js';
160
478
  import { YotoDeviceModel } from './yoto-device.js';
161
479
  import type { YotoClientConstructorOptions } from './api-client.js';
162
480
  import type { YotoDeviceModelOptions } from './yoto-device.js';
481
+ import type { YotoDeviceStatus } from './yoto-device.js';
482
+ import type { YotoDeviceModelConfig } from './yoto-device.js';
483
+ import type { YotoPlaybackState } from './yoto-device.js';
484
+ import type { YotoDeviceOnlineMetadata } from './yoto-device.js';
485
+ import type { YotoDeviceOfflineMetadata } from './yoto-device.js';
486
+ import type { YotoMqttConnectMetadata } from './yoto-device.js';
487
+ import type { YotoMqttDisconnectMetadata } from './yoto-device.js';
488
+ import type { YotoMqttCloseMetadata } from './yoto-device.js';
489
+ import type { YotoStatusMessage } from './mqtt/client.js';
490
+ import type { YotoEventsMessage } from './mqtt/client.js';
491
+ import type { YotoStatusLegacyMessage } from './mqtt/client.js';
492
+ import type { YotoResponseMessage } from './mqtt/client.js';
163
493
  //# sourceMappingURL=yoto-account.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"yoto-account.d.ts","sourceRoot":"","sources":["yoto-account.js"],"names":[],"mappings":"AAaA;;;;;GAKG;AAEH;;;;;;GAMG;AAEH;;;;;GAKG;AAEH;;;;;;;;;GASG;AAMH;;;;;;;;;;;;;;GAcG;AACH;IAOE;;;QAGI;IACJ,qBAFY,kBAAkB,EAS7B;IAMD;;;OAGG;IACH,cAFa,UAAU,CAEc;IAErC;;;OAGG;IACH,eAFa,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAEF;IAEvC;;;OAGG;IACH,eAFa,OAAO,CAEmB;IAEvC;;;OAGG;IACH,mBAFa,OAAO,CAE2B;IAE/C;;;;OAIG;IACH,oBAHW,MAAM,GACJ,eAAe,GAAG,SAAS,CAEmB;IAE3D;;;OAGG;IACH,gBAFa,MAAM,EAAE,CAEsC;IAM3D;;;;OAIG;IACH,SAHa,OAAO,CAAC,IAAI,CAAC,CAyDzB;IAED;;;OAGG;IACH,QAFa,OAAO,CAAC,IAAI,CAAC,CAwCzB;IAED;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,CAAC,CAKzB;IAED;;;OAGG;IACH,kBAFa,OAAO,CAAC,IAAI,CAAC,CAwEzB;;CAqBF;;;;;;;;mBAnUa,4BAA4B;;;;mBAC5B,sBAAsB;;;;;;;;;YAMtB,MAAM;;;;eACN,MAAM;;;;gBACN,MAAM;;;;;;;;;iBAMN,MAAM;;;;aACN,MAAM,EAAE;;;;;kCAKT;IACZ,SAAa,EAAE,CAAC,0BAA0B,CAAC,CAAC;IAC5C,SAAa,EAAE,EAAE,CAAC;IAClB,aAAiB,EAAE,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC7C,eAAmB,EAAE,CAAC,MAAM,CAAC,CAAC;IAC9B,OAAW,EAAE,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAA;CAC1C;6BAtCyB,QAAQ;2BACV,iBAAiB;gCACZ,kBAAkB;kDAND,iBAAiB;4CACvB,kBAAkB"}
1
+ {"version":3,"file":"yoto-account.d.ts","sourceRoot":"","sources":["yoto-account.js"],"names":[],"mappings":"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"}