homebridge-yoto 0.0.13 → 0.0.15

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/platform.js CHANGED
@@ -56,6 +56,10 @@ export class YotoPlatform {
56
56
  // Status polling interval
57
57
  this.statusPollInterval = null
58
58
 
59
+ // Cache of user's owned MYO card IDs
60
+ /** @type {Set<string>} */
61
+ this.ownedCardIds = new Set()
62
+
59
63
  // Initialize API clients
60
64
  this.auth = new YotoAuth(log, this.config.clientId)
61
65
  this.yotoApi = new YotoApi(log, this.auth)
@@ -108,6 +112,9 @@ export class YotoPlatform {
108
112
  throw error
109
113
  }
110
114
 
115
+ // Fetch user's owned cards for lookup optimization
116
+ await this.fetchOwnedCards()
117
+
111
118
  // Start platform-level status polling (every 60 seconds)
112
119
  this.startStatusPolling()
113
120
  } catch (error) {
@@ -173,6 +180,40 @@ export class YotoPlatform {
173
180
  }
174
181
 
175
182
  /**
183
+ * Fetch user's owned MYO cards and cache their IDs
184
+ * @returns {Promise<void>}
185
+ */
186
+ async fetchOwnedCards () {
187
+ try {
188
+ this.log.debug(LOG_PREFIX.PLATFORM, 'Fetching user\'s owned cards...')
189
+ const myContent = await this.yotoApi.getMyContent()
190
+
191
+ // Cache card IDs
192
+ if (myContent.cards && Array.isArray(myContent.cards)) {
193
+ this.ownedCardIds.clear()
194
+ for (const card of myContent.cards) {
195
+ if (card.cardId) {
196
+ this.ownedCardIds.add(card.cardId)
197
+ }
198
+ }
199
+ this.log.info(LOG_PREFIX.PLATFORM, `✓ Cached ${this.ownedCardIds.size} owned card(s)`)
200
+ }
201
+ } catch (error) {
202
+ this.log.warn(LOG_PREFIX.PLATFORM, 'Failed to fetch owned cards, card details may be limited:', error)
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Check if a card is owned by the user
208
+ * @param {string} cardId - Card ID to check
209
+ * @returns {boolean}
210
+ */
211
+ isCardOwned (cardId) {
212
+ return this.ownedCardIds.has(cardId)
213
+ }
214
+
215
+ /**
216
+ * Handle token refresh - update config
176
217
  * Perform device authorization flow
177
218
  * @returns {Promise<void>}
178
219
  */
@@ -510,16 +510,20 @@ export class YotoPlayerAccessory {
510
510
  }
511
511
 
512
512
  /**
513
- * Handle device status update from MQTT
514
- * @param {YotoDeviceStatus} status - Device status
513
+ * Handle status update from MQTT
514
+ * @param {YotoDeviceStatus} status - Status data
515
515
  */
516
516
  handleStatusUpdate (status) {
517
- this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Status update received`)
517
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Received status update:`, JSON.stringify(status, null, 2))
518
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Status - batteryLevel:`, status.batteryLevel)
519
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Status - userVolume:`, status.userVolume)
520
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Status - volume:`, status.volume)
521
+
518
522
  this.currentStatus = status
519
523
  this.lastUpdateTime = Date.now()
520
524
  this.accessory.context.lastStatus = status
525
+ this.accessory.context.lastUpdate = this.lastUpdateTime
521
526
 
522
- // Update all characteristics
523
527
  this.updateCharacteristics()
524
528
  }
525
529
 
@@ -785,10 +789,16 @@ export class YotoPlayerAccessory {
785
789
  * @returns {Promise<CharacteristicValue>}
786
790
  */
787
791
  async getVolume () {
792
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - currentStatus:`, this.currentStatus)
793
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - userVolume:`, this.currentStatus?.userVolume)
794
+
788
795
  if (!this.currentStatus || this.currentStatus.userVolume === undefined) {
796
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - returning default: 50`)
789
797
  return 50
790
798
  }
791
- return Number(this.currentStatus.userVolume) || 50
799
+ const volume = Number(this.currentStatus.userVolume) || 50
800
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - returning:`, volume)
801
+ return volume
792
802
  }
793
803
 
794
804
  /**
@@ -849,10 +859,16 @@ export class YotoPlayerAccessory {
849
859
  * @returns {Promise<CharacteristicValue>}
850
860
  */
851
861
  async getBatteryLevel () {
862
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - currentStatus:`, this.currentStatus)
863
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - batteryLevel:`, this.currentStatus?.batteryLevel)
864
+
852
865
  if (!this.currentStatus || this.currentStatus.batteryLevel === undefined) {
866
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - returning default: 100`)
853
867
  return 100
854
868
  }
855
- return Number(this.currentStatus.batteryLevel) || 100
869
+ const battery = Number(this.currentStatus.batteryLevel) || 100
870
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getBatteryLevel - returning:`, battery)
871
+ return battery
856
872
  }
857
873
 
858
874
  /**
@@ -1536,10 +1552,18 @@ export class YotoPlayerAccessory {
1536
1552
  return
1537
1553
  }
1538
1554
 
1555
+ // Check if we own this card before attempting to fetch details
1556
+ if (!this.platform.isCardOwned(cardId)) {
1557
+ this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Playing card ${cardId} (store content - details not available)`)
1558
+ this.activeContentInfo = null
1559
+ this.accessory.context.activeContentInfo = null
1560
+ return
1561
+ }
1562
+
1539
1563
  this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Active card changed: ${cardId}`)
1540
1564
 
1541
1565
  try {
1542
- // Fetch card details
1566
+ // Fetch card details for owned cards
1543
1567
  const content = await this.platform.yotoApi.getContent(cardId)
1544
1568
  this.activeContentInfo = content
1545
1569
 
package/lib/yotoApi.js CHANGED
@@ -114,7 +114,6 @@ export class YotoApi {
114
114
 
115
115
  if (!response.ok) {
116
116
  const errorText = await response.text()
117
- this.log.error(LOG_PREFIX.API, `Request failed: ${response.status} ${errorText}`)
118
117
 
119
118
  // Handle 401 by attempting token refresh once
120
119
  if (response.status === 401 && !options._retried) {
@@ -124,6 +123,13 @@ export class YotoApi {
124
123
  return this.request(endpoint, { ...options, _retried: true })
125
124
  }
126
125
 
126
+ // Reduce noise for expected errors (403/404 on content endpoints)
127
+ if ((response.status === 403 || response.status === 404) && endpoint.startsWith('/content/')) {
128
+ this.log.debug(LOG_PREFIX.API, `Request failed: ${response.status} ${errorText}`)
129
+ } else {
130
+ this.log.error(LOG_PREFIX.API, `Request failed: ${response.status} ${errorText}`)
131
+ }
132
+
127
133
  throw new Error(`API request failed: ${response.status} ${errorText}`)
128
134
  }
129
135
 
@@ -134,7 +140,10 @@ export class YotoApi {
134
140
 
135
141
  return await response.json()
136
142
  } catch (error) {
137
- this.log.error(LOG_PREFIX.API, `${ERROR_MESSAGES.API_ERROR}:`, error)
143
+ // Only log non-API errors (network issues, etc.)
144
+ if (!(error instanceof Error && error.message.startsWith('API request failed'))) {
145
+ this.log.error(LOG_PREFIX.API, `${ERROR_MESSAGES.API_ERROR}:`, error)
146
+ }
138
147
  throw error
139
148
  }
140
149
  }
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.13",
4
+ "version": "0.0.15",
5
5
  "author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/bcomnes/homebridge-yoto/issues"