yoto-nodejs-client 0.0.2 → 0.0.4

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 (75) hide show
  1. package/README.md +523 -30
  2. package/bin/auth.js +36 -46
  3. package/bin/content.js +0 -0
  4. package/bin/device-model.d.ts +3 -0
  5. package/bin/device-model.d.ts.map +1 -0
  6. package/bin/device-model.js +360 -0
  7. package/bin/device-tui.TODO.md +125 -0
  8. package/bin/device-tui.d.ts +31 -0
  9. package/bin/device-tui.d.ts.map +1 -0
  10. package/bin/device-tui.js +1123 -0
  11. package/bin/devices.js +166 -28
  12. package/bin/groups.js +0 -0
  13. package/bin/icons.js +0 -0
  14. package/bin/lib/cli-helpers.d.ts +1 -1
  15. package/bin/lib/cli-helpers.d.ts.map +1 -1
  16. package/bin/lib/cli-helpers.js +5 -5
  17. package/bin/refresh-token.js +6 -6
  18. package/bin/token-info.js +3 -3
  19. package/index.d.ts +4 -585
  20. package/index.d.ts.map +1 -1
  21. package/index.js +11 -689
  22. package/lib/api-client.d.ts +576 -0
  23. package/lib/api-client.d.ts.map +1 -0
  24. package/lib/api-client.js +681 -0
  25. package/lib/api-endpoints/auth.d.ts +199 -8
  26. package/lib/api-endpoints/auth.d.ts.map +1 -1
  27. package/lib/api-endpoints/auth.js +224 -7
  28. package/lib/api-endpoints/auth.test.js +54 -2
  29. package/lib/api-endpoints/constants.d.ts +14 -8
  30. package/lib/api-endpoints/constants.d.ts.map +1 -1
  31. package/lib/api-endpoints/constants.js +17 -10
  32. package/lib/api-endpoints/content.test.js +1 -1
  33. package/lib/api-endpoints/devices.d.ts +405 -117
  34. package/lib/api-endpoints/devices.d.ts.map +1 -1
  35. package/lib/api-endpoints/devices.js +114 -52
  36. package/lib/api-endpoints/devices.test.js +1 -1
  37. package/lib/api-endpoints/{test-helpers.d.ts → endpoint-test-helpers.d.ts} +1 -1
  38. package/lib/api-endpoints/endpoint-test-helpers.d.ts.map +1 -0
  39. package/lib/api-endpoints/family-library-groups.test.js +1 -1
  40. package/lib/api-endpoints/family.test.js +1 -1
  41. package/lib/api-endpoints/icons.test.js +1 -1
  42. package/lib/helpers/power-state.d.ts +53 -0
  43. package/lib/helpers/power-state.d.ts.map +1 -0
  44. package/lib/helpers/power-state.js +73 -0
  45. package/lib/helpers/power-state.test.js +100 -0
  46. package/lib/helpers/temperature.d.ts +24 -0
  47. package/lib/helpers/temperature.d.ts.map +1 -0
  48. package/lib/helpers/temperature.js +61 -0
  49. package/lib/helpers/temperature.test.js +58 -0
  50. package/lib/helpers/typed-keys.d.ts +7 -0
  51. package/lib/helpers/typed-keys.d.ts.map +1 -0
  52. package/lib/helpers/typed-keys.js +8 -0
  53. package/lib/mqtt/client.d.ts +348 -22
  54. package/lib/mqtt/client.d.ts.map +1 -1
  55. package/lib/mqtt/client.js +213 -31
  56. package/lib/mqtt/factory.d.ts +22 -4
  57. package/lib/mqtt/factory.d.ts.map +1 -1
  58. package/lib/mqtt/factory.js +27 -5
  59. package/lib/mqtt/mqtt.test.js +85 -28
  60. package/lib/mqtt/topics.d.ts +41 -13
  61. package/lib/mqtt/topics.d.ts.map +1 -1
  62. package/lib/mqtt/topics.js +54 -20
  63. package/lib/pkg.d.cts +8 -0
  64. package/lib/token.d.ts +21 -6
  65. package/lib/token.d.ts.map +1 -1
  66. package/lib/token.js +30 -23
  67. package/lib/yoto-account.d.ts +163 -0
  68. package/lib/yoto-account.d.ts.map +1 -0
  69. package/lib/yoto-account.js +340 -0
  70. package/lib/yoto-device.d.ts +656 -0
  71. package/lib/yoto-device.d.ts.map +1 -0
  72. package/lib/yoto-device.js +2850 -0
  73. package/package.json +21 -15
  74. package/lib/api-endpoints/test-helpers.d.ts.map +0 -1
  75. /package/lib/api-endpoints/{test-helpers.js → endpoint-test-helpers.js} +0 -0
