homebridge-yoto 0.0.17 → 0.0.19

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/lib/auth.js CHANGED
@@ -35,7 +35,7 @@ export class YotoAuth {
35
35
  * @returns {Promise<YotoApiDeviceCodeResponse>}
36
36
  */
37
37
  async initiateDeviceFlow () {
38
- this.log.info(LOG_PREFIX.AUTH, 'Initiating device authorization flow...')
38
+ this.log.debug(LOG_PREFIX.AUTH, 'Initiating device authorization flow...')
39
39
  this.log.debug(LOG_PREFIX.AUTH, `Client ID: ${this.clientId}`)
40
40
  this.log.debug(LOG_PREFIX.AUTH, `Endpoint: ${YOTO_OAUTH_DEVICE_CODE_URL}`)
41
41
  this.log.debug(LOG_PREFIX.AUTH, `Scope: ${OAUTH_SCOPE}`)
@@ -97,7 +97,7 @@ export class YotoAuth {
97
97
  const startTime = Date.now()
98
98
  const timeout = OAUTH_DEVICE_CODE_TIMEOUT
99
99
 
100
- this.log.info(LOG_PREFIX.AUTH, 'Waiting for user authorization...')
100
+ this.log.debug(LOG_PREFIX.AUTH, 'Waiting for user authorization...')
101
101
 
102
102
  while (Date.now() - startTime < timeout) {
103
103
  try {
package/lib/platform.js CHANGED
@@ -219,7 +219,7 @@ export class YotoPlatform {
219
219
  */
220
220
  async performDeviceFlow () {
221
221
  this.log.warn(LOG_PREFIX.PLATFORM, ERROR_MESSAGES.NO_AUTH)
222
- this.log.info(LOG_PREFIX.PLATFORM, 'Starting OAuth flow...')
222
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Starting OAuth flow...')
223
223
 
224
224
  const tokenResponse = await this.auth.authorize()
225
225
 
@@ -258,7 +258,7 @@ export class YotoPlatform {
258
258
  * @param {number} expiresAt - New expiration timestamp
259
259
  */
260
260
  handleTokenRefresh (accessToken, refreshToken, expiresAt) {
261
- this.log.info(LOG_PREFIX.PLATFORM, 'Token refreshed')
261
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Token refreshed')
262
262
  this.config.accessToken = accessToken
263
263
  this.config.refreshToken = refreshToken
264
264
  this.config.tokenExpiresAt = expiresAt
@@ -329,7 +329,7 @@ export class YotoPlatform {
329
329
  * @param {PlatformAccessory<YotoAccessoryContext>} accessory - Cached accessory
330
330
  */
331
331
  configureAccessory (accessory) {
332
- this.log.info(LOG_PREFIX.PLATFORM, 'Loading accessory from cache:', accessory.displayName)
332
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Loading accessory from cache:', accessory.displayName)
333
333
 
334
334
  // Add to our tracking map
335
335
  this.accessories.set(accessory.UUID, accessory)
@@ -341,7 +341,7 @@ export class YotoPlatform {
341
341
  */
342
342
  async discoverDevices () {
343
343
  try {
344
- this.log.info(LOG_PREFIX.PLATFORM, 'Discovering Yoto devices...')
344
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Discovering Yoto devices...')
345
345
 
346
346
  // Fetch devices from API
347
347
  const devices = await this.yotoApi.getDevices()
@@ -381,7 +381,7 @@ export class YotoPlatform {
381
381
 
382
382
  if (existingAccessory) {
383
383
  // Accessory exists - update it
384
- this.log.info(LOG_PREFIX.PLATFORM, 'Restoring existing accessory:', device.name)
384
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Restoring existing accessory:', device.name)
385
385
 
386
386
  // Update context with fresh device data
387
387
  const typedAccessory = /** @type {PlatformAccessory<YotoAccessoryContext>} */ (existingAccessory)
@@ -403,7 +403,7 @@ export class YotoPlatform {
403
403
  })
404
404
  } else {
405
405
  // Create new accessory
406
- this.log.info(LOG_PREFIX.PLATFORM, 'Adding new accessory:', device.name)
406
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Adding new accessory:', device.name)
407
407
 
408
408
  // Create platform accessory
409
409
  // eslint-disable-next-line new-cap
@@ -447,7 +447,7 @@ export class YotoPlatform {
447
447
 
448
448
  for (const [uuid, accessory] of this.accessories) {
449
449
  if (!this.discoveredUUIDs.includes(uuid)) {
450
- this.log.info(LOG_PREFIX.PLATFORM, 'Removing stale accessory:', accessory.displayName)
450
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Removing stale accessory:', accessory.displayName)
451
451
  staleAccessories.push(accessory)
452
452
  }
453
453
  }
@@ -473,7 +473,7 @@ export class YotoPlatform {
473
473
  * Shutdown handler - cleanup connections
474
474
  */
475
475
  async shutdown () {
476
- this.log.info(LOG_PREFIX.PLATFORM, 'Shutting down...')
476
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Shutting down...')
477
477
 
478
478
  // Stop status polling
479
479
  this.stopStatusPolling()
@@ -514,14 +514,14 @@ export class YotoPlayerAccessory {
514
514
  * @param {YotoDeviceStatus | {status: YotoDeviceStatus}} statusMessage - Status data or wrapped status
515
515
  */
516
516
  handleStatusUpdate (statusMessage) {
517
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Raw MQTT status message:`, JSON.stringify(statusMessage, null, 2))
517
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Raw MQTT status message:`, JSON.stringify(statusMessage, null, 2))
518
518
 
519
519
  // Unwrap status if it's nested under a 'status' property
520
520
  const status = /** @type {YotoDeviceStatus} */ ('status' in statusMessage ? statusMessage.status : statusMessage)
521
521
 
522
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - batteryLevel:`, status.batteryLevel)
523
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - userVolume:`, status.userVolume)
524
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - volume:`, status.volume)
522
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - batteryLevel:`, status.batteryLevel)
523
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - userVolume:`, status.userVolume)
524
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - volume:`, status.volume)
525
525
 
526
526
  this.currentStatus = status
527
527
  this.lastUpdateTime = Date.now()
@@ -536,19 +536,19 @@ export class YotoPlayerAccessory {
536
536
  * @param {YotoPlaybackEvents | {events: YotoPlaybackEvents}} eventsMessage - Playback events or wrapped events
537
537
  */
538
538
  handleEventsUpdate (eventsMessage) {
539
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Raw MQTT events message:`, JSON.stringify(eventsMessage, null, 2))
539
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Raw MQTT events message:`, JSON.stringify(eventsMessage, null, 2))
540
540
 
541
541
  // Unwrap events if it's nested under an 'events' property
542
542
  const events = /** @type {YotoPlaybackEvents} */ ('events' in eventsMessage ? eventsMessage.events : eventsMessage)
543
543
 
544
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped events - cardId:`, events.cardId)
544
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped events - cardId:`, events.cardId)
545
545
  this.currentEvents = events
546
546
  this.lastUpdateTime = Date.now()
547
547
  this.accessory.context.lastEvents = events
548
548
 
549
- // Track active content changes
549
+ // Track active content changes with event data
550
550
  if (this.platform.config.exposeActiveContent !== false && events.cardId) {
551
- this.handleActiveContentChange(events.cardId)
551
+ this.handleActiveContentChange(events.cardId, events)
552
552
  }
553
553
 
554
554
  // Update playback-related characteristics
@@ -560,7 +560,7 @@ export class YotoPlayerAccessory {
560
560
  * @param {import('./types.js').MqttCommandResponse} response - Command response
561
561
  */
562
562
  handleCommandResponse (response) {
563
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Command response:`, response)
563
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Command response:`, response)
564
564
  }
565
565
 
566
566
  /**
@@ -799,16 +799,16 @@ export class YotoPlayerAccessory {
799
799
  * @returns {Promise<CharacteristicValue>}
800
800
  */
801
801
  async getVolume () {
802
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - currentEvents:`, this.currentEvents)
803
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - events.volume:`, this.currentEvents?.volume)
802
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - currentEvents:`, this.currentEvents)
803
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - events.volume:`, this.currentEvents?.volume)
804
804
 
805
805
  // Volume comes from events, not status
806
806
  if (!this.currentEvents || this.currentEvents.volume === undefined) {
807
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - no volume in events, returning default: 50`)
807
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - no volume in events, returning default: 50`)
808
808
  return 50
809
809
  }
810
810
  const volume = Number(this.currentEvents.volume) || 50
811
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - returning volume:`, volume)
811
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - returning volume:`, volume)
812
812
  return volume
813
813
  }
814
814
 
@@ -871,15 +871,15 @@ export class YotoPlayerAccessory {
871
871
  * @returns {Promise<CharacteristicValue>}
872
872
  */
873
873
  async getBatteryLevel () {
874
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - currentStatus:`, this.currentStatus)
875
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - batteryLevel:`, this.currentStatus?.batteryLevel)
874
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - currentStatus:`, this.currentStatus)
875
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - batteryLevel:`, this.currentStatus?.batteryLevel)
876
876
 
877
877
  if (!this.currentStatus || this.currentStatus.batteryLevel === undefined) {
878
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - returning default: 100`)
878
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - returning default: 100`)
879
879
  return 100
880
880
  }
881
881
  const battery = Number(this.currentStatus.batteryLevel) || 100
882
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - returning:`, battery)
882
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - returning:`, battery)
883
883
  return battery
884
884
  }
885
885
 
@@ -1567,8 +1567,9 @@ export class YotoPlayerAccessory {
1567
1567
  /**
1568
1568
  * Handle active content change
1569
1569
  * @param {string} cardId - Card ID
1570
+ * @param {YotoPlaybackEvents} [events] - Optional event data with track/chapter info
1570
1571
  */
1571
- async handleActiveContentChange (cardId) {
1572
+ async handleActiveContentChange (cardId, events) {
1572
1573
  // Skip if same card
1573
1574
  if (this.activeContentCardId === cardId) {
1574
1575
  return
@@ -1584,25 +1585,38 @@ export class YotoPlayerAccessory {
1584
1585
  return
1585
1586
  }
1586
1587
 
1587
- // Check if we own this card before attempting to fetch details
1588
+ this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Active card changed: ${cardId}`)
1589
+
1590
+ // Use event data if available for immediate content info
1591
+ if (events?.trackTitle || events?.chapterTitle) {
1592
+ const title = events.trackTitle || events.chapterTitle || cardId
1593
+ this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Now playing: "${title}"`)
1594
+
1595
+ if (events.chapterKey && events.chapterKey !== events.chapterTitle) {
1596
+ this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Chapter: ${events.chapterKey}`)
1597
+ }
1598
+
1599
+ // Optionally update accessory display name with current content
1600
+ if (this.platform.config.updateAccessoryName) {
1601
+ this.accessory.displayName = `${this.device.name} - ${title}`
1602
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Updated display name`)
1603
+ }
1604
+ }
1605
+
1606
+ // Check if we own this card to fetch additional details
1588
1607
  if (!this.platform.isCardOwned(cardId)) {
1589
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Playing card ${cardId} (store content - details not available)`)
1608
+ this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Card ${cardId} (not in library - using event data)`)
1590
1609
  this.activeContentInfo = null
1591
1610
  this.accessory.context.activeContentInfo = null
1592
1611
  return
1593
1612
  }
1594
1613
 
1595
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Active card changed: ${cardId}`)
1596
-
1614
+ // Attempt to fetch full card details for owned cards
1597
1615
  try {
1598
- // Fetch card details for owned cards
1599
1616
  const content = await this.platform.yotoApi.getContent(cardId)
1600
1617
  this.activeContentInfo = content
1601
1618
 
1602
- // Log content information
1603
- const title = content.card?.title || 'Unknown'
1604
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Now playing: "${title}"`)
1605
-
1619
+ // Log additional metadata from API
1606
1620
  if (content.card?.metadata?.author) {
1607
1621
  this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Author: ${content.card.metadata.author}`)
1608
1622
  }
@@ -1611,23 +1625,17 @@ export class YotoPlayerAccessory {
1611
1625
  this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Category: ${content.card.metadata.category}`)
1612
1626
  }
1613
1627
 
1614
- // Optionally update accessory display name with current content
1615
- if (this.platform.config.updateAccessoryName && content.card?.title) {
1616
- this.accessory.displayName = `${this.device.name} - ${content.card.title}`
1617
- this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Updated display name`)
1618
- }
1619
-
1620
1628
  // Store in context for persistence
1621
1629
  this.accessory.context.activeContentInfo = this.activeContentInfo
1622
1630
  } catch (error) {
1623
1631
  // Handle 403 Forbidden (store-bought cards user doesn't own)
1624
1632
  if (error instanceof Error && error.message.includes('403')) {
1625
- this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Playing card ${cardId} (store content - details not available)`)
1633
+ this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Card ${cardId} API access denied (using event data)`)
1626
1634
  this.activeContentInfo = null
1627
1635
  this.accessory.context.activeContentInfo = null
1628
1636
  } else if (error instanceof Error && error.message.includes('404')) {
1629
1637
  // Card not found - might be deleted or invalid
1630
- this.log.warn(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Card ${cardId} not found`)
1638
+ this.log.warn(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Card ${cardId} not found in API`)
1631
1639
  this.activeContentInfo = null
1632
1640
  this.accessory.context.activeContentInfo = null
1633
1641
  } else {
package/lib/yotoApi.js CHANGED
@@ -59,7 +59,7 @@ export class YotoApi {
59
59
 
60
60
  // Check if token needs refresh
61
61
  if (this.auth.isTokenExpired(this.tokenExpiresAt)) {
62
- this.log.info(LOG_PREFIX.API, ERROR_MESSAGES.TOKEN_EXPIRED)
62
+ this.log.debug(LOG_PREFIX.API, ERROR_MESSAGES.TOKEN_EXPIRED)
63
63
 
64
64
  if (!this.refreshToken) {
65
65
  throw new Error(ERROR_MESSAGES.TOKEN_REFRESH_FAILED)
package/lib/yotoMqtt.js CHANGED
@@ -65,7 +65,7 @@ export class YotoMqtt extends EventEmitter {
65
65
  }
66
66
 
67
67
  return new Promise((resolve, reject) => {
68
- this.log.info(LOG_PREFIX.MQTT, `Connecting to ${this.brokerUrl}...`)
68
+ this.log.debug(LOG_PREFIX.MQTT, `Connecting to ${this.brokerUrl}...`)
69
69
 
70
70
  const clientId = `DASH${deviceId}`
71
71
  const username = `${deviceId}?x-amz-customauthorizer-name=${YOTO_MQTT_AUTH_NAME}`
@@ -144,7 +144,7 @@ export class YotoMqtt extends EventEmitter {
144
144
 
145
145
  this.client.on('reconnect', () => {
146
146
  this.reconnectAttempts++
147
- this.log.info(LOG_PREFIX.MQTT, `Reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}`)
147
+ this.log.debug(LOG_PREFIX.MQTT, `Reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}`)
148
148
 
149
149
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
150
150
  this.log.error(LOG_PREFIX.MQTT, 'Max reconnection attempts reached, stopping reconnection')
@@ -201,14 +201,14 @@ export class YotoMqtt extends EventEmitter {
201
201
  }
202
202
 
203
203
  return new Promise((resolve) => {
204
- this.log.info(LOG_PREFIX.MQTT, 'Disconnecting from MQTT broker...')
204
+ this.log.debug(LOG_PREFIX.MQTT, 'Disconnecting from MQTT broker...')
205
205
 
206
206
  if (this.client) {
207
207
  this.client.end(false, {}, () => {
208
208
  this.connected = false
209
209
  this.client = null
210
210
  this.subscribedDevices.clear()
211
- this.log.info(LOG_PREFIX.MQTT, 'Disconnected')
211
+ this.log.debug(LOG_PREFIX.MQTT, 'Disconnected')
212
212
  resolve()
213
213
  })
214
214
  } else {
@@ -246,7 +246,7 @@ export class YotoMqtt extends EventEmitter {
246
246
  return
247
247
  }
248
248
 
249
- this.log.info(LOG_PREFIX.MQTT, `Resubscribing to ${this.subscribedDevices.size} device(s)...`)
249
+ this.log.debug(LOG_PREFIX.MQTT, `Resubscribing to ${this.subscribedDevices.size} device(s)...`)
250
250
 
251
251
  for (const deviceId of this.subscribedDevices) {
252
252
  const callbacks = this.deviceCallbacks.get(deviceId)
@@ -281,7 +281,7 @@ export class YotoMqtt extends EventEmitter {
281
281
  return
282
282
  }
283
283
 
284
- this.log.info(LOG_PREFIX.MQTT, `Subscribing to device ${deviceId}...`)
284
+ this.log.debug(LOG_PREFIX.MQTT, `Subscribing to device ${deviceId}...`)
285
285
 
286
286
  const topics = [
287
287
  this.buildTopic(MQTT_TOPIC_DATA_STATUS, deviceId),
@@ -331,7 +331,7 @@ export class YotoMqtt extends EventEmitter {
331
331
  return
332
332
  }
333
333
 
334
- this.log.info(LOG_PREFIX.MQTT, `Unsubscribing from device ${deviceId}...`)
334
+ this.log.debug(LOG_PREFIX.MQTT, `Unsubscribing from device ${deviceId}...`)
335
335
 
336
336
  const topics = [
337
337
  this.buildTopic(MQTT_TOPIC_DATA_STATUS, deviceId),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-yoto",
3
3
  "description": "Control your Yoto players through Apple HomeKit with real-time MQTT updates",
4
- "version": "0.0.17",
4
+ "version": "0.0.19",
5
5
  "author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/bcomnes/homebridge-yoto/issues"