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.
- package/lib/playerAccessory.js +124 -60
- package/package.json +1 -1
package/lib/playerAccessory.js
CHANGED
|
@@ -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
|
-
//
|
|
981
|
-
|
|
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
|
|
985
|
-
|
|
986
|
-
|
|
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
|
-
|
|
990
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
//
|
|
1030
|
-
const brightness =
|
|
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
|
-
|
|
1034
|
-
|
|
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,
|
|
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
|
|
1079
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set Bluetooth:`, value)
|
|
1062
1080
|
|
|
1063
1081
|
try {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
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
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
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
|
|
1147
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set BT headphones:`, value)
|
|
1122
1148
|
|
|
1123
1149
|
try {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1262
|
-
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId,
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1320
|
-
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId,
|
|
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
|
|
1336
|
-
|
|
1337
|
-
|
|
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.
|
|
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
|
-
//
|
|
1355
|
-
await this.
|
|
1406
|
+
// Restore to a default color (warm white) when turning on
|
|
1407
|
+
await this.updateAmbientLightColor()
|
|
1356
1408
|
} else {
|
|
1357
|
-
// Turn off
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
1500
|
+
return 0
|
|
1440
1501
|
}
|
|
1441
1502
|
return brightness
|
|
1442
1503
|
} catch (error) {
|
|
1443
|
-
this.log.
|
|
1444
|
-
return
|
|
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
|
-
//
|
|
1465
|
-
|
|
1466
|
-
|
|
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.
|
|
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"
|