package/index.js CHANGED
@@ -1,689 +1,11 @@
1
- /**
2
- * @import { YotoContentResponse, YotoCreateOrUpdateContentRequest, YotoMyoContentResponse, YotoCreateOrUpdateContentResponse, YotoDeleteContentResponse } from './lib/api-endpoints/content.js'
3
- * @import { YotoDevicesResponse, YotoDeviceStatusResponse, YotoDeviceConfigResponse, YotoUpdateDeviceConfigRequest, YotoUpdateDeviceConfigResponse, YotoUpdateShortcutsRequest, YotoUpdateShortcutsResponse, YotoDeviceCommand, YotoDeviceCommandResponse } from './lib/api-endpoints/devices.js'
4
- * @import { YotoGroup, YotoCreateGroupRequest, YotoUpdateGroupRequest, YotoDeleteGroupResponse } from './lib/api-endpoints/family-library-groups.js'
5
- * @import { YotoFamilyImagesResponse, YotoFamilyImageResponse, YotoUploadFamilyImageResponse } from './lib/api-endpoints/family.js'
6
- * @import { YotoPublicIconsResponse, YotoUserIconsResponse, YotoUploadIconResponse } from './lib/api-endpoints/icons.js'
7
- * @import { YotoAudioUploadUrlResponse, YotoUploadCoverImageResponse, YotoCoverType } from './lib/api-endpoints/media.js'
8
- * @import { YotoTokenResponse, YotoDeviceCodeResponse } from './lib/api-endpoints/auth.js'
9
- * @import { YotoMqttClient } from './lib/mqtt/client.js'
10
- * @import { RequestOptions } from './lib/api-endpoints/helpers.js'
11
- */
12
-
13
- import { RefreshableToken } from './lib/token.js'
14
- import * as Auth from './lib/api-endpoints/auth.js'
15
- import * as Content from './lib/api-endpoints/content.js'
16
- import * as Devices from './lib/api-endpoints/devices.js'
17
- import * as FamilyLibraryGroups from './lib/api-endpoints/family-library-groups.js'
18
- import * as Family from './lib/api-endpoints/family.js'
19
- import * as Icons from './lib/api-endpoints/icons.js'
20
- import * as Media from './lib/api-endpoints/media.js'
21
- import { createYotoMqttClient } from './lib/mqtt/index.js'
22
-
23
- /**
24
- * @typedef {Object} RefreshSuccessEvent
25
- * @property {string} clientId - The OAuth client ID
26
- * @property {string} accessToken - The new access token
27
- * @property {string} refreshToken - The refresh token (may be updated)
28
- * @property {number} expiresAt - Unix timestamp in seconds when token expires
29
- */
30
-
31
- /**
32
- * @typedef {Object} YotoClientConstructorOptions
33
- * @property {string} clientId - OAuth client ID
34
- * @property {string} refreshToken - OAuth refresh token
35
- * @property {string} accessToken - Initial OAuth access token (JWT)
36
- * @property {(event: RefreshSuccessEvent) => void | Promise<void>} onTokenRefresh - **REQUIRED** Callback invoked when tokens are refreshed. You MUST persist these tokens (to file, database, etc.) as the refresh can happen at any time during API calls. The refresh token may be rotated by the auth server. **DO NOT STUB THIS CALLBACK** - always implement proper persistence logic.
37
- * @property {number} [bufferSeconds=30] - Seconds before expiration to consider token expired
38
- * @property {() => void | Promise<void>} [onRefreshStart] - Optional callback invoked when token refresh starts. Defaults to console.log.
39
- * @property {(error: Error) => void | Promise<void>} [onRefreshError] - Optional callback invoked when token refresh fails with a transient error. Defaults to console.warn.
40
- * @property {(error: Error) => void | Promise<void>} [onInvalid] - Optional callback invoked when refresh token is permanently invalid. Defaults to console.error.
41
- * @property {string} [userAgent] - Optional user agent string to identify your application
42
- * @property {RequestOptions} [defaultRequestOptions] - Default undici request options for all requests (dispatcher, timeouts, etc.)
43
- */
44
-
45
- /**
46
- * Yoto API Client with automatic token refresh
47
- */
48
- export class YotoClient {
49
- // ============================================================================
50
- // Authentication Static Methods
51
- // ============================================================================
52
-
53
- /**
54
- * Get authorization URL for browser-based OAuth flow
55
- * @see https://yoto.dev/api/get-authorize/
56
- * @param {object} params
57
- * @param {string} params.clientId - OAuth client ID
58
- * @param {string} params.redirectUri - Redirect URI after authorization
59
- * @param {'code' | 'token' | 'id_token' | 'code token' | 'code id_token' | 'token id_token' | 'code token id_token'} params.responseType - OAuth response type
60
- * @param {string} params.state - State parameter for CSRF protection
61
- * @param {string} [params.audience] - Audience for the token
62
- * @param {string} [params.scope] - Requested scopes
63
- * @param {string} [params.nonce] - Nonce for replay attack prevention
64
- * @param {'none' | 'login' | 'consent' | 'select_account'} [params.prompt] - Authorization prompt behavior
65
- * @param {number} [params.maxAge] - Maximum authentication age in seconds
66
- * @param {string} [params.codeChallenge] - PKCE code challenge
67
- * @param {'S256' | 'plain'} [params.codeChallengeMethod] - PKCE code challenge method
68
- * @returns {string} Authorization URL
69
- */
70
- static getAuthorizeUrl (params) {
71
- return Auth.getAuthorizeUrl(params)
72
- }
73
-
74
- /**
75
- * Exchange authorization code or refresh token for access tokens
76
- * @see https://yoto.dev/api/post-oauth-token/
77
- * @param {object} params
78
- * @param {'authorization_code' | 'refresh_token' | 'client_credentials' | 'urn:ietf:params:oauth:grant-type:device_code'} params.grantType - OAuth grant type
79
- * @param {string} [params.code] - Authorization code (required for authorization_code grant)
80
- * @param {string} [params.redirectUri] - Redirect URI (required for authorization_code grant if used in authorize request)
81
- * @param {string} [params.refreshToken] - Refresh token (required for refresh_token grant)
82
- * @param {string} [params.clientId] - OAuth client ID
83
- * @param {string} [params.clientSecret] - OAuth client secret
84
- * @param {string} [params.scope] - Requested scope
85
- * @param {string} [params.codeVerifier] - PKCE code verifier
86
- * @param {string} [params.deviceCode] - Device code (required for device_code grant)
87
- * @param {string} [params.audience] - Audience for the token
88
- * @returns {Promise<YotoTokenResponse>}
89
- */
90
- static async exchangeToken (params) {
91
- return await Auth.exchangeToken(params)
92
- }
93
-
94
- /**
95
- * Request device code for device authorization flow
96
- * @see https://yoto.dev/api/post-oauth-device-code/
97
- * @param {object} params
98
- * @param {string} params.clientId - OAuth client ID
99
- * @param {string} [params.scope] - Requested scopes
100
- * @param {string} [params.audience] - Audience for the token
101
- * @returns {Promise<YotoDeviceCodeResponse>}
102
- */
103
- static async requestDeviceCode (params) {
104
- return await Auth.requestDeviceCode(params)
105
- }
106
-
107
- // ============================================================================
108
- // Instance Properties and Constructor
109
- // ============================================================================
110
- /** @type {RefreshableToken} */
111
- #token
112
-
113
- /** @type {string | undefined} */
114
- #userAgent
115
-
116
- /** @type {RequestOptions | undefined} */
117
- #defaultRequestOptions
118
-
119
- /**
120
- * Create a new Yoto API client
121
- * @param {YotoClientConstructorOptions} options
122
- */
123
- constructor ({
124
- clientId,
125
- refreshToken,
126
- accessToken,
127
- onTokenRefresh,
128
- bufferSeconds,
129
- onRefreshStart,
130
- onRefreshError,
131
- onInvalid,
132
- userAgent,
133
- defaultRequestOptions
134
- }) {
135
- if (!onTokenRefresh) {
136
- throw new Error('onTokenRefresh callback is required. You must persist refreshed tokens as they can be updated at any time.')
137
- }
138
-
139
- this.#token = new RefreshableToken({
140
- clientId,
141
- refreshToken,
142
- accessToken,
143
- ...(bufferSeconds !== undefined && { bufferSeconds })
144
- })
145
-
146
- this.#userAgent = userAgent
147
- this.#defaultRequestOptions = defaultRequestOptions
148
-
149
- // Listen for token refresh events and call the user's callbacks
150
- this.#token.on('refresh:success', onTokenRefresh)
151
-
152
- this.#token.on('refresh:start', onRefreshStart || (() => {
153
- console.log('Token refresh started')
154
- }))
155
-
156
- this.#token.on('refresh:error', onRefreshError || ((error) => {
157
- console.warn('Token refresh failed (transient error):', error.message)
158
- }))
159
-
160
- this.#token.on('invalid', onInvalid || ((error) => {
161
- console.error('Refresh token is permanently invalid:', error.message)
162
- }))
163
- }
164
-
165
- /**
166
- * Get the underlying RefreshableToken instance
167
- * @returns {RefreshableToken}
168
- */
169
- get token () {
170
- return this.#token
171
- }
172
- // ============================================================================
173
- // Content API
174
- // ============================================================================
175
-
176
- /**
177
- * Get content/card details
178
- * @see https://yoto.dev/api/getcontent/
179
- * @param {object} params
180
- * @param {string} params.cardId - The card/content ID
181
- * @param {string} [params.timezone] - Timezone for schedule-based content
182
- * @param {'full' | 'pre'} [params.signingType] - Type of URL signing
183
- * @param {boolean} [params.playable] - Whether to include playback URLs
184
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
185
- * @returns {Promise<YotoContentResponse>}
186
- */
187
- async getContent ({ cardId, timezone, signingType, playable, requestOptions }) {
188
- const accessToken = await this.#token.getAccessToken()
189
- return await Content.getContent({
190
- accessToken,
191
- userAgent: this.#userAgent,
192
- requestOptions: requestOptions || this.#defaultRequestOptions,
193
- cardId,
194
- timezone,
195
- signingType,
196
- playable
197
- })
198
- }
199
-
200
- /**
201
- * Get user's MYO (Make Your Own) content
202
- * @see https://yoto.dev/api/getusersmyocontent/
203
- * @param {object} [params]
204
- * @param {boolean} [params.showDeleted=false] - Include deleted content
205
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
206
- * @returns {Promise<YotoMyoContentResponse>}
207
- */
208
- async getUserMyoContent ({ showDeleted = false, requestOptions } = {}) {
209
- const accessToken = await this.#token.getAccessToken()
210
- return await Content.getUserMyoContent({
211
- accessToken,
212
- userAgent: this.#userAgent,
213
- requestOptions: requestOptions || this.#defaultRequestOptions,
214
- showDeleted
215
- })
216
- }
217
-
218
- /**
219
- * Create or update content/card
220
- * @see https://yoto.dev/api/createorupdatecontent/
221
- * @param {object} params
222
- * @param {YotoCreateOrUpdateContentRequest} params.content - Content data to create/update
223
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
224
- * @returns {Promise<YotoCreateOrUpdateContentResponse>}
225
- */
226
- async createOrUpdateContent ({ content, requestOptions }) {
227
- const accessToken = await this.#token.getAccessToken()
228
- return await Content.createOrUpdateContent({
229
- accessToken,
230
- userAgent: this.#userAgent,
231
- requestOptions: requestOptions || this.#defaultRequestOptions,
232
- content
233
- })
234
- }
235
-
236
- /**
237
- * Delete content/card
238
- * @see https://yoto.dev/api/deletecontent/
239
- * @param {object} params
240
- * @param {string} params.cardId - The card/content ID to delete
241
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
242
- * @returns {Promise<YotoDeleteContentResponse>}
243
- */
244
- async deleteContent ({ cardId, requestOptions }) {
245
- const accessToken = await this.#token.getAccessToken()
246
- return await Content.deleteContent({
247
- accessToken,
248
- userAgent: this.#userAgent,
249
- requestOptions: requestOptions || this.#defaultRequestOptions,
250
- cardId
251
- })
252
- }
253
-
254
- // ============================================================================
255
- // Devices API
256
- // ============================================================================
257
-
258
- /**
259
- * Get all devices for authenticated user
260
- * @see https://yoto.dev/api/getdevices/
261
- * @param {object} [params]
262
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
263
- * @returns {Promise<YotoDevicesResponse>}
264
- */
265
- async getDevices ({ requestOptions } = {}) {
266
- const accessToken = await this.#token.getAccessToken()
267
- return await Devices.getDevices({ accessToken, userAgent: this.#userAgent, requestOptions: requestOptions || this.#defaultRequestOptions })
268
- }
269
-
270
- /**
271
- * Get device status
272
- * @see https://yoto.dev/api/getdevicestatus/
273
- * @param {object} params
274
- * @param {string} params.deviceId - Device ID
275
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
276
- * @returns {Promise<YotoDeviceStatusResponse>}
277
- */
278
- async getDeviceStatus ({ deviceId, requestOptions }) {
279
- const accessToken = await this.#token.getAccessToken()
280
- return await Devices.getDeviceStatus({
281
- accessToken,
282
- userAgent: this.#userAgent,
283
- requestOptions: requestOptions || this.#defaultRequestOptions,
284
- deviceId
285
- })
286
- }
287
-
288
- /**
289
- * Get device configuration
290
- * @see https://yoto.dev/api/getdeviceconfig/
291
- * @param {object} params
292
- * @param {string} params.deviceId - Device ID
293
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
294
- * @returns {Promise<YotoDeviceConfigResponse>}
295
- */
296
- async getDeviceConfig ({ deviceId, requestOptions }) {
297
- const accessToken = await this.#token.getAccessToken()
298
- return await Devices.getDeviceConfig({
299
- accessToken,
300
- userAgent: this.#userAgent,
301
- requestOptions: requestOptions || this.#defaultRequestOptions,
302
- deviceId
303
- })
304
- }
305
-
306
- /**
307
- * Update device configuration
308
- * @see https://yoto.dev/api/updatedeviceconfig/
309
- * @param {object} params
310
- * @param {string} params.deviceId - Device ID
311
- * @param {YotoUpdateDeviceConfigRequest} params.configUpdate - Config updates
312
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
313
- * @returns {Promise<YotoUpdateDeviceConfigResponse>}
314
- */
315
- async updateDeviceConfig ({ deviceId, configUpdate, requestOptions }) {
316
- const accessToken = await this.#token.getAccessToken()
317
- return await Devices.updateDeviceConfig({
318
- accessToken,
319
- userAgent: this.#userAgent,
320
- requestOptions: requestOptions || this.#defaultRequestOptions,
321
- deviceId,
322
- configUpdate
323
- })
324
- }
325
-
326
- /**
327
- * Update device shortcuts
328
- * @see https://yoto.dev/api/updateshortcutsbeta/
329
- * @param {object} params
330
- * @param {string} params.deviceId - Device ID
331
- * @param {YotoUpdateShortcutsRequest} params.shortcutsUpdate - Shortcuts config
332
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
333
- * @returns {Promise<YotoUpdateShortcutsResponse>}
334
- */
335
- async updateDeviceShortcuts ({ deviceId, shortcutsUpdate, requestOptions }) {
336
- const accessToken = await this.#token.getAccessToken()
337
- return await Devices.updateDeviceShortcuts({
338
- accessToken,
339
- userAgent: this.#userAgent,
340
- requestOptions: requestOptions || this.#defaultRequestOptions,
341
- deviceId,
342
- shortcutsUpdate
343
- })
344
- }
345
-
346
- /**
347
- * Send command to device
348
- * @see https://yoto.dev/api/senddevicecommand/
349
- * @see https://yoto.dev/players-mqtt/mqtt-docs/
350
- * @param {object} params
351
- * @param {string} params.deviceId - Device ID
352
- * @param {YotoDeviceCommand} params.command - Command to send
353
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
354
- * @returns {Promise<YotoDeviceCommandResponse>}
355
- */
356
- async sendDeviceCommand ({ deviceId, command, requestOptions }) {
357
- const accessToken = await this.#token.getAccessToken()
358
- return await Devices.sendDeviceCommand({
359
- accessToken,
360
- userAgent: this.#userAgent,
361
- requestOptions: requestOptions || this.#defaultRequestOptions,
362
- deviceId,
363
- command
364
- })
365
- }
366
-
367
- // ============================================================================
368
- // Family Library Groups API
369
- // ============================================================================
370
-
371
- /**
372
- * Get all family library groups
373
- * @see https://yoto.dev/api/getgroups/
374
- * @param {object} [params]
375
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
376
- * @returns {Promise<YotoGroup[]>}
377
- */
378
- async getGroups ({ requestOptions } = {}) {
379
- const accessToken = await this.#token.getAccessToken()
380
- return await FamilyLibraryGroups.getGroups({ accessToken, userAgent: this.#userAgent, requestOptions: requestOptions || this.#defaultRequestOptions })
381
- }
382
-
383
- /**
384
- * Create a family library group
385
- * @see https://yoto.dev/api/createagroup/
386
- * @param {object} params
387
- * @param {YotoCreateGroupRequest} params.group - Group data
388
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
389
- * @returns {Promise<YotoGroup>}
390
- */
391
- async createGroup ({ group, requestOptions }) {
392
- const accessToken = await this.#token.getAccessToken()
393
- return await FamilyLibraryGroups.createGroup({
394
- token: accessToken,
395
- userAgent: this.#userAgent,
396
- requestOptions: requestOptions || this.#defaultRequestOptions,
397
- group
398
- })
399
- }
400
-
401
- /**
402
- * Get a specific family library group
403
- * @see https://yoto.dev/api/getgroup/
404
- * @param {object} params
405
- * @param {string} params.groupId - Group ID
406
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
407
- * @returns {Promise<YotoGroup>}
408
- */
409
- async getGroup ({ groupId, requestOptions }) {
410
- const accessToken = await this.#token.getAccessToken()
411
- return await FamilyLibraryGroups.getGroup({
412
- accessToken,
413
- userAgent: this.#userAgent,
414
- requestOptions: requestOptions || this.#defaultRequestOptions,
415
- groupId
416
- })
417
- }
418
-
419
- /**
420
- * Update a family library group
421
- * @see https://yoto.dev/api/updateagroup/
422
- * @param {object} params
423
- * @param {string} params.groupId - Group ID
424
- * @param {YotoUpdateGroupRequest} params.group - Updated group data
425
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
426
- * @returns {Promise<YotoGroup>}
427
- */
428
- async updateGroup ({ groupId, group, requestOptions }) {
429
- const accessToken = await this.#token.getAccessToken()
430
- return await FamilyLibraryGroups.updateGroup({
431
- accessToken,
432
- userAgent: this.#userAgent,
433
- requestOptions: requestOptions || this.#defaultRequestOptions,
434
- groupId,
435
- group
436
- })
437
- }
438
-
439
- /**
440
- * Delete a family library group
441
- * @see https://yoto.dev/api/deleteagroup/
442
- * @param {object} params
443
- * @param {string} params.groupId - Group ID
444
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
445
- * @returns {Promise<YotoDeleteGroupResponse>}
446
- */
447
- async deleteGroup ({ groupId, requestOptions }) {
448
- const accessToken = await this.#token.getAccessToken()
449
- return await FamilyLibraryGroups.deleteGroup({
450
- accessToken,
451
- userAgent: this.#userAgent,
452
- requestOptions: requestOptions || this.#defaultRequestOptions,
453
- groupId
454
- })
455
- }
456
-
457
- // ============================================================================
458
- // Family Images API
459
- // ============================================================================
460
-
461
- /**
462
- * Get all family images
463
- * @see https://yoto.dev/api/getfamilyimages/
464
- * @param {object} [params]
465
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
466
- * @returns {Promise<YotoFamilyImagesResponse>}
467
- */
468
- async getFamilyImages ({ requestOptions } = {}) {
469
- const accessToken = await this.#token.getAccessToken()
470
- return await Family.getFamilyImages({ accessToken, userAgent: this.#userAgent, requestOptions: requestOptions || this.#defaultRequestOptions })
471
- }
472
-
473
- /**
474
- * Get a specific family image
475
- * @see https://yoto.dev/api/getafamilyimage/
476
- * @param {object} params
477
- * @param {string} params.imageId - Image ID
478
- * @param {'640x480' | '320x320'} params.size - Image size
479
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
480
- * @returns {Promise<YotoFamilyImageResponse>}
481
- */
482
- async getAFamilyImage ({ imageId, size, requestOptions }) {
483
- const accessToken = await this.#token.getAccessToken()
484
- return await Family.getAFamilyImage({
485
- accessToken,
486
- userAgent: this.#userAgent,
487
- requestOptions: requestOptions || this.#defaultRequestOptions,
488
- imageId,
489
- size
490
- })
491
- }
492
-
493
- /**
494
- * Upload a family image
495
- * @see https://yoto.dev/api/uploadafamilyimage/
496
- * @param {object} params
497
- * @param {Buffer} params.imageData - Image binary data
498
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
499
- * @returns {Promise<YotoUploadFamilyImageResponse>}
500
- */
501
- async uploadAFamilyImage ({ imageData, requestOptions }) {
502
- const accessToken = await this.#token.getAccessToken()
503
- return await Family.uploadAFamilyImage({
504
- accessToken,
505
- userAgent: this.#userAgent,
506
- requestOptions: requestOptions || this.#defaultRequestOptions,
507
- imageData
508
- })
509
- }
510
-
511
- // ============================================================================
512
- // Icons API
513
- // ============================================================================
514
-
515
- /**
516
- * Get public Yoto icons
517
- * @see https://yoto.dev/api/getpublicicons/
518
- * @param {object} [params]
519
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
520
- * @returns {Promise<YotoPublicIconsResponse>}
521
- */
522
- async getPublicIcons ({ requestOptions } = {}) {
523
- const accessToken = await this.#token.getAccessToken()
524
- return await Icons.getPublicIcons({ accessToken, userAgent: this.#userAgent, requestOptions: requestOptions || this.#defaultRequestOptions })
525
- }
526
-
527
- /**
528
- * Get user's custom icons
529
- * @see https://yoto.dev/api/getusericons/
530
- * @param {object} [params]
531
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
532
- * @returns {Promise<YotoUserIconsResponse>}
533
- */
534
- async getUserIcons ({ requestOptions } = {}) {
535
- const accessToken = await this.#token.getAccessToken()
536
- return await Icons.getUserIcons({ accessToken, userAgent: this.#userAgent, requestOptions: requestOptions || this.#defaultRequestOptions })
537
- }
538
-
539
- /**
540
- * Upload a custom icon
541
- * @see https://yoto.dev/api/uploadicon/
542
- * @param {object} params
543
- * @param {Buffer} params.imageData - Image binary data
544
- * @param {boolean} [params.autoConvert=true] - Auto-convert to proper format
545
- * @param {string} [params.filename] - Optional filename
546
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
547
- * @returns {Promise<YotoUploadIconResponse>}
548
- */
549
- async uploadIcon ({ imageData, autoConvert = true, filename, requestOptions }) {
550
- const accessToken = await this.#token.getAccessToken()
551
- return await Icons.uploadIcon({
552
- accessToken,
553
- userAgent: this.#userAgent,
554
- requestOptions: requestOptions || this.#defaultRequestOptions,
555
- imageData,
556
- autoConvert,
557
- filename
558
- })
559
- }
560
-
561
- // ============================================================================
562
- // Media API
563
- // ============================================================================
564
-
565
- /**
566
- * Get audio upload URL
567
- * @see https://yoto.dev/api/getaudiouploadurl/
568
- * @param {object} params
569
- * @param {string} params.sha256 - SHA256 hash of audio file
570
- * @param {string} [params.filename] - Optional filename
571
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
572
- * @returns {Promise<YotoAudioUploadUrlResponse>}
573
- */
574
- async getAudioUploadUrl ({ sha256, filename, requestOptions }) {
575
- const accessToken = await this.#token.getAccessToken()
576
- return await Media.getAudioUploadUrl({
577
- accessToken,
578
- userAgent: this.#userAgent,
579
- requestOptions: requestOptions || this.#defaultRequestOptions,
580
- sha256,
581
- filename
582
- })
583
- }
584
-
585
- /**
586
- * Upload a cover image
587
- * @see https://yoto.dev/api/uploadcoverimage/
588
- * @param {object} params
589
- * @param {Buffer} [params.imageData] - Image binary data
590
- * @param {string} [params.imageUrl] - URL to image
591
- * @param {boolean} [params.autoConvert] - Auto-convert to proper format
592
- * @param {YotoCoverType} [params.coverType] - Cover image type
593
- * @param {string} [params.filename] - Optional filename
594
- * @param {RequestOptions} [params.requestOptions] - Request options that override defaults
595
- * @returns {Promise<YotoUploadCoverImageResponse>}
596
- */
597
- async uploadCoverImage ({ imageData, imageUrl, autoConvert, coverType, filename, requestOptions }) {
598
- const accessToken = await this.#token.getAccessToken()
599
- return await Media.uploadCoverImage({
600
- accessToken,
601
- userAgent: this.#userAgent,
602
- requestOptions: requestOptions || this.#defaultRequestOptions,
603
- imageData,
604
- imageUrl,
605
- autoConvert,
606
- coverType,
607
- filename
608
- })
609
- }
610
-
611
- // ============================================================================
612
- // MQTT Client
613
- // ============================================================================
614
-
615
- /**
616
- * Create an MQTT client for a specific device
617
- *
618
- * The MQTT client provides real-time communication with Yoto devices:
619
- * - Subscribe to device events (playback, volume, card changes)
620
- * - Subscribe to device status (battery, temperature, network)
621
- * - Send commands (volume, card playback, ambient lights, etc.)
622
- *
623
- * @see https://yoto.dev/players-mqtt/
624
- * @param {object} params
625
- * @param {string} params.deviceId - The device ID to connect to
626
- * @param {object} [params.options] - MQTT client options
627
- * @param {boolean} [params.options.autoSubscribe=true] - Auto-subscribe to device topics on connect
628
- * @returns {Promise<YotoMqttClient>} MQTT client instance
629
- *
630
- * @example
631
- * // Create YotoClient with token refresh callback
632
- * const client = new YotoClient({
633
- * clientId: 'your-client-id',
634
- * refreshToken: 'your-refresh-token',
635
- * accessToken: 'your-access-token',
636
- * onTokenRefresh: async (event) => {
637
- * // Save tokens to file/database
638
- * await saveTokens(event.accessToken, event.refreshToken)
639
- * }
640
- * })
641
- *
642
- * // Get an online device
643
- * const devices = await client.getDevices()
644
- * const device = devices.devices.find(d => d.online)
645
- *
646
- * // Create MQTT client for the device
647
- * const mqttClient = await client.createMqttClient({ deviceId: device.deviceId })
648
- *
649
- * // Setup event handlers
650
- * mqttClient.on('events', (message) => {
651
- * console.log('Playback event:', message.playbackStatus)
652
- * console.log('Volume:', message.volume)
653
- * })
654
- *
655
- * mqttClient.on('status', (message) => {
656
- * console.log('Battery:', message.status.batteryLevel)
657
- * console.log('Temperature:', message.status.temp)
658
- * })
659
- *
660
- * mqttClient.on('response', (message) => {
661
- * console.log('Command acknowledged:', message.status)
662
- * })
663
- *
664
- * // Connect and send commands
665
- * await mqttClient.connect()
666
- * await mqttClient.requestStatus()
667
- * await mqttClient.setVolume(50)
668
- * await mqttClient.startCard({ uri: 'https://yoto.io/cardId123' })
669
- *
670
- * // Cleanup
671
- * await mqttClient.disconnect()
672
- */
673
- async createMqttClient ({ deviceId, options }) {
674
- const accessToken = await this.#token.getAccessToken()
675
-
676
- /** @type {{ deviceId: string, accessToken: string, autoSubscribe?: boolean }} */
677
- const mqttOptions = {
678
- deviceId,
679
- accessToken
680
- }
681
-
682
- // Only add autoSubscribe if explicitly provided
683
- if (options?.autoSubscribe !== undefined) {
684
- mqttOptions.autoSubscribe = options.autoSubscribe
685
- }
686
-
687
- return createYotoMqttClient(mqttOptions)
688
- }
689
- }
1
+ export { YotoClient } from './lib/api-client.js'
2
+ export { YotoDeviceModel } from './lib/yoto-device.js'
3
+ export { YotoAccount } from './lib/yoto-account.js'
4
+
5
+ // Export OAuth constants
6
+ export {
7
+ DEFAULT_CLIENT_ID,
8
+ DEFAULT_AUDIENCE,
9
+ DEFAULT_SCOPE,
10
+ DEVICE_CODE_GRANT_TYPE
11
+ } from './lib/api-endpoints/constants.js'