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 +41 -0
- package/lib/playerAccessory.js +31 -7
- package/lib/yotoApi.js +11 -2
- package/package.json +1 -1
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
|
*/
|
package/lib/playerAccessory.js
CHANGED
|
@@ -510,16 +510,20 @@ export class YotoPlayerAccessory {
|
|
|
510
510
|
}
|
|
511
511
|
|
|
512
512
|
/**
|
|
513
|
-
* Handle
|
|
514
|
-
* @param {YotoDeviceStatus} 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}]
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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"
|