homebridge-yoto 0.0.19 → 0.0.21
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 +153 -77
- package/lib/yotoMqtt.js +9 -23
- 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
|
}
|
|
@@ -455,7 +467,7 @@ export class YotoPlayerAccessory {
|
|
|
455
467
|
try {
|
|
456
468
|
// Check if device is online first
|
|
457
469
|
if (!this.device.online) {
|
|
458
|
-
this.log.
|
|
470
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Device is offline, skipping MQTT connection`)
|
|
459
471
|
return
|
|
460
472
|
}
|
|
461
473
|
|
|
@@ -465,12 +477,7 @@ export class YotoPlayerAccessory {
|
|
|
465
477
|
return
|
|
466
478
|
}
|
|
467
479
|
|
|
468
|
-
|
|
469
|
-
this.log.warn(LOG_PREFIX.ACCESSORY, `[${this.device.name}] MQTT Connection Details:`)
|
|
470
|
-
this.log.warn(LOG_PREFIX.ACCESSORY, ` Device Online: ${this.device.online}`)
|
|
471
|
-
this.log.warn(LOG_PREFIX.ACCESSORY, ` Device ID: ${this.device.deviceId}`)
|
|
472
|
-
this.log.warn(LOG_PREFIX.ACCESSORY, ` Access Token: ${this.platform.config.accessToken}`)
|
|
473
|
-
this.log.warn(LOG_PREFIX.ACCESSORY, ` Token Length: ${this.platform.config.accessToken.length}`)
|
|
480
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Connecting to MQTT (device online: ${this.device.online})`)
|
|
474
481
|
|
|
475
482
|
// Connect MQTT with device ID and access token
|
|
476
483
|
await this.mqtt.connect(
|
|
@@ -491,12 +498,12 @@ export class YotoPlayerAccessory {
|
|
|
491
498
|
// Set up MQTT event listeners
|
|
492
499
|
this.mqtt.on('disconnected', () => {
|
|
493
500
|
this.mqttConnected = false
|
|
494
|
-
this.log.
|
|
501
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] MQTT disconnected`)
|
|
495
502
|
})
|
|
496
503
|
|
|
497
504
|
this.mqtt.on('offline', () => {
|
|
498
505
|
this.mqttConnected = false
|
|
499
|
-
this.log.
|
|
506
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] MQTT offline`)
|
|
500
507
|
})
|
|
501
508
|
|
|
502
509
|
this.mqtt.on('connected', () => {
|
|
@@ -604,12 +611,18 @@ export class YotoPlayerAccessory {
|
|
|
604
611
|
|
|
605
612
|
// Update temperature
|
|
606
613
|
if (this.temperatureService && this.currentStatus.temp) {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
614
|
+
// Parse temp string format "x:temp:y" to get middle value
|
|
615
|
+
const tempParts = this.currentStatus.temp.split(':')
|
|
616
|
+
if (tempParts.length >= 2) {
|
|
617
|
+
const temp = Number(tempParts[1])
|
|
618
|
+
if (!isNaN(temp)) {
|
|
619
|
+
// Clamp to HomeKit's valid range: 0-100°C
|
|
620
|
+
const clampedTemp = Math.max(0, Math.min(100, temp))
|
|
621
|
+
this.temperatureService.updateCharacteristic(
|
|
622
|
+
this.platform.Characteristic.CurrentTemperature,
|
|
623
|
+
clampedTemp
|
|
624
|
+
)
|
|
625
|
+
}
|
|
613
626
|
}
|
|
614
627
|
|
|
615
628
|
// Update display brightness
|
|
@@ -917,11 +930,22 @@ export class YotoPlayerAccessory {
|
|
|
917
930
|
*/
|
|
918
931
|
async getCurrentTemperature () {
|
|
919
932
|
if (!this.currentStatus || !this.currentStatus.temp) {
|
|
933
|
+
return 20 // Default room temperature
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Parse temp string format "x:temp:y" to get middle value (actual temperature)
|
|
937
|
+
const tempParts = this.currentStatus.temp.split(':')
|
|
938
|
+
if (tempParts.length < 2) {
|
|
939
|
+
return 20
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const temp = Number(tempParts[1])
|
|
943
|
+
if (isNaN(temp)) {
|
|
920
944
|
return 20
|
|
921
945
|
}
|
|
922
946
|
|
|
923
|
-
|
|
924
|
-
return
|
|
947
|
+
// Clamp to HomeKit's valid range: 0-100°C
|
|
948
|
+
return Math.max(0, Math.min(100, temp))
|
|
925
949
|
}
|
|
926
950
|
|
|
927
951
|
/**
|
|
@@ -977,20 +1001,23 @@ export class YotoPlayerAccessory {
|
|
|
977
1001
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set display on:`, value)
|
|
978
1002
|
|
|
979
1003
|
try {
|
|
980
|
-
//
|
|
981
|
-
|
|
1004
|
+
// Refresh config if not cached
|
|
1005
|
+
if (!this.deviceConfig) {
|
|
1006
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1007
|
+
}
|
|
982
1008
|
|
|
983
1009
|
if (value) {
|
|
984
|
-
// Turn on - restore to
|
|
985
|
-
|
|
986
|
-
|
|
1010
|
+
// Turn on - restore to previous brightness or default
|
|
1011
|
+
const brightness = this.currentStatus?.dnowBrightness || 100
|
|
1012
|
+
this.deviceConfig.config.dayDisplayBrightness = String(brightness)
|
|
1013
|
+
this.deviceConfig.config.nightDisplayBrightness = String(brightness)
|
|
987
1014
|
} else {
|
|
988
1015
|
// Turn off - set brightness to 0
|
|
989
|
-
|
|
990
|
-
|
|
1016
|
+
this.deviceConfig.config.dayDisplayBrightness = '0'
|
|
1017
|
+
this.deviceConfig.config.nightDisplayBrightness = '0'
|
|
991
1018
|
}
|
|
992
1019
|
|
|
993
|
-
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId,
|
|
1020
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
994
1021
|
} catch (error) {
|
|
995
1022
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set display on:`, error)
|
|
996
1023
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -1024,16 +1051,19 @@ export class YotoPlayerAccessory {
|
|
|
1024
1051
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set display brightness:`, value)
|
|
1025
1052
|
|
|
1026
1053
|
try {
|
|
1027
|
-
|
|
1054
|
+
// Refresh config if not cached
|
|
1055
|
+
if (!this.deviceConfig) {
|
|
1056
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1057
|
+
}
|
|
1028
1058
|
|
|
1029
|
-
//
|
|
1030
|
-
const brightness =
|
|
1059
|
+
// Clamp brightness to 0-100
|
|
1060
|
+
const brightness = Math.max(0, Math.min(100, Number(value)))
|
|
1031
1061
|
|
|
1032
1062
|
// Update both day and night brightness
|
|
1033
|
-
|
|
1034
|
-
|
|
1063
|
+
this.deviceConfig.config.dayDisplayBrightness = String(brightness)
|
|
1064
|
+
this.deviceConfig.config.nightDisplayBrightness = String(brightness)
|
|
1035
1065
|
|
|
1036
|
-
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId,
|
|
1066
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
1037
1067
|
} catch (error) {
|
|
1038
1068
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set display brightness:`, error)
|
|
1039
1069
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -1058,12 +1088,16 @@ export class YotoPlayerAccessory {
|
|
|
1058
1088
|
* @param {CharacteristicValue} value - Enabled state
|
|
1059
1089
|
*/
|
|
1060
1090
|
async setBluetoothEnabled (value) {
|
|
1061
|
-
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set Bluetooth
|
|
1091
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set Bluetooth:`, value)
|
|
1062
1092
|
|
|
1063
1093
|
try {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1094
|
+
// Refresh config if not cached
|
|
1095
|
+
if (!this.deviceConfig) {
|
|
1096
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
this.deviceConfig.config.bluetoothEnabled = value ? 'true' : 'false'
|
|
1100
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
1067
1101
|
} catch (error) {
|
|
1068
1102
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set Bluetooth:`, error)
|
|
1069
1103
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -1091,11 +1125,15 @@ export class YotoPlayerAccessory {
|
|
|
1091
1125
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set repeat all:`, value)
|
|
1092
1126
|
|
|
1093
1127
|
try {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1128
|
+
// Refresh config if not cached
|
|
1129
|
+
if (!this.deviceConfig) {
|
|
1130
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
this.deviceConfig.config.repeatAll = Boolean(value)
|
|
1134
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
1097
1135
|
} catch (error) {
|
|
1098
|
-
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set repeat:`, error)
|
|
1136
|
+
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set repeat all:`, error)
|
|
1099
1137
|
throw new this.platform.api.hap.HapStatusError(
|
|
1100
1138
|
this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE
|
|
1101
1139
|
)
|
|
@@ -1118,12 +1156,16 @@ export class YotoPlayerAccessory {
|
|
|
1118
1156
|
* @param {CharacteristicValue} value - Enabled state
|
|
1119
1157
|
*/
|
|
1120
1158
|
async setBtHeadphonesEnabled (value) {
|
|
1121
|
-
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set BT headphones
|
|
1159
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set BT headphones:`, value)
|
|
1122
1160
|
|
|
1123
1161
|
try {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1162
|
+
// Refresh config if not cached
|
|
1163
|
+
if (!this.deviceConfig) {
|
|
1164
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
this.deviceConfig.config.btHeadphonesEnabled = Boolean(value)
|
|
1168
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
1127
1169
|
} catch (error) {
|
|
1128
1170
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set BT headphones:`, error)
|
|
1129
1171
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -1233,16 +1275,19 @@ export class YotoPlayerAccessory {
|
|
|
1233
1275
|
* @returns {Promise<CharacteristicValue>}
|
|
1234
1276
|
*/
|
|
1235
1277
|
async getDayVolumeLimit () {
|
|
1278
|
+
if (!this.deviceConfig) {
|
|
1279
|
+
return 100 // Default to max
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1236
1282
|
try {
|
|
1237
|
-
const
|
|
1238
|
-
const limit = parseInt(config.config.maxVolumeLimit || '16')
|
|
1283
|
+
const limit = parseInt(this.deviceConfig.config.maxVolumeLimit || '16')
|
|
1239
1284
|
if (isNaN(limit)) {
|
|
1240
1285
|
return 100
|
|
1241
1286
|
}
|
|
1242
1287
|
// Map 0-16 to 0-100
|
|
1243
1288
|
return Math.round((limit / 16) * 100)
|
|
1244
1289
|
} catch (error) {
|
|
1245
|
-
this.log.
|
|
1290
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse day volume limit:`, error)
|
|
1246
1291
|
return 100
|
|
1247
1292
|
}
|
|
1248
1293
|
}
|
|
@@ -1255,11 +1300,15 @@ export class YotoPlayerAccessory {
|
|
|
1255
1300
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set day volume limit:`, value)
|
|
1256
1301
|
|
|
1257
1302
|
try {
|
|
1258
|
-
|
|
1303
|
+
// Refresh config if not cached
|
|
1304
|
+
if (!this.deviceConfig) {
|
|
1305
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1259
1308
|
// Map 0-100 to 0-16
|
|
1260
1309
|
const limit = Math.round((Number(value) / 100) * 16)
|
|
1261
|
-
|
|
1262
|
-
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId,
|
|
1310
|
+
this.deviceConfig.config.maxVolumeLimit = String(limit)
|
|
1311
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
1263
1312
|
} catch (error) {
|
|
1264
1313
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set day volume limit:`, error)
|
|
1265
1314
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -1291,16 +1340,19 @@ export class YotoPlayerAccessory {
|
|
|
1291
1340
|
* @returns {Promise<CharacteristicValue>}
|
|
1292
1341
|
*/
|
|
1293
1342
|
async getNightVolumeLimit () {
|
|
1343
|
+
if (!this.deviceConfig) {
|
|
1344
|
+
return 100 // Default to max
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1294
1347
|
try {
|
|
1295
|
-
const
|
|
1296
|
-
const limit = parseInt(config.config.nightMaxVolumeLimit || '16')
|
|
1348
|
+
const limit = parseInt(this.deviceConfig.config.nightMaxVolumeLimit || '16')
|
|
1297
1349
|
if (isNaN(limit)) {
|
|
1298
1350
|
return 100
|
|
1299
1351
|
}
|
|
1300
1352
|
// Map 0-16 to 0-100
|
|
1301
1353
|
return Math.round((limit / 16) * 100)
|
|
1302
1354
|
} catch (error) {
|
|
1303
|
-
this.log.
|
|
1355
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse night volume limit:`, error)
|
|
1304
1356
|
return 100
|
|
1305
1357
|
}
|
|
1306
1358
|
}
|
|
@@ -1313,11 +1365,15 @@ export class YotoPlayerAccessory {
|
|
|
1313
1365
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set night volume limit:`, value)
|
|
1314
1366
|
|
|
1315
1367
|
try {
|
|
1316
|
-
|
|
1368
|
+
// Refresh config if not cached
|
|
1369
|
+
if (!this.deviceConfig) {
|
|
1370
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1317
1373
|
// Map 0-100 to 0-16
|
|
1318
1374
|
const limit = Math.round((Number(value) / 100) * 16)
|
|
1319
|
-
|
|
1320
|
-
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId,
|
|
1375
|
+
this.deviceConfig.config.nightMaxVolumeLimit = String(limit)
|
|
1376
|
+
await this.platform.yotoApi.updateDeviceConfig(this.device.deviceId, this.deviceConfig)
|
|
1321
1377
|
} catch (error) {
|
|
1322
1378
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set night volume limit:`, error)
|
|
1323
1379
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -1331,13 +1387,16 @@ export class YotoPlayerAccessory {
|
|
|
1331
1387
|
* @returns {Promise<CharacteristicValue>}
|
|
1332
1388
|
*/
|
|
1333
1389
|
async getAmbientLightOn () {
|
|
1390
|
+
if (!this.deviceConfig) {
|
|
1391
|
+
return false // Default to off
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1334
1394
|
try {
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
return color !== '#000000' && color !== 'off'
|
|
1395
|
+
const color = this.deviceConfig.config.ambientColour || '000000'
|
|
1396
|
+
// Consider light "on" if not pure black
|
|
1397
|
+
return color !== '000000'
|
|
1339
1398
|
} catch (error) {
|
|
1340
|
-
this.log.
|
|
1399
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient light state:`, error)
|
|
1341
1400
|
return false
|
|
1342
1401
|
}
|
|
1343
1402
|
}
|
|
@@ -1350,11 +1409,16 @@ export class YotoPlayerAccessory {
|
|
|
1350
1409
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set ambient light on:`, value)
|
|
1351
1410
|
|
|
1352
1411
|
try {
|
|
1412
|
+
// Refresh config if not cached
|
|
1413
|
+
if (!this.deviceConfig) {
|
|
1414
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1353
1417
|
if (value) {
|
|
1354
|
-
//
|
|
1355
|
-
await this.
|
|
1418
|
+
// Restore to a default color (warm white) when turning on
|
|
1419
|
+
await this.updateAmbientLightColor()
|
|
1356
1420
|
} else {
|
|
1357
|
-
// Turn off
|
|
1421
|
+
// Turn off by setting to black
|
|
1358
1422
|
await this.mqtt.setAmbientLight(this.device.deviceId, 0, 0, 0)
|
|
1359
1423
|
}
|
|
1360
1424
|
} catch (error) {
|
|
@@ -1370,16 +1434,19 @@ export class YotoPlayerAccessory {
|
|
|
1370
1434
|
* @returns {Promise<CharacteristicValue>}
|
|
1371
1435
|
*/
|
|
1372
1436
|
async getAmbientLightHue () {
|
|
1437
|
+
if (!this.deviceConfig) {
|
|
1438
|
+
return 0 // Default hue
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1373
1441
|
try {
|
|
1374
|
-
const
|
|
1375
|
-
const hex = config.config.ambientColour || '#000000'
|
|
1442
|
+
const hex = this.deviceConfig.config.ambientColour || '000000'
|
|
1376
1443
|
const { h } = this.hexToHsv(hex)
|
|
1377
1444
|
if (isNaN(h)) {
|
|
1378
1445
|
return 0
|
|
1379
1446
|
}
|
|
1380
1447
|
return h
|
|
1381
1448
|
} catch (error) {
|
|
1382
|
-
this.log.
|
|
1449
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient light hue:`, error)
|
|
1383
1450
|
return 0
|
|
1384
1451
|
}
|
|
1385
1452
|
}
|
|
@@ -1400,16 +1467,19 @@ export class YotoPlayerAccessory {
|
|
|
1400
1467
|
* @returns {Promise<CharacteristicValue>}
|
|
1401
1468
|
*/
|
|
1402
1469
|
async getAmbientLightSaturation () {
|
|
1470
|
+
if (!this.deviceConfig) {
|
|
1471
|
+
return 0 // Default saturation
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1403
1474
|
try {
|
|
1404
|
-
const
|
|
1405
|
-
const hex = config.config.ambientColour || '#000000'
|
|
1475
|
+
const hex = this.deviceConfig.config.ambientColour || '000000'
|
|
1406
1476
|
const { s } = this.hexToHsv(hex)
|
|
1407
1477
|
if (isNaN(s)) {
|
|
1408
1478
|
return 0
|
|
1409
1479
|
}
|
|
1410
1480
|
return s
|
|
1411
1481
|
} catch (error) {
|
|
1412
|
-
this.log.
|
|
1482
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient light saturation:`, error)
|
|
1413
1483
|
return 0
|
|
1414
1484
|
}
|
|
1415
1485
|
}
|
|
@@ -1430,18 +1500,21 @@ export class YotoPlayerAccessory {
|
|
|
1430
1500
|
* @returns {Promise<CharacteristicValue>}
|
|
1431
1501
|
*/
|
|
1432
1502
|
async getAmbientLightBrightness () {
|
|
1503
|
+
if (!this.deviceConfig) {
|
|
1504
|
+
return 0 // Default brightness
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1433
1507
|
try {
|
|
1434
|
-
const
|
|
1435
|
-
const hex = config.config.ambientColour
|
|
1508
|
+
const hex = this.deviceConfig.config.ambientColour || '000000'
|
|
1436
1509
|
const { v } = this.hexToHsv(hex)
|
|
1437
1510
|
const brightness = Math.round(v)
|
|
1438
1511
|
if (isNaN(brightness)) {
|
|
1439
|
-
return
|
|
1512
|
+
return 0
|
|
1440
1513
|
}
|
|
1441
1514
|
return brightness
|
|
1442
1515
|
} catch (error) {
|
|
1443
|
-
this.log.
|
|
1444
|
-
return
|
|
1516
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to parse ambient brightness:`, error)
|
|
1517
|
+
return 0
|
|
1445
1518
|
}
|
|
1446
1519
|
}
|
|
1447
1520
|
|
|
@@ -1461,9 +1534,12 @@ export class YotoPlayerAccessory {
|
|
|
1461
1534
|
*/
|
|
1462
1535
|
async updateAmbientLightColor () {
|
|
1463
1536
|
try {
|
|
1464
|
-
//
|
|
1465
|
-
|
|
1466
|
-
|
|
1537
|
+
// Refresh config if not cached
|
|
1538
|
+
if (!this.deviceConfig) {
|
|
1539
|
+
this.deviceConfig = await this.platform.yotoApi.getDeviceConfig(this.device.deviceId)
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
const currentHex = this.deviceConfig.config.ambientColour || '000000'
|
|
1467
1543
|
const currentHsv = this.hexToHsv(currentHex)
|
|
1468
1544
|
|
|
1469
1545
|
// Use pending values or current values
|
package/lib/yotoMqtt.js
CHANGED
|
@@ -60,7 +60,7 @@ export class YotoMqtt extends EventEmitter {
|
|
|
60
60
|
*/
|
|
61
61
|
async connect (accessToken, deviceId) {
|
|
62
62
|
if (this.client) {
|
|
63
|
-
this.log.
|
|
63
|
+
this.log.debug(LOG_PREFIX.MQTT, 'Already connected, disconnecting first...')
|
|
64
64
|
await this.disconnect()
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -70,21 +70,7 @@ export class YotoMqtt extends EventEmitter {
|
|
|
70
70
|
const clientId = `DASH${deviceId}`
|
|
71
71
|
const username = `${deviceId}?x-amz-customauthorizer-name=${YOTO_MQTT_AUTH_NAME}`
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
this.log.warn(LOG_PREFIX.MQTT, '='.repeat(60))
|
|
75
|
-
this.log.warn(LOG_PREFIX.MQTT, 'MQTT CONNECTION DETAILS')
|
|
76
|
-
this.log.warn(LOG_PREFIX.MQTT, '='.repeat(60))
|
|
77
|
-
this.log.warn(LOG_PREFIX.MQTT, `Broker URL: ${this.brokerUrl}`)
|
|
78
|
-
this.log.warn(LOG_PREFIX.MQTT, `Client ID: ${clientId}`)
|
|
79
|
-
this.log.warn(LOG_PREFIX.MQTT, `Username: ${username}`)
|
|
80
|
-
this.log.warn(LOG_PREFIX.MQTT, `Device ID: ${deviceId}`)
|
|
81
|
-
this.log.warn(LOG_PREFIX.MQTT, `Token: ${accessToken}`)
|
|
82
|
-
this.log.warn(LOG_PREFIX.MQTT, `Token length: ${accessToken.length}`)
|
|
83
|
-
this.log.warn(LOG_PREFIX.MQTT, 'Port: 443')
|
|
84
|
-
this.log.warn(LOG_PREFIX.MQTT, 'Protocol: wss')
|
|
85
|
-
this.log.warn(LOG_PREFIX.MQTT, 'Keepalive: 300')
|
|
86
|
-
this.log.warn(LOG_PREFIX.MQTT, `Connect Timeout: ${MQTT_CONNECT_TIMEOUT}ms`)
|
|
87
|
-
this.log.warn(LOG_PREFIX.MQTT, '='.repeat(60))
|
|
73
|
+
this.log.debug(LOG_PREFIX.MQTT, `Connecting with client ID: ${clientId}`)
|
|
88
74
|
|
|
89
75
|
this.client = mqtt.connect(this.brokerUrl, {
|
|
90
76
|
keepalive: 300,
|
|
@@ -135,7 +121,7 @@ export class YotoMqtt extends EventEmitter {
|
|
|
135
121
|
this.emit('disconnected')
|
|
136
122
|
|
|
137
123
|
if (wasConnected) {
|
|
138
|
-
this.log.
|
|
124
|
+
this.log.info(LOG_PREFIX.MQTT, ERROR_MESSAGES.MQTT_DISCONNECTED)
|
|
139
125
|
this.handleReconnect()
|
|
140
126
|
} else {
|
|
141
127
|
this.log.error(LOG_PREFIX.MQTT, 'Connection closed before establishing connection')
|
|
@@ -156,18 +142,18 @@ export class YotoMqtt extends EventEmitter {
|
|
|
156
142
|
|
|
157
143
|
this.client.on('offline', () => {
|
|
158
144
|
this.connected = false
|
|
159
|
-
this.log.
|
|
145
|
+
this.log.debug(LOG_PREFIX.MQTT, 'MQTT client offline - connection failed or lost')
|
|
160
146
|
|
|
161
147
|
// Emit offline event
|
|
162
148
|
this.emit('offline')
|
|
163
149
|
})
|
|
164
150
|
|
|
165
151
|
this.client.on('end', () => {
|
|
166
|
-
this.log.
|
|
152
|
+
this.log.debug(LOG_PREFIX.MQTT, 'MQTT client ended')
|
|
167
153
|
})
|
|
168
154
|
|
|
169
155
|
this.client.on('disconnect', (packet) => {
|
|
170
|
-
this.log.
|
|
156
|
+
this.log.debug(LOG_PREFIX.MQTT, 'MQTT client disconnected:', packet)
|
|
171
157
|
})
|
|
172
158
|
|
|
173
159
|
this.client.on('packetreceive', (packet) => {
|
|
@@ -309,10 +295,10 @@ export class YotoMqtt extends EventEmitter {
|
|
|
309
295
|
// Request initial status after a short delay
|
|
310
296
|
setTimeout(() => {
|
|
311
297
|
this.requestStatus(deviceId).catch(err => {
|
|
312
|
-
this.log.
|
|
298
|
+
this.log.debug(LOG_PREFIX.MQTT, `Failed to request initial status for ${deviceId}:`, err)
|
|
313
299
|
})
|
|
314
300
|
this.requestEvents(deviceId).catch(err => {
|
|
315
|
-
this.log.
|
|
301
|
+
this.log.debug(LOG_PREFIX.MQTT, `Failed to request initial events for ${deviceId}:`, err)
|
|
316
302
|
})
|
|
317
303
|
}, INITIAL_STATUS_REQUEST_DELAY)
|
|
318
304
|
|
|
@@ -371,7 +357,7 @@ export class YotoMqtt extends EventEmitter {
|
|
|
371
357
|
const deviceId = this.extractDeviceId(topic)
|
|
372
358
|
|
|
373
359
|
if (!deviceId) {
|
|
374
|
-
this.log.
|
|
360
|
+
this.log.debug(LOG_PREFIX.MQTT, `Could not extract device ID from topic: ${topic}`)
|
|
375
361
|
return
|
|
376
362
|
}
|
|
377
363
|
|
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.21",
|
|
5
5
|
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/bcomnes/homebridge-yoto/issues"
|