yoto-nodejs-client 0.0.6 → 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.
- package/README.md +51 -36
- package/lib/test-helpers/device-model-test-helpers.d.ts +29 -0
- package/lib/test-helpers/device-model-test-helpers.d.ts.map +1 -0
- package/lib/test-helpers/device-model-test-helpers.js +116 -0
- package/lib/yoto-account.d.ts +339 -9
- package/lib/yoto-account.d.ts.map +1 -1
- package/lib/yoto-account.js +411 -39
- package/lib/yoto-account.test.js +139 -0
- package/lib/yoto-device.d.ts +31 -3
- package/lib/yoto-device.d.ts.map +1 -1
- package/lib/yoto-device.js +33 -11
- package/lib/yoto-device.test.js +6 -111
- package/package.json +1 -1
package/lib/yoto-device.d.ts
CHANGED
|
@@ -171,6 +171,10 @@ export const YotoPlaybackStateType: {};
|
|
|
171
171
|
* MQTT disconnect event metadata (from MQTT disconnect packet)
|
|
172
172
|
* @typedef {YotoMqttClientDisconnectMetadata} YotoMqttDisconnectMetadata
|
|
173
173
|
*/
|
|
174
|
+
/**
|
|
175
|
+
* MQTT connect event metadata (from MQTT CONNACK)
|
|
176
|
+
* @typedef {IConnackPacket} YotoMqttConnectMetadata
|
|
177
|
+
*/
|
|
174
178
|
/**
|
|
175
179
|
* MQTT close event metadata (from connection close)
|
|
176
180
|
* @typedef {YotoMqttClientCloseMetadata} YotoMqttCloseMetadata
|
|
@@ -196,12 +200,17 @@ export const YotoPlaybackStateType: {};
|
|
|
196
200
|
* 'playbackUpdate': [YotoPlaybackState, Set<keyof YotoPlaybackState>],
|
|
197
201
|
* 'online': [YotoDeviceOnlineMetadata],
|
|
198
202
|
* 'offline': [YotoDeviceOfflineMetadata],
|
|
199
|
-
* 'mqttConnect': [],
|
|
203
|
+
* 'mqttConnect': [YotoMqttConnectMetadata],
|
|
200
204
|
* 'mqttDisconnect': [YotoMqttDisconnectMetadata],
|
|
201
205
|
* 'mqttClose': [YotoMqttCloseMetadata],
|
|
202
206
|
* 'mqttReconnect': [],
|
|
203
207
|
* 'mqttOffline': [],
|
|
204
208
|
* 'mqttEnd': [],
|
|
209
|
+
* 'mqttStatus': [string, YotoStatusMessage],
|
|
210
|
+
* 'mqttEvents': [string, YotoEventsMessage],
|
|
211
|
+
* 'mqttStatusLegacy': [string, YotoStatusLegacyMessage],
|
|
212
|
+
* 'mqttResponse': [string, YotoResponseMessage],
|
|
213
|
+
* 'mqttUnknown': [string, unknown],
|
|
205
214
|
* 'error': [Error]
|
|
206
215
|
* }} YotoDeviceModelEventMap
|
|
207
216
|
*/
|
|
@@ -223,12 +232,17 @@ export const YotoPlaybackStateType: {};
|
|
|
223
232
|
* - 'playbackUpdate' - Emitted when playback state changes, passes (playback, changedFields)
|
|
224
233
|
* - 'online' - Emitted when device comes online, passes metadata with reason and optional upTime
|
|
225
234
|
* - 'offline' - Emitted when device goes offline, passes metadata with reason and optional shutDownReason or timeSinceLastSeen
|
|
226
|
-
* - 'mqttConnect' - Emitted when MQTT client connects
|
|
235
|
+
* - 'mqttConnect' - Emitted when MQTT client connects, passes CONNACK metadata
|
|
227
236
|
* - 'mqttDisconnect' - Emitted when MQTT disconnect packet received, passes metadata with disconnect packet
|
|
228
237
|
* - 'mqttClose' - Emitted when MQTT connection closes, passes metadata with close reason
|
|
229
238
|
* - 'mqttReconnect' - Emitted when MQTT client is reconnecting
|
|
230
239
|
* - 'mqttOffline' - Emitted when MQTT client goes offline
|
|
231
240
|
* - 'mqttEnd' - Emitted when MQTT client end is called
|
|
241
|
+
* - 'mqttStatus' - Emitted with raw MQTT status messages, passes (topic, message)
|
|
242
|
+
* - 'mqttEvents' - Emitted with raw MQTT events messages, passes (topic, message)
|
|
243
|
+
* - 'mqttStatusLegacy' - Emitted with raw legacy MQTT status messages, passes (topic, message)
|
|
244
|
+
* - 'mqttResponse' - Emitted with raw MQTT response messages, passes (topic, message)
|
|
245
|
+
* - 'mqttUnknown' - Emitted with unknown MQTT messages, passes (topic, message)
|
|
232
246
|
* - 'error' - Emitted when an error occurs, passes error
|
|
233
247
|
*
|
|
234
248
|
* @extends {EventEmitter<YotoDeviceModelEventMap>}
|
|
@@ -946,6 +960,10 @@ export type YotoDeviceOfflineMetadata = {
|
|
|
946
960
|
* MQTT disconnect event metadata (from MQTT disconnect packet)
|
|
947
961
|
*/
|
|
948
962
|
export type YotoMqttDisconnectMetadata = YotoMqttClientDisconnectMetadata;
|
|
963
|
+
/**
|
|
964
|
+
* MQTT connect event metadata (from MQTT CONNACK)
|
|
965
|
+
*/
|
|
966
|
+
export type YotoMqttConnectMetadata = IConnackPacket;
|
|
949
967
|
/**
|
|
950
968
|
* MQTT close event metadata (from connection close)
|
|
951
969
|
*/
|
|
@@ -994,12 +1012,17 @@ export type YotoDeviceModelEventMap = {
|
|
|
994
1012
|
"playbackUpdate": [YotoPlaybackState, Set<keyof YotoPlaybackState>];
|
|
995
1013
|
"online": [YotoDeviceOnlineMetadata];
|
|
996
1014
|
"offline": [YotoDeviceOfflineMetadata];
|
|
997
|
-
"mqttConnect": [];
|
|
1015
|
+
"mqttConnect": [YotoMqttConnectMetadata];
|
|
998
1016
|
"mqttDisconnect": [YotoMqttDisconnectMetadata];
|
|
999
1017
|
"mqttClose": [YotoMqttCloseMetadata];
|
|
1000
1018
|
"mqttReconnect": [];
|
|
1001
1019
|
"mqttOffline": [];
|
|
1002
1020
|
"mqttEnd": [];
|
|
1021
|
+
"mqttStatus": [string, YotoStatusMessage];
|
|
1022
|
+
"mqttEvents": [string, YotoEventsMessage];
|
|
1023
|
+
"mqttStatusLegacy": [string, YotoStatusLegacyMessage];
|
|
1024
|
+
"mqttResponse": [string, YotoResponseMessage];
|
|
1025
|
+
"mqttUnknown": [string, unknown];
|
|
1003
1026
|
"error": [Error];
|
|
1004
1027
|
};
|
|
1005
1028
|
import { EventEmitter } from 'events';
|
|
@@ -1012,5 +1035,10 @@ import type { YotoClient } from './api-client.js';
|
|
|
1012
1035
|
import type { PlaybackStatus } from './mqtt/client.js';
|
|
1013
1036
|
import type { YotoMqttOptions } from './mqtt/factory.js';
|
|
1014
1037
|
import type { YotoMqttClientDisconnectMetadata } from './mqtt/client.js';
|
|
1038
|
+
import type { IConnackPacket } from 'mqtt';
|
|
1015
1039
|
import type { YotoMqttClientCloseMetadata } from './mqtt/client.js';
|
|
1040
|
+
import type { YotoStatusMessage } from './mqtt/client.js';
|
|
1041
|
+
import type { YotoEventsMessage } from './mqtt/client.js';
|
|
1042
|
+
import type { YotoStatusLegacyMessage } from './mqtt/client.js';
|
|
1043
|
+
import type { YotoResponseMessage } from './mqtt/client.js';
|
|
1016
1044
|
//# sourceMappingURL=yoto-device.d.ts.map
|
package/lib/yoto-device.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yoto-device.d.ts","sourceRoot":"","sources":["yoto-device.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"yoto-device.d.ts","sourceRoot":"","sources":["yoto-device.js"],"names":[],"mappings":"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"}
|
package/lib/yoto-device.js
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
+
* @import { IConnackPacket } from 'mqtt'
|
|
10
11
|
* @import { YotoClient } from './api-client.js'
|
|
11
12
|
* @import { YotoDevice, YotoDeviceConfig, YotoDeviceShortcuts, YotoDeviceFullStatus, YotoDeviceStatusResponse, YotoDeviceCommand, YotoDeviceCommandResponse } from './api-endpoints/devices.js'
|
|
12
|
-
* @import { YotoMqttClient, YotoMqttStatus, YotoEventsMessage, YotoLegacyStatus, YotoMqttClientDisconnectMetadata, YotoMqttClientCloseMetadata, PlaybackStatus } from './mqtt/client.js'
|
|
13
|
+
* @import { YotoMqttClient, YotoMqttStatus, YotoEventsMessage, YotoLegacyStatus, YotoStatusMessage, YotoStatusLegacyMessage, YotoResponseMessage, YotoMqttClientDisconnectMetadata, YotoMqttClientCloseMetadata, PlaybackStatus } from './mqtt/client.js'
|
|
13
14
|
* @import { YotoMqttOptions } from './mqtt/factory.js'
|
|
14
15
|
*/
|
|
15
16
|
|
|
@@ -459,6 +460,11 @@ export const YotoPlaybackStateType = {}
|
|
|
459
460
|
* @typedef {YotoMqttClientDisconnectMetadata} YotoMqttDisconnectMetadata
|
|
460
461
|
*/
|
|
461
462
|
|
|
463
|
+
/**
|
|
464
|
+
* MQTT connect event metadata (from MQTT CONNACK)
|
|
465
|
+
* @typedef {IConnackPacket} YotoMqttConnectMetadata
|
|
466
|
+
*/
|
|
467
|
+
|
|
462
468
|
/**
|
|
463
469
|
* MQTT close event metadata (from connection close)
|
|
464
470
|
* @typedef {YotoMqttClientCloseMetadata} YotoMqttCloseMetadata
|
|
@@ -486,12 +492,17 @@ export const YotoPlaybackStateType = {}
|
|
|
486
492
|
* 'playbackUpdate': [YotoPlaybackState, Set<keyof YotoPlaybackState>],
|
|
487
493
|
* 'online': [YotoDeviceOnlineMetadata],
|
|
488
494
|
* 'offline': [YotoDeviceOfflineMetadata],
|
|
489
|
-
* 'mqttConnect': [],
|
|
495
|
+
* 'mqttConnect': [YotoMqttConnectMetadata],
|
|
490
496
|
* 'mqttDisconnect': [YotoMqttDisconnectMetadata],
|
|
491
497
|
* 'mqttClose': [YotoMqttCloseMetadata],
|
|
492
498
|
* 'mqttReconnect': [],
|
|
493
499
|
* 'mqttOffline': [],
|
|
494
500
|
* 'mqttEnd': [],
|
|
501
|
+
* 'mqttStatus': [string, YotoStatusMessage],
|
|
502
|
+
* 'mqttEvents': [string, YotoEventsMessage],
|
|
503
|
+
* 'mqttStatusLegacy': [string, YotoStatusLegacyMessage],
|
|
504
|
+
* 'mqttResponse': [string, YotoResponseMessage],
|
|
505
|
+
* 'mqttUnknown': [string, unknown],
|
|
495
506
|
* 'error': [Error]
|
|
496
507
|
* }} YotoDeviceModelEventMap
|
|
497
508
|
*/
|
|
@@ -518,12 +529,17 @@ export const YotoPlaybackStateType = {}
|
|
|
518
529
|
* - 'playbackUpdate' - Emitted when playback state changes, passes (playback, changedFields)
|
|
519
530
|
* - 'online' - Emitted when device comes online, passes metadata with reason and optional upTime
|
|
520
531
|
* - 'offline' - Emitted when device goes offline, passes metadata with reason and optional shutDownReason or timeSinceLastSeen
|
|
521
|
-
* - 'mqttConnect' - Emitted when MQTT client connects
|
|
532
|
+
* - 'mqttConnect' - Emitted when MQTT client connects, passes CONNACK metadata
|
|
522
533
|
* - 'mqttDisconnect' - Emitted when MQTT disconnect packet received, passes metadata with disconnect packet
|
|
523
534
|
* - 'mqttClose' - Emitted when MQTT connection closes, passes metadata with close reason
|
|
524
535
|
* - 'mqttReconnect' - Emitted when MQTT client is reconnecting
|
|
525
536
|
* - 'mqttOffline' - Emitted when MQTT client goes offline
|
|
526
537
|
* - 'mqttEnd' - Emitted when MQTT client end is called
|
|
538
|
+
* - 'mqttStatus' - Emitted with raw MQTT status messages, passes (topic, message)
|
|
539
|
+
* - 'mqttEvents' - Emitted with raw MQTT events messages, passes (topic, message)
|
|
540
|
+
* - 'mqttStatusLegacy' - Emitted with raw legacy MQTT status messages, passes (topic, message)
|
|
541
|
+
* - 'mqttResponse' - Emitted with raw MQTT response messages, passes (topic, message)
|
|
542
|
+
* - 'mqttUnknown' - Emitted with unknown MQTT messages, passes (topic, message)
|
|
527
543
|
* - 'error' - Emitted when an error occurs, passes error
|
|
528
544
|
*
|
|
529
545
|
* @extends {EventEmitter<YotoDeviceModelEventMap>}
|
|
@@ -1144,8 +1160,8 @@ export class YotoDeviceModel extends EventEmitter {
|
|
|
1144
1160
|
if (!this.#mqttClient) return
|
|
1145
1161
|
|
|
1146
1162
|
// Connection events
|
|
1147
|
-
this.#mqttClient.on('connect', () => {
|
|
1148
|
-
this.emit('mqttConnect')
|
|
1163
|
+
this.#mqttClient.on('connect', (metadata) => {
|
|
1164
|
+
this.emit('mqttConnect', metadata)
|
|
1149
1165
|
|
|
1150
1166
|
// Request status and events after settling period
|
|
1151
1167
|
this.#scheduleMqttRequests()
|
|
@@ -1186,8 +1202,9 @@ export class YotoDeviceModel extends EventEmitter {
|
|
|
1186
1202
|
})
|
|
1187
1203
|
|
|
1188
1204
|
// Status updates - PRIMARY source for status after initialization
|
|
1189
|
-
this.#mqttClient.on('status', (
|
|
1205
|
+
this.#mqttClient.on('status', (topic, message) => {
|
|
1190
1206
|
this.#recordDeviceActivity()
|
|
1207
|
+
this.emit('mqttStatus', topic, message)
|
|
1191
1208
|
this.#updateStatusFromDocumentedMqtt(message.status)
|
|
1192
1209
|
})
|
|
1193
1210
|
|
|
@@ -1195,20 +1212,25 @@ export class YotoDeviceModel extends EventEmitter {
|
|
|
1195
1212
|
// This does NOT respond to requestStatus() - only emits on real-time events or 5-minute periodic updates
|
|
1196
1213
|
// NOTE: Don't call #recordDeviceActivity() here - #handleLegacyStatus handles online/offline transitions
|
|
1197
1214
|
// based on actual power state (shutdown/startup) which is more reliable than just "activity"
|
|
1198
|
-
this.#mqttClient.on('status-legacy', (
|
|
1215
|
+
this.#mqttClient.on('status-legacy', (topic, message) => {
|
|
1216
|
+
this.emit('mqttStatusLegacy', topic, message)
|
|
1199
1217
|
this.#handleLegacyStatus(message.status)
|
|
1200
1218
|
})
|
|
1201
1219
|
|
|
1202
1220
|
// Events updates (playback, volume, etc.)
|
|
1203
|
-
this.#mqttClient.on('events', (
|
|
1221
|
+
this.#mqttClient.on('events', (topic, message) => {
|
|
1204
1222
|
this.#recordDeviceActivity()
|
|
1223
|
+
this.emit('mqttEvents', topic, message)
|
|
1205
1224
|
this.#handleEventMessage(message)
|
|
1206
1225
|
})
|
|
1207
1226
|
|
|
1208
1227
|
// Response messages (for debugging/logging)
|
|
1209
|
-
this.#mqttClient.on('response', (
|
|
1210
|
-
|
|
1211
|
-
|
|
1228
|
+
this.#mqttClient.on('response', (topic, message) => {
|
|
1229
|
+
this.emit('mqttResponse', topic, message)
|
|
1230
|
+
})
|
|
1231
|
+
|
|
1232
|
+
this.#mqttClient.on('unknown', (topic, message) => {
|
|
1233
|
+
this.emit('mqttUnknown', topic, message)
|
|
1212
1234
|
})
|
|
1213
1235
|
}
|
|
1214
1236
|
|
package/lib/yoto-device.test.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
/** @import { YotoDeviceModelConfig, YotoPlaybackState } from './yoto-device.js' */
|
|
2
1
|
/** @import { YotoDevice } from './api-endpoints/devices.js' */
|
|
3
2
|
|
|
4
3
|
import test from 'node:test'
|
|
5
4
|
import assert from 'node:assert/strict'
|
|
6
|
-
import { once } from 'node:events'
|
|
7
|
-
import { setTimeout as sleep } from 'node:timers/promises'
|
|
8
5
|
import { join } from 'node:path'
|
|
9
6
|
import { YotoClient } from './api-client.js'
|
|
10
7
|
import { YotoDeviceModel } from './yoto-device.js'
|
|
11
8
|
import { loadTestTokens } from './api-endpoints/endpoint-test-helpers.js'
|
|
12
9
|
import { saveTokensToEnv } from '../bin/lib/token-helpers.js'
|
|
10
|
+
import {
|
|
11
|
+
assertConfigShape,
|
|
12
|
+
assertPlaybackShape,
|
|
13
|
+
toLower,
|
|
14
|
+
waitForModelReady
|
|
15
|
+
} from './test-helpers/device-model-test-helpers.js'
|
|
13
16
|
|
|
14
17
|
const envPath = join(import.meta.dirname, '..', '.env')
|
|
15
18
|
|
|
@@ -41,109 +44,6 @@ function createTestClient () {
|
|
|
41
44
|
})
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
/**
|
|
45
|
-
* @param {Promise<unknown>} promise
|
|
46
|
-
* @param {number} timeoutMs
|
|
47
|
-
* @param {string} label
|
|
48
|
-
* @returns {Promise<unknown>}
|
|
49
|
-
*/
|
|
50
|
-
function withTimeout (promise, timeoutMs, label) {
|
|
51
|
-
return Promise.race([
|
|
52
|
-
promise,
|
|
53
|
-
sleep(timeoutMs).then(() => {
|
|
54
|
-
throw new Error(`${label} timed out after ${timeoutMs}ms`)
|
|
55
|
-
})
|
|
56
|
-
])
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {YotoDeviceModel} model
|
|
61
|
-
* @returns {Promise<void>}
|
|
62
|
-
*/
|
|
63
|
-
async function waitForModelReady (model) {
|
|
64
|
-
const started = withTimeout(once(model, 'started'), 15000, 'started')
|
|
65
|
-
const statusUpdated = withTimeout(once(model, 'statusUpdate'), 15000, 'statusUpdate')
|
|
66
|
-
const configUpdated = withTimeout(once(model, 'configUpdate'), 15000, 'configUpdate')
|
|
67
|
-
|
|
68
|
-
await model.start()
|
|
69
|
-
await Promise.all([started, statusUpdated, configUpdated])
|
|
70
|
-
await sleep(1500)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* @param {YotoDeviceModelConfig} config
|
|
75
|
-
*/
|
|
76
|
-
function assertConfigShape (config) {
|
|
77
|
-
assert.ok(Array.isArray(config.alarms), 'alarms should be an array')
|
|
78
|
-
assert.equal(typeof config.ambientColour, 'string', 'ambientColour should be string')
|
|
79
|
-
assert.equal(typeof config.bluetoothEnabled, 'boolean', 'bluetoothEnabled should be boolean')
|
|
80
|
-
assert.equal(typeof config.btHeadphonesEnabled, 'boolean', 'btHeadphonesEnabled should be boolean')
|
|
81
|
-
assert.equal(typeof config.clockFace, 'string', 'clockFace should be string')
|
|
82
|
-
assert.equal(typeof config.dayDisplayBrightnessAuto, 'boolean', 'dayDisplayBrightnessAuto should be boolean')
|
|
83
|
-
if (config.dayDisplayBrightnessAuto) {
|
|
84
|
-
assert.equal(config.dayDisplayBrightness, null, 'dayDisplayBrightness should be null when auto')
|
|
85
|
-
} else {
|
|
86
|
-
assert.equal(typeof config.dayDisplayBrightness, 'number', 'dayDisplayBrightness should be number when not auto')
|
|
87
|
-
}
|
|
88
|
-
assert.equal(typeof config.dayTime, 'string', 'dayTime should be string')
|
|
89
|
-
assert.equal(typeof config.dayYotoDaily, 'string', 'dayYotoDaily should be string')
|
|
90
|
-
assert.equal(typeof config.dayYotoRadio, 'string', 'dayYotoRadio should be string')
|
|
91
|
-
assert.equal(typeof config.daySoundsOff, 'boolean', 'daySoundsOff should be boolean')
|
|
92
|
-
assert.equal(typeof config.displayDimBrightness, 'number', 'displayDimBrightness should be number')
|
|
93
|
-
assert.equal(typeof config.displayDimTimeout, 'number', 'displayDimTimeout should be number')
|
|
94
|
-
assert.equal(typeof config.headphonesVolumeLimited, 'boolean', 'headphonesVolumeLimited should be boolean')
|
|
95
|
-
assert.ok(config.hourFormat === 12 || config.hourFormat === 24, 'hourFormat should be 12 or 24')
|
|
96
|
-
assert.equal(typeof config.locale, 'string', 'locale should be string')
|
|
97
|
-
assert.equal(typeof config.logLevel, 'string', 'logLevel should be string')
|
|
98
|
-
assert.equal(typeof config.maxVolumeLimit, 'number', 'maxVolumeLimit should be number')
|
|
99
|
-
assert.equal(typeof config.nightAmbientColour, 'string', 'nightAmbientColour should be string')
|
|
100
|
-
assert.equal(typeof config.nightDisplayBrightnessAuto, 'boolean', 'nightDisplayBrightnessAuto should be boolean')
|
|
101
|
-
if (config.nightDisplayBrightnessAuto) {
|
|
102
|
-
assert.equal(config.nightDisplayBrightness, null, 'nightDisplayBrightness should be null when auto')
|
|
103
|
-
} else {
|
|
104
|
-
assert.equal(typeof config.nightDisplayBrightness, 'number', 'nightDisplayBrightness should be number when not auto')
|
|
105
|
-
}
|
|
106
|
-
assert.equal(typeof config.nightMaxVolumeLimit, 'number', 'nightMaxVolumeLimit should be number')
|
|
107
|
-
assert.equal(typeof config.nightTime, 'string', 'nightTime should be string')
|
|
108
|
-
assert.equal(typeof config.nightYotoDaily, 'string', 'nightYotoDaily should be string')
|
|
109
|
-
assert.equal(typeof config.nightYotoRadioEnabled, 'boolean', 'nightYotoRadioEnabled should be boolean')
|
|
110
|
-
if (config.nightYotoRadioEnabled) {
|
|
111
|
-
assert.equal(typeof config.nightYotoRadio, 'string', 'nightYotoRadio should be string when enabled')
|
|
112
|
-
} else {
|
|
113
|
-
assert.equal(config.nightYotoRadio, null, 'nightYotoRadio should be null when disabled')
|
|
114
|
-
}
|
|
115
|
-
assert.equal(typeof config.nightSoundsOff, 'boolean', 'nightSoundsOff should be boolean')
|
|
116
|
-
assert.equal(typeof config.pausePowerButton, 'boolean', 'pausePowerButton should be boolean')
|
|
117
|
-
assert.equal(typeof config.pauseVolumeDown, 'boolean', 'pauseVolumeDown should be boolean')
|
|
118
|
-
assert.equal(typeof config.repeatAll, 'boolean', 'repeatAll should be boolean')
|
|
119
|
-
assert.equal(typeof config.showDiagnostics, 'boolean', 'showDiagnostics should be boolean')
|
|
120
|
-
assert.equal(typeof config.shutdownTimeout, 'number', 'shutdownTimeout should be number')
|
|
121
|
-
assert.equal(typeof config.systemVolume, 'number', 'systemVolume should be number')
|
|
122
|
-
assert.equal(typeof config.timezone, 'string', 'timezone should be string')
|
|
123
|
-
assert.equal(typeof config.volumeLevel, 'string', 'volumeLevel should be string')
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* @param {YotoPlaybackState} playback
|
|
128
|
-
*/
|
|
129
|
-
function assertPlaybackShape (playback) {
|
|
130
|
-
assert.ok(playback, 'playback should exist')
|
|
131
|
-
assert.equal(typeof playback.updatedAt, 'string', 'playback.updatedAt should be string')
|
|
132
|
-
|
|
133
|
-
assert.ok(playback.cardId === null || typeof playback.cardId === 'string', 'playback.cardId should be string or null')
|
|
134
|
-
assert.ok(playback.source === null || typeof playback.source === 'string', 'playback.source should be string or null')
|
|
135
|
-
assert.ok(playback.playbackStatus === null || typeof playback.playbackStatus === 'string', 'playback.playbackStatus should be string or null')
|
|
136
|
-
assert.ok(playback.trackTitle === null || typeof playback.trackTitle === 'string', 'playback.trackTitle should be string or null')
|
|
137
|
-
assert.ok(playback.trackKey === null || typeof playback.trackKey === 'string', 'playback.trackKey should be string or null')
|
|
138
|
-
assert.ok(playback.chapterTitle === null || typeof playback.chapterTitle === 'string', 'playback.chapterTitle should be string or null')
|
|
139
|
-
assert.ok(playback.chapterKey === null || typeof playback.chapterKey === 'string', 'playback.chapterKey should be string or null')
|
|
140
|
-
assert.ok(playback.position === null || typeof playback.position === 'number', 'playback.position should be number or null')
|
|
141
|
-
assert.ok(playback.trackLength === null || typeof playback.trackLength === 'number', 'playback.trackLength should be number or null')
|
|
142
|
-
assert.ok(playback.streaming === null || typeof playback.streaming === 'boolean', 'playback.streaming should be boolean or null')
|
|
143
|
-
assert.ok(playback.sleepTimerActive === null || typeof playback.sleepTimerActive === 'boolean', 'playback.sleepTimerActive should be boolean or null')
|
|
144
|
-
assert.ok(playback.sleepTimerSeconds === null || typeof playback.sleepTimerSeconds === 'number', 'playback.sleepTimerSeconds should be number or null')
|
|
145
|
-
}
|
|
146
|
-
|
|
147
47
|
/**
|
|
148
48
|
* @param {YotoClient} client
|
|
149
49
|
* @param {YotoDevice} device
|
|
@@ -169,11 +69,6 @@ test('YotoDeviceModel - online devices', async (t) => {
|
|
|
169
69
|
const client = createTestClient()
|
|
170
70
|
const response = await client.getDevices()
|
|
171
71
|
|
|
172
|
-
/**
|
|
173
|
-
* @param {string | undefined} value
|
|
174
|
-
* @returns {string}
|
|
175
|
-
*/
|
|
176
|
-
const toLower = (value) => typeof value === 'string' ? value.toLowerCase() : ''
|
|
177
72
|
const onlineMini = response.devices.find(device =>
|
|
178
73
|
device.online && (toLower(device.deviceFamily) === 'mini' || toLower(device.deviceType).includes('mini'))
|
|
179
74
|
)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yoto-nodejs-client",
|
|
3
3
|
"description": "(Unofficial) Node.js client for the Yoto API with automatic token refresh, MQTT device communication, and TypeScript support",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.7",
|
|
5
5
|
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/bcomnes/yoto-nodejs-client/issues"
|