homebridge-yoto 0.0.19 → 0.0.20

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.
@@ -38,6 +38,9 @@ export class YotoPlayerAccessory {
38
38
  this.currentEvents = accessory.context.lastEvents || null
39
39
  this.lastUpdateTime = Date.now()
40
40
 
41
+ // Cache for device config
42
+ this.deviceConfig = null
43
+
41
44
  // Create dedicated MQTT client for this device
42
45
  this.mqtt = new YotoMqtt(this.log)
43
46
 
@@ -110,6 +113,15 @@ export class YotoPlayerAccessory {
110
113
  * @returns {Promise<void>}
111
114
  */
112
115
  async initialize () {
116
+ // Fetch device config for cached access
117
+ try {
118
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
119
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Device config loaded`)
120
+ } catch (error) {
121
+ this.log.warn(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to load device config:`, error)
122
+ // Continue without config - getters will return defaults
123
+ }
124
+
113
125
  await this.connectMqtt()
114
126
  // Status polling is handled at platform level
115
127
  }
@@ -977,20 +989,23 @@ export class YotoPlayerAccessory {
977
989
  this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set display on:`, value)
978
990
 
979
991
  try {
980
- // Get current config to update brightness settings
981
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
992
+ // Refresh config if not cached
993
+ if (!this.deviceConfig) {
994
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
995
+ }
982
996
 
983
997
  if (value) {
984
- // Turn on - restore to auto or previous brightness
985
- config.config.dayDisplayBrightness = 'auto'
986
- config.config.nightDisplayBrightness = 'auto'
998
+ // Turn on - restore to previous brightness or default
999
+ const brightness = this.currentStatus?.dnowBrightness || 100
1000
+ this.deviceConfig.config.dayDisplayBrightness = String(brightness)
1001
+ this.deviceConfig.config.nightDisplayBrightness = String(brightness)
987
1002
  } else {
988
1003
  // Turn off - set brightness to 0
989
- config.config.dayDisplayBrightness = '0'
990
- config.config.nightDisplayBrightness = '0'
1004
+ this.deviceConfig.config.dayDisplayBrightness = '0'
1005
+ this.deviceConfig.config.nightDisplayBrightness = '0'
991
1006
  }
992
1007
 
993
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1008
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
994
1009
  } catch (error) {
995
1010
  this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set display on:`, error)
996
1011
  throw new this.platform.api.hap.HapStatusError(
@@ -1024,16 +1039,19 @@ export class YotoPlayerAccessory {
1024
1039
  this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set display brightness:`, value)
1025
1040
 
1026
1041
  try {
1027
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1042
+ // Refresh config if not cached
1043
+ if (!this.deviceConfig) {
1044
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1045
+ }
1028
1046
 
1029
- // Map 0-100 to brightness string
1030
- const brightness = String(Math.round(Number(value)))
1047
+ // Clamp brightness to 0-100
1048
+ const brightness = Math.max(0, Math.min(100, Number(value)))
1031
1049
 
1032
1050
  // Update both day and night brightness
1033
- config.config.dayDisplayBrightness = brightness
1034
- config.config.nightDisplayBrightness = brightness
1051
+ this.deviceConfig.config.dayDisplayBrightness = String(brightness)
1052
+ this.deviceConfig.config.nightDisplayBrightness = String(brightness)
1035
1053
 
1036
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1054
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
1037
1055
  } catch (error) {
1038
1056
  this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set display brightness:`, error)
1039
1057
  throw new this.platform.api.hap.HapStatusError(
@@ -1058,12 +1076,16 @@ export class YotoPlayerAccessory {
1058
1076
  * @param {CharacteristicValue} value - Enabled state
1059
1077
  */
1060
1078
  async setBluetoothEnabled (value) {
1061
- this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set Bluetooth enabled:`, value)
1079
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set Bluetooth:`, value)
1062
1080
 
1063
1081
  try {
1064
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1065
- config.config.bluetoothEnabled = value ? '1' : '0'
1066
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1082
+ // Refresh config if not cached
1083
+ if (!this.deviceConfig) {
1084
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1085
+ }
1086
+
1087
+ this.deviceConfig.config.bluetoothEnabled = value ? 'true' : 'false'
1088
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
1067
1089
  } catch (error) {
1068
1090
  this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set Bluetooth:`, error)
1069
1091
  throw new this.platform.api.hap.HapStatusError(
@@ -1091,11 +1113,15 @@ export class YotoPlayerAccessory {
1091
1113
  this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set repeat all:`, value)
1092
1114
 
1093
1115
  try {
1094
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1095
- config.config.repeatAll = Boolean(value)
1096
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1116
+ // Refresh config if not cached
1117
+ if (!this.deviceConfig) {
1118
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1119
+ }
1120
+
1121
+ this.deviceConfig.config.repeatAll = Boolean(value)
1122
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
1097
1123
  } catch (error) {
1098
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set repeat:`, error)
1124
+ this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set repeat all:`, error)
1099
1125
  throw new this.platform.api.hap.HapStatusError(
1100
1126
  this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE
1101
1127
  )
@@ -1118,12 +1144,16 @@ export class YotoPlayerAccessory {
1118
1144
  * @param {CharacteristicValue} value - Enabled state
1119
1145
  */
1120
1146
  async setBtHeadphonesEnabled (value) {
1121
- this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set BT headphones enabled:`, value)
1147
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set BT headphones:`, value)
1122
1148
 
1123
1149
  try {
1124
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1125
- config.config.btHeadphonesEnabled = Boolean(value)
1126
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1150
+ // Refresh config if not cached
1151
+ if (!this.deviceConfig) {
1152
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1153
+ }
1154
+
1155
+ this.deviceConfig.config.btHeadphonesEnabled = Boolean(value)
1156
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
1127
1157
  } catch (error) {
1128
1158
  this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set BT headphones:`, error)
1129
1159
  throw new this.platform.api.hap.HapStatusError(
@@ -1233,16 +1263,19 @@ export class YotoPlayerAccessory {
1233
1263
  * @returns {Promise<CharacteristicValue>}
1234
1264
  */
1235
1265
  async getDayVolumeLimit () {
1266
+ if (!this.deviceConfig) {
1267
+ return 100 // Default to max
1268
+ }
1269
+
1236
1270
  try {
1237
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1238
- const limit = parseInt(config.config.maxVolumeLimit || '16')
1271
+ const limit = parseInt(this.deviceConfig.config.maxVolumeLimit || '16')
1239
1272
  if (isNaN(limit)) {
1240
1273
  return 100
1241
1274
  }
1242
1275
  // Map 0-16 to 0-100
1243
1276
  return Math.round((limit / 16) * 100)
1244
1277
  } catch (error) {
1245
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to get day volume limit:`, error)
1278
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse day volume limit:`, error)
1246
1279
  return 100
1247
1280
  }
1248
1281
  }
@@ -1255,11 +1288,15 @@ export class YotoPlayerAccessory {
1255
1288
  this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set day volume limit:`, value)
1256
1289
 
1257
1290
  try {
1258
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1291
+ // Refresh config if not cached
1292
+ if (!this.deviceConfig) {
1293
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1294
+ }
1295
+
1259
1296
  // Map 0-100 to 0-16
1260
1297
  const limit = Math.round((Number(value) / 100) * 16)
1261
- config.config.maxVolumeLimit = String(limit)
1262
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1298
+ this.deviceConfig.config.maxVolumeLimit = String(limit)
1299
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
1263
1300
  } catch (error) {
1264
1301
  this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set day volume limit:`, error)
1265
1302
  throw new this.platform.api.hap.HapStatusError(
@@ -1291,16 +1328,19 @@ export class YotoPlayerAccessory {
1291
1328
  * @returns {Promise<CharacteristicValue>}
1292
1329
  */
1293
1330
  async getNightVolumeLimit () {
1331
+ if (!this.deviceConfig) {
1332
+ return 100 // Default to max
1333
+ }
1334
+
1294
1335
  try {
1295
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1296
- const limit = parseInt(config.config.nightMaxVolumeLimit || '16')
1336
+ const limit = parseInt(this.deviceConfig.config.nightMaxVolumeLimit || '16')
1297
1337
  if (isNaN(limit)) {
1298
1338
  return 100
1299
1339
  }
1300
1340
  // Map 0-16 to 0-100
1301
1341
  return Math.round((limit / 16) * 100)
1302
1342
  } catch (error) {
1303
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to get night volume limit:`, error)
1343
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse night volume limit:`, error)
1304
1344
  return 100
1305
1345
  }
1306
1346
  }
@@ -1313,11 +1353,15 @@ export class YotoPlayerAccessory {
1313
1353
  this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set night volume limit:`, value)
1314
1354
 
1315
1355
  try {
1316
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1356
+ // Refresh config if not cached
1357
+ if (!this.deviceConfig) {
1358
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1359
+ }
1360
+
1317
1361
  // Map 0-100 to 0-16
1318
1362
  const limit = Math.round((Number(value) / 100) * 16)
1319
- config.config.nightMaxVolumeLimit = String(limit)
1320
- await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, config)
1363
+ this.deviceConfig.config.nightMaxVolumeLimit = String(limit)
1364
+ await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
1321
1365
  } catch (error) {
1322
1366
  this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set night volume limit:`, error)
1323
1367
  throw new this.platform.api.hap.HapStatusError(
@@ -1331,13 +1375,16 @@ export class YotoPlayerAccessory {
1331
1375
  * @returns {Promise<CharacteristicValue>}
1332
1376
  */
1333
1377
  async getAmbientLightOn () {
1378
+ if (!this.deviceConfig) {
1379
+ return false // Default to off
1380
+ }
1381
+
1334
1382
  try {
1335
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1336
- const color = config.config.ambientColour || '#000000'
1337
- // Off if color is black (#000000)
1338
- return color !== '#000000' && color !== 'off'
1383
+ const color = this.deviceConfig.config.ambientColour || '000000'
1384
+ // Consider light "on" if not pure black
1385
+ return color !== '000000'
1339
1386
  } catch (error) {
1340
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to get ambient light state:`, error)
1387
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient light state:`, error)
1341
1388
  return false
1342
1389
  }
1343
1390
  }
@@ -1350,11 +1397,16 @@ export class YotoPlayerAccessory {
1350
1397
  this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set ambient light on:`, value)
1351
1398
 
1352
1399
  try {
1400
+ // Refresh config if not cached
1401
+ if (!this.deviceConfig) {
1402
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1403
+ }
1404
+
1353
1405
  if (value) {
1354
- // Turn on - set to white or previous color
1355
- await this.mqtt.setAmbientLight(this.device.deviceId, 255, 255, 255)
1406
+ // Restore to a default color (warm white) when turning on
1407
+ await this.updateAmbientLightColor()
1356
1408
  } else {
1357
- // Turn off - set to black
1409
+ // Turn off by setting to black
1358
1410
  await this.mqtt.setAmbientLight(this.device.deviceId, 0, 0, 0)
1359
1411
  }
1360
1412
  } catch (error) {
@@ -1370,16 +1422,19 @@ export class YotoPlayerAccessory {
1370
1422
  * @returns {Promise<CharacteristicValue>}
1371
1423
  */
1372
1424
  async getAmbientLightHue () {
1425
+ if (!this.deviceConfig) {
1426
+ return 0 // Default hue
1427
+ }
1428
+
1373
1429
  try {
1374
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1375
- const hex = config.config.ambientColour || '#000000'
1430
+ const hex = this.deviceConfig.config.ambientColour || '000000'
1376
1431
  const { h } = this.hexToHsv(hex)
1377
1432
  if (isNaN(h)) {
1378
1433
  return 0
1379
1434
  }
1380
1435
  return h
1381
1436
  } catch (error) {
1382
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to get ambient light hue:`, error)
1437
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient light hue:`, error)
1383
1438
  return 0
1384
1439
  }
1385
1440
  }
@@ -1400,16 +1455,19 @@ export class YotoPlayerAccessory {
1400
1455
  * @returns {Promise<CharacteristicValue>}
1401
1456
  */
1402
1457
  async getAmbientLightSaturation () {
1458
+ if (!this.deviceConfig) {
1459
+ return 0 // Default saturation
1460
+ }
1461
+
1403
1462
  try {
1404
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1405
- const hex = config.config.ambientColour || '#000000'
1463
+ const hex = this.deviceConfig.config.ambientColour || '000000'
1406
1464
  const { s } = this.hexToHsv(hex)
1407
1465
  if (isNaN(s)) {
1408
1466
  return 0
1409
1467
  }
1410
1468
  return s
1411
1469
  } catch (error) {
1412
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to get ambient light saturation:`, error)
1470
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient light saturation:`, error)
1413
1471
  return 0
1414
1472
  }
1415
1473
  }
@@ -1430,18 +1488,21 @@ export class YotoPlayerAccessory {
1430
1488
  * @returns {Promise<CharacteristicValue>}
1431
1489
  */
1432
1490
  async getAmbientLightBrightness () {
1491
+ if (!this.deviceConfig) {
1492
+ return 0 // Default brightness
1493
+ }
1494
+
1433
1495
  try {
1434
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1435
- const hex = config.config.ambientColour
1496
+ const hex = this.deviceConfig.config.ambientColour || '000000'
1436
1497
  const { v } = this.hexToHsv(hex)
1437
1498
  const brightness = Math.round(v)
1438
1499
  if (isNaN(brightness)) {
1439
- return 100
1500
+ return 0
1440
1501
  }
1441
1502
  return brightness
1442
1503
  } catch (error) {
1443
- this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to get ambient brightness:`, error)
1444
- return 100
1504
+ this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient brightness:`, error)
1505
+ return 0
1445
1506
  }
1446
1507
  }
1447
1508
 
@@ -1461,9 +1522,12 @@ export class YotoPlayerAccessory {
1461
1522
  */
1462
1523
  async updateAmbientLightColor () {
1463
1524
  try {
1464
- // Get current config to read existing HSV values
1465
- const config = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1466
- const currentHex = config.config.ambientColour || '#ffffff'
1525
+ // Refresh config if not cached
1526
+ if (!this.deviceConfig) {
1527
+ this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
1528
+ }
1529
+
1530
+ const currentHex = this.deviceConfig.config.ambientColour || '000000'
1467
1531
  const currentHsv = this.hexToHsv(currentHex)
1468
1532
 
1469
1533
  // Use pending values or current values
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.19",
4
+ "version": "0.0.20",
5
5
  "author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/bcomnes/homebridge-yoto/issues"