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 CHANGED
@@ -211,24 +211,21 @@ account.on('started', (metadata) => {
211
211
 
212
212
 
213
213
 
214
- // Listen for device-specific events from individual devices
215
- account.on('deviceAdded', (deviceId, deviceModel) => {
216
- // Attach event listeners to each device as it's added
217
- deviceModel.on('statusUpdate', (status, source) => {
218
- console.log(`${deviceId} battery: ${status.batteryLevelPercentage}%`)
219
- })
214
+ // Listen for device events across all devices via the unified bus
215
+ account.on('statusUpdate', ({ deviceId, status, source }) => {
216
+ console.log(`${deviceId} battery: ${status.batteryLevelPercentage}% (${source})`)
217
+ })
220
218
 
221
- deviceModel.on('online', (metadata) => {
222
- console.log(`${deviceId} came online`)
223
- })
219
+ account.on('online', ({ deviceId }) => {
220
+ console.log(`${deviceId} came online`)
221
+ })
224
222
 
225
- deviceModel.on('offline', (metadata) => {
226
- console.log(`${deviceId} went offline`)
227
- })
223
+ account.on('offline', ({ deviceId }) => {
224
+ console.log(`${deviceId} went offline`)
228
225
  })
229
226
 
230
227
  // Unified error handling
231
- account.on('error', (error, context) => {
228
+ account.on('error', ({ error, context }) => {
232
229
  console.error(`Error in ${context.source}:`, error.message)
233
230
  if (context.deviceId) {
234
231
  console.error(`Device: ${context.deviceId}`)
@@ -970,10 +967,17 @@ Create a stateful device client that manages device state primarily from MQTT wi
970
967
  - `playbackUpdate(playback, changedFields)` - Playback state changed, passes (playback, changedFields)
971
968
  - `online(metadata)` - Device came online, passes metadata with reason and optional upTime
972
969
  - `offline(metadata)` - Device went offline, passes metadata with reason and optional shutDownReason or timeSinceLastSeen
973
- - `mqttConnect()` - MQTT client connected
970
+ - `mqttConnect(metadata)` - MQTT client connected, passes CONNACK metadata
974
971
  - `mqttDisconnect(metadata)` - MQTT disconnect packet received, passes metadata with disconnect packet
975
972
  - `mqttClose(metadata)` - MQTT connection closed, passes metadata with close reason
976
973
  - `mqttReconnect()` - MQTT client is reconnecting
974
+ - `mqttOffline()` - MQTT client goes offline
975
+ - `mqttEnd()` - MQTT client end is called
976
+ - `mqttStatus(topic, message)` - Raw MQTT status messages (documented status topic)
977
+ - `mqttEvents(topic, message)` - Raw MQTT events messages
978
+ - `mqttStatusLegacy(topic, message)` - Raw legacy MQTT status messages (undocumented status topic)
979
+ - `mqttResponse(topic, message)` - Raw MQTT response messages
980
+ - `mqttUnknown(topic, message)` - Raw MQTT messages that do not match known types
977
981
  - `error(error)` - Error occurred, passes error
978
982
 
979
983
  **Static Properties & Methods:**
@@ -1051,11 +1055,27 @@ Create an account manager that automatically discovers and manages all devices f
1051
1055
  **Events:**
1052
1056
  - `started(metadata)` - Account started (metadata: { deviceCount, devices })
1053
1057
  - `stopped()` - Account stopped
1054
- - `deviceAdded(deviceId, deviceModel)` - Device was added
1055
- - `deviceRemoved(deviceId)` - Device was removed
1056
- - `error(error, context)` - Error occurred (context: { source, deviceId, operation })
1057
-
1058
- **Note:** To listen to individual device events (statusUpdate, configUpdate, playbackUpdate, online, offline, mqttConnect, mqttDisconnect, mqttClose, mqttReconnect, etc.), access the device models directly via `account.devices` or `account.getDevice(deviceId)` and attach listeners to them.
1058
+ - `deviceAdded({ deviceId })` - Device was added
1059
+ - `deviceRemoved({ deviceId })` - Device was removed
1060
+ - `statusUpdate({ deviceId, status, source, changedFields })` - Re-emitted device status update
1061
+ - `configUpdate({ deviceId, config, changedFields })` - Re-emitted config update
1062
+ - `playbackUpdate({ deviceId, playback, changedFields })` - Re-emitted playback update
1063
+ - `online({ deviceId, metadata })` - Re-emitted online event
1064
+ - `offline({ deviceId, metadata })` - Re-emitted offline event
1065
+ - `mqttConnect({ deviceId, metadata })` - Re-emitted MQTT connect
1066
+ - `mqttDisconnect({ deviceId, metadata })` - Re-emitted MQTT disconnect
1067
+ - `mqttClose({ deviceId, metadata })` - Re-emitted MQTT close
1068
+ - `mqttReconnect({ deviceId })` - Re-emitted MQTT reconnect
1069
+ - `mqttOffline({ deviceId })` - Re-emitted MQTT offline
1070
+ - `mqttEnd({ deviceId })` - Re-emitted MQTT end
1071
+ - `mqttStatus({ deviceId, topic, message })` - Re-emitted raw MQTT status
1072
+ - `mqttEvents({ deviceId, topic, message })` - Re-emitted raw MQTT events
1073
+ - `mqttStatusLegacy({ deviceId, topic, message })` - Re-emitted raw MQTT legacy status
1074
+ - `mqttResponse({ deviceId, topic, message })` - Re-emitted raw MQTT response
1075
+ - `mqttUnknown({ deviceId, topic, message })` - Re-emitted raw MQTT unknown message
1076
+ - `error({ error, context })` - Error occurred (context: { source, deviceId, operation })
1077
+
1078
+ **Note:** You can still listen to individual device events by attaching listeners to each `YotoDeviceModel`, but the account now re-emits device and MQTT events with device context for unified handling.
1059
1079
 
1060
1080
  ```js
1061
1081
  import { YotoAccount } from 'yoto-nodejs-client'
@@ -1075,26 +1095,21 @@ const account = new YotoAccount({
1075
1095
  })
1076
1096
 
1077
1097
  // Account-level error handling
1078
- account.on('error', (error, context) => {
1098
+ account.on('error', ({ error, context }) => {
1079
1099
  console.error(`Error in ${context.source}:`, error.message)
1080
1100
  })
1081
1101
 
1082
- // Listen to device added events
1083
- account.on('deviceAdded', (deviceId, deviceModel) => {
1084
- console.log(`Device ${deviceId} added`)
1085
-
1086
- // Attach listeners to individual devices
1087
- deviceModel.on('statusUpdate', (status, source) => {
1088
- console.log(`${deviceId} battery: ${status.batteryLevelPercentage}%`)
1089
- })
1090
-
1091
- deviceModel.on('online', (metadata) => {
1092
- console.log(`${deviceId} came online (${metadata.reason})`)
1093
- })
1094
-
1095
- deviceModel.on('offline', (metadata) => {
1096
- console.log(`${deviceId} went offline (${metadata.reason})`)
1097
- })
1102
+ // Unified device events across all devices
1103
+ account.on('statusUpdate', ({ deviceId, status, source }) => {
1104
+ console.log(`${deviceId} battery: ${status.batteryLevelPercentage}% (${source})`)
1105
+ })
1106
+
1107
+ account.on('online', ({ deviceId, metadata }) => {
1108
+ console.log(`${deviceId} came online (${metadata.reason})`)
1109
+ })
1110
+
1111
+ account.on('offline', ({ deviceId, metadata }) => {
1112
+ console.log(`${deviceId} went offline (${metadata.reason})`)
1098
1113
  })
1099
1114
 
1100
1115
  await account.start()
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @param {Promise<unknown>} promise
3
+ * @param {number} timeoutMs
4
+ * @param {string} label
5
+ * @returns {Promise<unknown>}
6
+ */
7
+ export function withTimeout(promise: Promise<unknown>, timeoutMs: number, label: string): Promise<unknown>;
8
+ /**
9
+ * @param {YotoDeviceModel} model
10
+ * @returns {Promise<void>}
11
+ */
12
+ export function waitForModelReady(model: YotoDeviceModel): Promise<void>;
13
+ /**
14
+ * @param {YotoDeviceModelConfig} config
15
+ */
16
+ export function assertConfigShape(config: YotoDeviceModelConfig): void;
17
+ /**
18
+ * @param {YotoPlaybackState} playback
19
+ */
20
+ export function assertPlaybackShape(playback: YotoPlaybackState): void;
21
+ /**
22
+ * @param {string | undefined} value
23
+ * @returns {string}
24
+ */
25
+ export function toLower(value: string | undefined): string;
26
+ import type { YotoDeviceModel } from '../yoto-device.js';
27
+ import type { YotoDeviceModelConfig } from '../yoto-device.js';
28
+ import type { YotoPlaybackState } from '../yoto-device.js';
29
+ //# sourceMappingURL=device-model-test-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-model-test-helpers.d.ts","sourceRoot":"","sources":["device-model-test-helpers.js"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,qCALW,OAAO,CAAC,OAAO,CAAC,aAChB,MAAM,SACN,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAS5B;AAED;;;GAGG;AACH,yCAHW,eAAe,GACb,OAAO,CAAC,IAAI,CAAC,CAUzB;AAED;;GAEG;AACH,0CAFW,qBAAqB,QAkD/B;AAED;;GAEG;AACH,8CAFW,iBAAiB,QAkB3B;AAED;;;GAGG;AACH,+BAHW,MAAM,GAAG,SAAS,GAChB,MAAM,CAIlB;qCAnH8E,mBAAmB;2CAAnB,mBAAmB;uCAAnB,mBAAmB"}
@@ -0,0 +1,116 @@
1
+ /** @import { YotoDeviceModel, YotoDeviceModelConfig, YotoPlaybackState } from '../yoto-device.js' */
2
+
3
+ import assert from 'node:assert/strict'
4
+ import { once } from 'node:events'
5
+ import { setTimeout as sleep } from 'node:timers/promises'
6
+
7
+ /**
8
+ * @param {Promise<unknown>} promise
9
+ * @param {number} timeoutMs
10
+ * @param {string} label
11
+ * @returns {Promise<unknown>}
12
+ */
13
+ export function withTimeout (promise, timeoutMs, label) {
14
+ return Promise.race([
15
+ promise,
16
+ sleep(timeoutMs).then(() => {
17
+ throw new Error(`${label} timed out after ${timeoutMs}ms`)
18
+ })
19
+ ])
20
+ }
21
+
22
+ /**
23
+ * @param {YotoDeviceModel} model
24
+ * @returns {Promise<void>}
25
+ */
26
+ export async function waitForModelReady (model) {
27
+ const started = withTimeout(once(model, 'started'), 15000, 'started')
28
+ const statusUpdated = withTimeout(once(model, 'statusUpdate'), 15000, 'statusUpdate')
29
+ const configUpdated = withTimeout(once(model, 'configUpdate'), 15000, 'configUpdate')
30
+
31
+ await model.start()
32
+ await Promise.all([started, statusUpdated, configUpdated])
33
+ await sleep(1500)
34
+ }
35
+
36
+ /**
37
+ * @param {YotoDeviceModelConfig} config
38
+ */
39
+ export function assertConfigShape (config) {
40
+ assert.ok(Array.isArray(config.alarms), 'alarms should be an array')
41
+ assert.equal(typeof config.ambientColour, 'string', 'ambientColour should be string')
42
+ assert.equal(typeof config.bluetoothEnabled, 'boolean', 'bluetoothEnabled should be boolean')
43
+ assert.equal(typeof config.btHeadphonesEnabled, 'boolean', 'btHeadphonesEnabled should be boolean')
44
+ assert.equal(typeof config.clockFace, 'string', 'clockFace should be string')
45
+ assert.equal(typeof config.dayDisplayBrightnessAuto, 'boolean', 'dayDisplayBrightnessAuto should be boolean')
46
+ if (config.dayDisplayBrightnessAuto) {
47
+ assert.equal(config.dayDisplayBrightness, null, 'dayDisplayBrightness should be null when auto')
48
+ } else {
49
+ assert.equal(typeof config.dayDisplayBrightness, 'number', 'dayDisplayBrightness should be number when not auto')
50
+ }
51
+ assert.equal(typeof config.dayTime, 'string', 'dayTime should be string')
52
+ assert.equal(typeof config.dayYotoDaily, 'string', 'dayYotoDaily should be string')
53
+ assert.equal(typeof config.dayYotoRadio, 'string', 'dayYotoRadio should be string')
54
+ assert.equal(typeof config.daySoundsOff, 'boolean', 'daySoundsOff should be boolean')
55
+ assert.equal(typeof config.displayDimBrightness, 'number', 'displayDimBrightness should be number')
56
+ assert.equal(typeof config.displayDimTimeout, 'number', 'displayDimTimeout should be number')
57
+ assert.equal(typeof config.headphonesVolumeLimited, 'boolean', 'headphonesVolumeLimited should be boolean')
58
+ assert.ok(config.hourFormat === 12 || config.hourFormat === 24, 'hourFormat should be 12 or 24')
59
+ assert.equal(typeof config.locale, 'string', 'locale should be string')
60
+ assert.equal(typeof config.logLevel, 'string', 'logLevel should be string')
61
+ assert.equal(typeof config.maxVolumeLimit, 'number', 'maxVolumeLimit should be number')
62
+ assert.equal(typeof config.nightAmbientColour, 'string', 'nightAmbientColour should be string')
63
+ assert.equal(typeof config.nightDisplayBrightnessAuto, 'boolean', 'nightDisplayBrightnessAuto should be boolean')
64
+ if (config.nightDisplayBrightnessAuto) {
65
+ assert.equal(config.nightDisplayBrightness, null, 'nightDisplayBrightness should be null when auto')
66
+ } else {
67
+ assert.equal(typeof config.nightDisplayBrightness, 'number', 'nightDisplayBrightness should be number when not auto')
68
+ }
69
+ assert.equal(typeof config.nightMaxVolumeLimit, 'number', 'nightMaxVolumeLimit should be number')
70
+ assert.equal(typeof config.nightTime, 'string', 'nightTime should be string')
71
+ assert.equal(typeof config.nightYotoDaily, 'string', 'nightYotoDaily should be string')
72
+ assert.equal(typeof config.nightYotoRadioEnabled, 'boolean', 'nightYotoRadioEnabled should be boolean')
73
+ if (config.nightYotoRadioEnabled) {
74
+ assert.equal(typeof config.nightYotoRadio, 'string', 'nightYotoRadio should be string when enabled')
75
+ } else {
76
+ assert.equal(config.nightYotoRadio, null, 'nightYotoRadio should be null when disabled')
77
+ }
78
+ assert.equal(typeof config.nightSoundsOff, 'boolean', 'nightSoundsOff should be boolean')
79
+ assert.equal(typeof config.pausePowerButton, 'boolean', 'pausePowerButton should be boolean')
80
+ assert.equal(typeof config.pauseVolumeDown, 'boolean', 'pauseVolumeDown should be boolean')
81
+ assert.equal(typeof config.repeatAll, 'boolean', 'repeatAll should be boolean')
82
+ assert.equal(typeof config.showDiagnostics, 'boolean', 'showDiagnostics should be boolean')
83
+ assert.equal(typeof config.shutdownTimeout, 'number', 'shutdownTimeout should be number')
84
+ assert.equal(typeof config.systemVolume, 'number', 'systemVolume should be number')
85
+ assert.equal(typeof config.timezone, 'string', 'timezone should be string')
86
+ assert.equal(typeof config.volumeLevel, 'string', 'volumeLevel should be string')
87
+ }
88
+
89
+ /**
90
+ * @param {YotoPlaybackState} playback
91
+ */
92
+ export function assertPlaybackShape (playback) {
93
+ assert.ok(playback, 'playback should exist')
94
+ assert.equal(typeof playback.updatedAt, 'string', 'playback.updatedAt should be string')
95
+
96
+ assert.ok(playback.cardId === null || typeof playback.cardId === 'string', 'playback.cardId should be string or null')
97
+ assert.ok(playback.source === null || typeof playback.source === 'string', 'playback.source should be string or null')
98
+ assert.ok(playback.playbackStatus === null || typeof playback.playbackStatus === 'string', 'playback.playbackStatus should be string or null')
99
+ assert.ok(playback.trackTitle === null || typeof playback.trackTitle === 'string', 'playback.trackTitle should be string or null')
100
+ assert.ok(playback.trackKey === null || typeof playback.trackKey === 'string', 'playback.trackKey should be string or null')
101
+ assert.ok(playback.chapterTitle === null || typeof playback.chapterTitle === 'string', 'playback.chapterTitle should be string or null')
102
+ assert.ok(playback.chapterKey === null || typeof playback.chapterKey === 'string', 'playback.chapterKey should be string or null')
103
+ assert.ok(playback.position === null || typeof playback.position === 'number', 'playback.position should be number or null')
104
+ assert.ok(playback.trackLength === null || typeof playback.trackLength === 'number', 'playback.trackLength should be number or null')
105
+ assert.ok(playback.streaming === null || typeof playback.streaming === 'boolean', 'playback.streaming should be boolean or null')
106
+ assert.ok(playback.sleepTimerActive === null || typeof playback.sleepTimerActive === 'boolean', 'playback.sleepTimerActive should be boolean or null')
107
+ assert.ok(playback.sleepTimerSeconds === null || typeof playback.sleepTimerSeconds === 'number', 'playback.sleepTimerSeconds should be number or null')
108
+ }
109
+
110
+ /**
111
+ * @param {string | undefined} value
112
+ * @returns {string}
113
+ */
114
+ export function toLower (value) {
115
+ return typeof value === 'string' ? value.toLowerCase() : ''
116
+ }
@@ -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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"}