homebridge-yoto 0.0.16 → 0.0.17
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 +41 -29
- package/lib/types.js +53 -33
- package/package.json +1 -1
package/lib/playerAccessory.js
CHANGED
|
@@ -511,13 +511,17 @@ export class YotoPlayerAccessory {
|
|
|
511
511
|
|
|
512
512
|
/**
|
|
513
513
|
* Handle status update from MQTT
|
|
514
|
-
* @param {YotoDeviceStatus
|
|
514
|
+
* @param {YotoDeviceStatus | {status: YotoDeviceStatus}} statusMessage - Status data or wrapped status
|
|
515
515
|
*/
|
|
516
|
-
handleStatusUpdate (
|
|
517
|
-
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}]
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
516
|
+
handleStatusUpdate (statusMessage) {
|
|
517
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Raw MQTT status message:`, JSON.stringify(statusMessage, null, 2))
|
|
518
|
+
|
|
519
|
+
// Unwrap status if it's nested under a 'status' property
|
|
520
|
+
const status = /** @type {YotoDeviceStatus} */ ('status' in statusMessage ? statusMessage.status : statusMessage)
|
|
521
|
+
|
|
522
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - batteryLevel:`, status.batteryLevel)
|
|
523
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - userVolume:`, status.userVolume)
|
|
524
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped status - volume:`, status.volume)
|
|
521
525
|
|
|
522
526
|
this.currentStatus = status
|
|
523
527
|
this.lastUpdateTime = Date.now()
|
|
@@ -529,10 +533,15 @@ export class YotoPlayerAccessory {
|
|
|
529
533
|
|
|
530
534
|
/**
|
|
531
535
|
* Handle playback events update from MQTT
|
|
532
|
-
* @param {YotoPlaybackEvents
|
|
536
|
+
* @param {YotoPlaybackEvents | {events: YotoPlaybackEvents}} eventsMessage - Playback events or wrapped events
|
|
533
537
|
*/
|
|
534
|
-
handleEventsUpdate (
|
|
535
|
-
this.log.
|
|
538
|
+
handleEventsUpdate (eventsMessage) {
|
|
539
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Raw MQTT events message:`, JSON.stringify(eventsMessage, null, 2))
|
|
540
|
+
|
|
541
|
+
// Unwrap events if it's nested under an 'events' property
|
|
542
|
+
const events = /** @type {YotoPlaybackEvents} */ ('events' in eventsMessage ? eventsMessage.events : eventsMessage)
|
|
543
|
+
|
|
544
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Unwrapped events - cardId:`, events.cardId)
|
|
536
545
|
this.currentEvents = events
|
|
537
546
|
this.lastUpdateTime = Date.now()
|
|
538
547
|
this.accessory.context.lastEvents = events
|
|
@@ -551,7 +560,7 @@ export class YotoPlayerAccessory {
|
|
|
551
560
|
* @param {import('./types.js').MqttCommandResponse} response - Command response
|
|
552
561
|
*/
|
|
553
562
|
handleCommandResponse (response) {
|
|
554
|
-
this.log.
|
|
563
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Command response:`, response)
|
|
555
564
|
}
|
|
556
565
|
|
|
557
566
|
/**
|
|
@@ -562,16 +571,17 @@ export class YotoPlayerAccessory {
|
|
|
562
571
|
return
|
|
563
572
|
}
|
|
564
573
|
|
|
565
|
-
// Update volume
|
|
566
|
-
if (this.speakerService) {
|
|
574
|
+
// Update volume from events, not status
|
|
575
|
+
if (this.speakerService && this.currentEvents?.volume !== undefined) {
|
|
576
|
+
const volume = Number(this.currentEvents.volume) || 0
|
|
567
577
|
this.speakerService.updateCharacteristic(
|
|
568
578
|
this.platform.Characteristic.Volume,
|
|
569
|
-
|
|
579
|
+
volume
|
|
570
580
|
)
|
|
571
581
|
}
|
|
572
582
|
|
|
573
583
|
// Update battery
|
|
574
|
-
if (this.batteryService) {
|
|
584
|
+
if (this.batteryService && this.currentStatus.batteryLevel !== undefined) {
|
|
575
585
|
this.batteryService.updateCharacteristic(
|
|
576
586
|
this.platform.Characteristic.BatteryLevel,
|
|
577
587
|
this.currentStatus.batteryLevel
|
|
@@ -603,7 +613,7 @@ export class YotoPlayerAccessory {
|
|
|
603
613
|
}
|
|
604
614
|
|
|
605
615
|
// Update display brightness
|
|
606
|
-
if (this.displayService) {
|
|
616
|
+
if (this.displayService && this.currentStatus.dnowBrightness !== undefined) {
|
|
607
617
|
const isOn = this.currentStatus.dnowBrightness > 0
|
|
608
618
|
this.displayService.updateCharacteristic(
|
|
609
619
|
this.platform.Characteristic.On,
|
|
@@ -619,7 +629,7 @@ export class YotoPlayerAccessory {
|
|
|
619
629
|
}
|
|
620
630
|
|
|
621
631
|
// Update advanced control switches
|
|
622
|
-
if (this.bluetoothSwitch) {
|
|
632
|
+
if (this.bluetoothSwitch && this.currentStatus.bluetoothHp !== undefined) {
|
|
623
633
|
const bluetoothEnabled = this.currentStatus.bluetoothHp
|
|
624
634
|
this.bluetoothSwitch.updateCharacteristic(
|
|
625
635
|
this.platform.Characteristic.On,
|
|
@@ -627,7 +637,7 @@ export class YotoPlayerAccessory {
|
|
|
627
637
|
)
|
|
628
638
|
}
|
|
629
639
|
|
|
630
|
-
if (this.btHeadphonesSwitch) {
|
|
640
|
+
if (this.btHeadphonesSwitch && this.currentStatus.bluetoothHp !== undefined) {
|
|
631
641
|
const btHeadphonesEnabled = this.currentStatus.bluetoothHp
|
|
632
642
|
this.btHeadphonesSwitch.updateCharacteristic(
|
|
633
643
|
this.platform.Characteristic.On,
|
|
@@ -789,15 +799,16 @@ export class YotoPlayerAccessory {
|
|
|
789
799
|
* @returns {Promise<CharacteristicValue>}
|
|
790
800
|
*/
|
|
791
801
|
async getVolume () {
|
|
792
|
-
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume -
|
|
793
|
-
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume -
|
|
802
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - currentEvents:`, this.currentEvents)
|
|
803
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - events.volume:`, this.currentEvents?.volume)
|
|
794
804
|
|
|
795
|
-
|
|
796
|
-
|
|
805
|
+
// Volume comes from events, not status
|
|
806
|
+
if (!this.currentEvents || this.currentEvents.volume === undefined) {
|
|
807
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - no volume in events, returning default: 50`)
|
|
797
808
|
return 50
|
|
798
809
|
}
|
|
799
|
-
const volume = Number(this.
|
|
800
|
-
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - returning:`, volume)
|
|
810
|
+
const volume = Number(this.currentEvents.volume) || 50
|
|
811
|
+
this.log.info(LOG_PREFIX.ACCESSORY, `[${this.device.name}] getVolume - returning volume:`, volume)
|
|
801
812
|
return volume
|
|
802
813
|
}
|
|
803
814
|
|
|
@@ -823,10 +834,11 @@ export class YotoPlayerAccessory {
|
|
|
823
834
|
* @returns {Promise<CharacteristicValue>}
|
|
824
835
|
*/
|
|
825
836
|
async getMute () {
|
|
826
|
-
|
|
837
|
+
// Volume comes from events, not status
|
|
838
|
+
if (!this.currentEvents || this.currentEvents.volume === undefined) {
|
|
827
839
|
return false
|
|
828
840
|
}
|
|
829
|
-
return Number(this.
|
|
841
|
+
return Number(this.currentEvents.volume) === 0
|
|
830
842
|
}
|
|
831
843
|
|
|
832
844
|
/**
|
|
@@ -842,7 +854,7 @@ export class YotoPlayerAccessory {
|
|
|
842
854
|
await this.mqtt.setVolume(this.device.deviceId, 0)
|
|
843
855
|
} else {
|
|
844
856
|
// Unmute - restore to a reasonable volume if currently 0
|
|
845
|
-
const currentVolume = this.
|
|
857
|
+
const currentVolume = this.currentEvents?.volume ? Number(this.currentEvents.volume) : 0
|
|
846
858
|
const targetVolume = currentVolume === 0 ? 50 : currentVolume
|
|
847
859
|
await this.mqtt.setVolume(this.device.deviceId, targetVolume)
|
|
848
860
|
}
|
|
@@ -951,7 +963,7 @@ export class YotoPlayerAccessory {
|
|
|
951
963
|
* @returns {Promise<CharacteristicValue>}
|
|
952
964
|
*/
|
|
953
965
|
async getDisplayOn () {
|
|
954
|
-
if (!this.currentStatus) {
|
|
966
|
+
if (!this.currentStatus || this.currentStatus.dnowBrightness === undefined) {
|
|
955
967
|
return true
|
|
956
968
|
}
|
|
957
969
|
return this.currentStatus.dnowBrightness > 0
|
|
@@ -1035,7 +1047,7 @@ export class YotoPlayerAccessory {
|
|
|
1035
1047
|
* @returns {Promise<CharacteristicValue>}
|
|
1036
1048
|
*/
|
|
1037
1049
|
async getBluetoothEnabled () {
|
|
1038
|
-
if (!this.currentStatus) {
|
|
1050
|
+
if (!this.currentStatus || this.currentStatus.bluetoothHp === undefined) {
|
|
1039
1051
|
return false
|
|
1040
1052
|
}
|
|
1041
1053
|
return this.currentStatus.bluetoothHp
|
|
@@ -1095,7 +1107,7 @@ export class YotoPlayerAccessory {
|
|
|
1095
1107
|
* @returns {Promise<CharacteristicValue>}
|
|
1096
1108
|
*/
|
|
1097
1109
|
async getBtHeadphonesEnabled () {
|
|
1098
|
-
if (!this.currentStatus) {
|
|
1110
|
+
if (!this.currentStatus || this.currentStatus.bluetoothHp === undefined) {
|
|
1099
1111
|
return false
|
|
1100
1112
|
}
|
|
1101
1113
|
return this.currentStatus.bluetoothHp
|
package/lib/types.js
CHANGED
|
@@ -15,43 +15,63 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
+
* YotoDeviceStatus - ACTUAL structure from MQTT /device/{id}/data/status
|
|
19
|
+
* Note: This differs from the documented API schema
|
|
18
20
|
* @typedef {Object} YotoDeviceStatus
|
|
19
|
-
* @property {number}
|
|
20
|
-
* @property {string}
|
|
21
|
-
* @property {
|
|
22
|
-
* @property {number}
|
|
23
|
-
* @property {
|
|
24
|
-
* @property {number}
|
|
25
|
-
* @property {number}
|
|
26
|
-
* @property {number}
|
|
27
|
-
* @property {number}
|
|
28
|
-
* @property {
|
|
29
|
-
* @property {number}
|
|
30
|
-
* @property {number}
|
|
31
|
-
* @property {
|
|
32
|
-
* @property {number}
|
|
33
|
-
* @property {number}
|
|
34
|
-
* @property {
|
|
35
|
-
* @property {
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @property {
|
|
40
|
-
* @property {string}
|
|
41
|
-
* @property {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
21
|
+
* @property {number} battery - Raw battery voltage (e.g., 3693)
|
|
22
|
+
* @property {string} [powerCaps] - Power capability flags (e.g., '0x02')
|
|
23
|
+
* @property {number} batteryLevel - Battery level percentage (0-100)
|
|
24
|
+
* @property {number} batteryTemp - Battery temperature
|
|
25
|
+
* @property {string} batteryData - Battery data string (e.g., '0:0:0')
|
|
26
|
+
* @property {number} batteryLevelRaw - Raw battery level value
|
|
27
|
+
* @property {number} free - Free memory
|
|
28
|
+
* @property {number} freeDMA - Free DMA memory
|
|
29
|
+
* @property {number} free32 - Free 32-bit memory
|
|
30
|
+
* @property {number} upTime - Device uptime in seconds
|
|
31
|
+
* @property {number} utcTime - Current UTC time (Unix timestamp)
|
|
32
|
+
* @property {number} aliveTime - Time device has been alive
|
|
33
|
+
* @property {number} accelTemp - Accelerometer temperature in Celsius
|
|
34
|
+
* @property {number} [qiOtp] - Qi charging related field
|
|
35
|
+
* @property {number} [errorsLogged] - Number of errors logged
|
|
36
|
+
* @property {string} nightlightMode - Nightlight mode (e.g., 'off')
|
|
37
|
+
* @property {string} temp - Temperature data string (e.g., '1014:23:318')
|
|
38
|
+
*
|
|
39
|
+
* Note: Volume information comes from events, not status!
|
|
40
|
+
* The following documented fields may exist but haven't been observed in v3 players:
|
|
41
|
+
* @property {number} [statusVersion] - Status data version
|
|
42
|
+
* @property {string} [fwVersion] - Firmware version
|
|
43
|
+
* @property {string} [productType] - Product type identifier
|
|
44
|
+
* @property {number} [als] - Ambient light sensor reading
|
|
45
|
+
* @property {number} [freeDisk] - Free disk space in bytes
|
|
46
|
+
* @property {number} [shutdownTimeout] - Auto-shutdown timeout in seconds
|
|
47
|
+
* @property {number} [dbatTimeout] - Display battery timeout
|
|
48
|
+
* @property {number} [charging] - Charging state (0=not charging, 1=charging)
|
|
49
|
+
* @property {string | null} [activeCard] - Currently active card ID or null
|
|
50
|
+
* @property {number} [cardInserted] - Card insertion state (0=none, 1=physical, 2=remote)
|
|
51
|
+
* @property {number} [playingStatus] - Playing status code
|
|
52
|
+
* @property {boolean} [headphones] - Whether headphones are connected
|
|
53
|
+
* @property {number} [dnowBrightness] - Current display brightness
|
|
54
|
+
* @property {number} [dayBright] - Day mode brightness setting
|
|
55
|
+
* @property {number} [nightBright] - Night mode brightness setting
|
|
56
|
+
* @property {boolean} [bluetoothHp] - Bluetooth headphones enabled
|
|
57
|
+
* @property {number} [volume] - System volume level (may be in events instead)
|
|
58
|
+
* @property {number} [userVolume] - User volume level 0-100 (may be in events instead)
|
|
59
|
+
* @property {'12' | '24'} [timeFormat] - Time format preference
|
|
60
|
+
* @property {number} [day] - Day mode (0=night, 1=day, -1=unknown)
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* YotoPlaybackEvents - From MQTT /device/{id}/data/events
|
|
45
65
|
* @typedef {Object} YotoPlaybackEvents
|
|
46
66
|
* @property {string} repeatAll - Repeat all setting ("true" or "false")
|
|
47
67
|
* @property {string} streaming - Streaming active ("true" or "false")
|
|
48
|
-
* @property {string} volume - Current volume level
|
|
49
|
-
* @property {string} volumeMax - Maximum volume level
|
|
68
|
+
* @property {string} volume - Current volume level (0-100 as string)
|
|
69
|
+
* @property {string} volumeMax - Maximum volume level (0-100 as string)
|
|
50
70
|
* @property {string} playbackWait - Playback wait state ("true" or "false")
|
|
51
71
|
* @property {string} sleepTimerActive - Sleep timer active ("true" or "false")
|
|
52
|
-
* @property {string} eventUtc - Event timestamp (Unix timestamp)
|
|
53
|
-
* @property {string} trackLength - Track duration in seconds
|
|
54
|
-
* @property {string} position - Current playback position in seconds
|
|
72
|
+
* @property {string} eventUtc - Event timestamp (Unix timestamp as string)
|
|
73
|
+
* @property {string} trackLength - Track duration in seconds (as string)
|
|
74
|
+
* @property {string} position - Current playback position in seconds (as string)
|
|
55
75
|
* @property {string} cardId - Active card ID
|
|
56
76
|
* @property {string} source - Playback source (e.g., "card", "remote", "MQTT")
|
|
57
77
|
* @property {string} cardUpdatedAt - Card last updated timestamp (ISO8601)
|
|
@@ -59,8 +79,8 @@
|
|
|
59
79
|
* @property {string} chapterKey - Current chapter key
|
|
60
80
|
* @property {string} trackTitle - Current track title
|
|
61
81
|
* @property {string} trackKey - Current track key
|
|
62
|
-
* @property {string} playbackStatus - Playback status ("playing", "paused", "stopped")
|
|
63
|
-
* @property {string} sleepTimerSeconds -
|
|
82
|
+
* @property {string} playbackStatus - Playback status (e.g., "playing", "paused", "stopped")
|
|
83
|
+
* @property {string} sleepTimerSeconds - Remaining sleep timer seconds (as string)
|
|
64
84
|
*/
|
|
65
85
|
|
|
66
86
|
/**
|
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.17",
|
|
5
5
|
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/bcomnes/homebridge-yoto/issues"
|