homebridge-yoto 0.0.36 → 0.0.37

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/accessory.js CHANGED
@@ -129,15 +129,15 @@ export class YotoPlayerAccessory {
129
129
  : {}
130
130
 
131
131
  return {
132
- playback: getBooleanSetting(serviceConfig.playback, getServiceDefault('playback')),
133
- volume: getBooleanSetting(serviceConfig.volume, getServiceDefault('volume')),
134
- temperature: getBooleanSetting(serviceConfig.temperature, getServiceDefault('temperature')),
135
- nightlight: getBooleanSetting(serviceConfig.nightlight, getServiceDefault('nightlight')),
136
- cardSlot: getBooleanSetting(serviceConfig.cardSlot, getServiceDefault('cardSlot')),
137
- nightMode: getBooleanSetting(serviceConfig.nightMode, getServiceDefault('nightMode')),
138
- sleepTimer: getBooleanSetting(serviceConfig.sleepTimer, getServiceDefault('sleepTimer')),
139
- bluetooth: getBooleanSetting(serviceConfig.bluetooth, getServiceDefault('bluetooth')),
140
- volumeLimits: getBooleanSetting(serviceConfig.volumeLimits, getServiceDefault('volumeLimits')),
132
+ playback: getBooleanSetting(serviceConfig['playback'], getServiceDefault('playback')),
133
+ volume: getBooleanSetting(serviceConfig['volume'], getServiceDefault('volume')),
134
+ temperature: getBooleanSetting(serviceConfig['temperature'], getServiceDefault('temperature')),
135
+ nightlight: getBooleanSetting(serviceConfig['nightlight'], getServiceDefault('nightlight')),
136
+ cardSlot: getBooleanSetting(serviceConfig['cardSlot'], getServiceDefault('cardSlot')),
137
+ nightMode: getBooleanSetting(serviceConfig['nightMode'], getServiceDefault('nightMode')),
138
+ sleepTimer: getBooleanSetting(serviceConfig['sleepTimer'], getServiceDefault('sleepTimer')),
139
+ bluetooth: getBooleanSetting(serviceConfig['bluetooth'], getServiceDefault('bluetooth')),
140
+ volumeLimits: getBooleanSetting(serviceConfig['volumeLimits'], getServiceDefault('volumeLimits')),
141
141
  }
142
142
  }
143
143
 
@@ -663,10 +663,6 @@ export class YotoPlayerAccessory {
663
663
  this.updateFirmwareVersionCharacteristic(status.firmwareVersion)
664
664
  break
665
665
 
666
- case 'maxVolume':
667
- this.updateVolumeLimitProps(status.maxVolume)
668
- break
669
-
670
666
  case 'temperatureCelsius':
671
667
  if (this.#deviceModel.capabilities.hasTemperatureSensor && status.temperatureCelsius !== null) {
672
668
  this.updateTemperatureCharacteristic(status.temperatureCelsius)
@@ -687,8 +683,6 @@ export class YotoPlayerAccessory {
687
683
  if (this.#deviceModel.capabilities.hasColoredNightlight) {
688
684
  this.updateNightlightStatusCharacteristics()
689
685
  }
690
- // Day/night mode affects which volume limit is active
691
- this.updateVolumeLimitProps(status.maxVolume)
692
686
  break
693
687
 
694
688
  case 'cardInsertionState':
@@ -697,6 +691,7 @@ export class YotoPlayerAccessory {
697
691
 
698
692
  // Available but not yet mapped to characteristics
699
693
  case 'activeCardId':
694
+ case 'maxVolume':
700
695
  case 'powerSource':
701
696
  case 'wifiStrength':
702
697
  case 'freeDiskSpaceBytes':
@@ -873,12 +868,12 @@ export class YotoPlayerAccessory {
873
868
  })
874
869
 
875
870
  // Lifecycle events
876
- this.#deviceModel.on('online', ({ reason }) => {
871
+ this.#deviceModel.on('online', ({ reason: _reason }) => {
877
872
  // Platform logs online/offline events to avoid duplicate output.
878
873
  this.updateOnlineStatusCharacteristic(true)
879
874
  })
880
875
 
881
- this.#deviceModel.on('offline', ({ reason }) => {
876
+ this.#deviceModel.on('offline', ({ reason: _reason }) => {
882
877
  // Platform logs online/offline events to avoid duplicate output.
883
878
  this.updateOnlineStatusCharacteristic(false)
884
879
  })
@@ -926,6 +921,7 @@ export class YotoPlayerAccessory {
926
921
  * @returns {Promise<CharacteristicValue>}
927
922
  */
928
923
  async getVolume () {
924
+ this.#log.debug(LOG_PREFIX.ACCESSORY, `[${this.#device.name}] Get volume`)
929
925
  return this.#deviceModel.status.volume
930
926
  }
931
927
 
@@ -949,6 +945,10 @@ export class YotoPlayerAccessory {
949
945
  ? deviceModel.status.maxVolume
950
946
  : 16
951
947
  const steps = Math.max(0, Math.min(Math.round(requestedSteps), maxVolumeSteps))
948
+ this.#log.debug(
949
+ LOG_PREFIX.ACCESSORY,
950
+ `[${this.#device.name}] Set volume raw=${value} steps=${requestedSteps} -> ${steps} (max ${maxVolumeSteps}/16)`
951
+ )
952
952
 
953
953
  // Track last non-zero volume for unmute
954
954
  if (steps > 0) {
@@ -1560,6 +1560,10 @@ export class YotoPlayerAccessory {
1560
1560
  */
1561
1561
  async getDayMaxVolume () {
1562
1562
  const limit = this.#deviceModel.config.maxVolumeLimit
1563
+ this.#log.debug(
1564
+ LOG_PREFIX.ACCESSORY,
1565
+ `[${this.#device.name}] Get day max volume limit: ${limit}`
1566
+ )
1563
1567
  return Number.isFinite(limit) ? limit : 16
1564
1568
  }
1565
1569
 
@@ -1576,7 +1580,10 @@ export class YotoPlayerAccessory {
1576
1580
  }
1577
1581
 
1578
1582
  const limit = Math.max(0, Math.min(Math.round(requested), 16))
1579
- this.#log.debug(LOG_PREFIX.ACCESSORY, `Setting day max volume limit: ${limit}/16`)
1583
+ this.#log.debug(
1584
+ LOG_PREFIX.ACCESSORY,
1585
+ `[${this.#device.name}] Set day max volume limit raw=${value} requested=${requested} -> ${limit}/16`
1586
+ )
1580
1587
  await this.#deviceModel.updateConfig({ maxVolumeLimit: limit })
1581
1588
  }
1582
1589
 
@@ -1586,6 +1593,10 @@ export class YotoPlayerAccessory {
1586
1593
  */
1587
1594
  async getNightMaxVolume () {
1588
1595
  const limit = this.#deviceModel.config.nightMaxVolumeLimit
1596
+ this.#log.debug(
1597
+ LOG_PREFIX.ACCESSORY,
1598
+ `[${this.#device.name}] Get night max volume limit: ${limit}`
1599
+ )
1589
1600
  return Number.isFinite(limit) ? limit : 10
1590
1601
  }
1591
1602
 
@@ -1602,7 +1613,10 @@ export class YotoPlayerAccessory {
1602
1613
  }
1603
1614
 
1604
1615
  const limit = Math.max(0, Math.min(Math.round(requested), 16))
1605
- this.#log.debug(LOG_PREFIX.ACCESSORY, `Setting night max volume limit: ${limit}/16`)
1616
+ this.#log.debug(
1617
+ LOG_PREFIX.ACCESSORY,
1618
+ `[${this.#device.name}] Set night max volume limit raw=${value} requested=${requested} -> ${limit}/16`
1619
+ )
1606
1620
  await this.#deviceModel.updateConfig({ nightMaxVolumeLimit: limit })
1607
1621
  }
1608
1622
 
@@ -1634,30 +1648,15 @@ export class YotoPlayerAccessory {
1634
1648
  this.#lastNonZeroVolume = volumeSteps
1635
1649
  }
1636
1650
 
1651
+ const normalizedVolume = Number.isFinite(volumeSteps) ? volumeSteps : 0
1652
+ const clampedVolume = Math.max(0, Math.min(normalizedVolume, 16))
1653
+ this.#log.debug(
1654
+ LOG_PREFIX.ACCESSORY,
1655
+ `[${this.#device.name}] Update volume characteristic raw=${volumeSteps} -> ${clampedVolume}`
1656
+ )
1637
1657
  this.volumeService
1638
1658
  .getCharacteristic(Characteristic.Brightness)
1639
- .updateValue(volumeSteps)
1640
- }
1641
-
1642
- /**
1643
- * Update volume limit props - adjusts max value based on day/night mode
1644
- * @param {number} maxVolume - Maximum volume limit (0-16)
1645
- */
1646
- updateVolumeLimitProps (maxVolume) {
1647
- if (!this.volumeService) return
1648
-
1649
- const { Characteristic } = this.#platform
1650
- const maxVolumeSteps = Number.isFinite(maxVolume) ? maxVolume : 16
1651
- const clampedMaxVolume = Math.max(0, Math.min(maxVolumeSteps, 16))
1652
- this.volumeService
1653
- .getCharacteristic(Characteristic.Brightness)
1654
- .setProps({
1655
- minValue: 0,
1656
- maxValue: clampedMaxVolume,
1657
- minStep: 1,
1658
- })
1659
-
1660
- this.#log.debug(`[${this.#device.name}] Volume max is ${clampedMaxVolume}/16`)
1659
+ .updateValue(clampedVolume)
1661
1660
  }
1662
1661
 
1663
1662
  /**
@@ -1920,16 +1919,26 @@ export class YotoPlayerAccessory {
1920
1919
 
1921
1920
  if (this.dayMaxVolumeService) {
1922
1921
  const limit = Number.isFinite(config.maxVolumeLimit) ? config.maxVolumeLimit : 16
1922
+ const clampedLimit = Math.max(0, Math.min(limit, 16))
1923
+ this.#log.debug(
1924
+ LOG_PREFIX.ACCESSORY,
1925
+ `[${this.#device.name}] Update day max volume characteristic raw=${limit} -> ${clampedLimit}`
1926
+ )
1923
1927
  this.dayMaxVolumeService
1924
1928
  .getCharacteristic(Characteristic.Brightness)
1925
- .updateValue(limit)
1929
+ .updateValue(clampedLimit)
1926
1930
  }
1927
1931
 
1928
1932
  if (this.nightMaxVolumeService) {
1929
1933
  const limit = Number.isFinite(config.nightMaxVolumeLimit) ? config.nightMaxVolumeLimit : 10
1934
+ const clampedLimit = Math.max(0, Math.min(limit, 16))
1935
+ this.#log.debug(
1936
+ LOG_PREFIX.ACCESSORY,
1937
+ `[${this.#device.name}] Update night max volume characteristic raw=${limit} -> ${clampedLimit}`
1938
+ )
1930
1939
  this.nightMaxVolumeService
1931
1940
  .getCharacteristic(Characteristic.Brightness)
1932
- .updateValue(limit)
1941
+ .updateValue(clampedLimit)
1933
1942
  }
1934
1943
  }
1935
1944
 
package/lib/platform.js CHANGED
@@ -70,7 +70,7 @@ export class YotoPlatform {
70
70
  return
71
71
  }
72
72
 
73
- log.info('Authentication tokens found, initializing Yoto account...')
73
+ log.debug('Authentication tokens found, initializing Yoto account...')
74
74
 
75
75
  const { updateHomebridgeConfig, sessionId } = this
76
76
 
@@ -158,7 +158,7 @@ export class YotoPlatform {
158
158
  }
159
159
 
160
160
  try {
161
- this.log.info('Starting Yoto account...')
161
+ this.log.debug('Starting Yoto account...')
162
162
 
163
163
  // Listen for devices being added
164
164
  this.yotoAccount.on('deviceAdded', async ({ deviceId }) => {
@@ -22,13 +22,16 @@ export function syncServiceNames ({
22
22
 
23
23
  service.updateCharacteristic(Characteristic.Name, sanitizedName)
24
24
 
25
- // Set ConfiguredName on all services, not just ones that say they support it.
26
- // This is the only way to set the service name inside an accessory.
27
- // const hasConfiguredNameCharacteristic = service.characteristics.some(c => c.UUID === Characteristic.ConfiguredName.UUID)
28
- // const hasConfiguredNameOptional = service.optionalCharacteristics.some(c => c.UUID === Characteristic.ConfiguredName.UUID)
29
- // if (!hasConfiguredNameCharacteristic && !hasConfiguredNameOptional) {
30
- // service.addOptionalCharacteristic(Characteristic.ConfiguredName)
31
- // }
25
+ // Add ConfiguredName when missing so we avoid HAP warnings on update.
26
+ const configuredNameUuid = Characteristic.ConfiguredName.UUID
27
+ const hasConfiguredNameCharacteristic = service.characteristics
28
+ .some((characteristic) => characteristic.UUID === configuredNameUuid)
29
+ const hasConfiguredNameOptional = service.optionalCharacteristics
30
+ .some((characteristic) => characteristic.UUID === configuredNameUuid)
31
+
32
+ if (!hasConfiguredNameCharacteristic && !hasConfiguredNameOptional) {
33
+ service.addOptionalCharacteristic(Characteristic.ConfiguredName)
34
+ }
32
35
 
33
36
  service.updateCharacteristic(Characteristic.ConfiguredName, sanitizedName)
34
37
  }
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.36",
4
+ "version": "0.0.37",
5
5
  "author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/bcomnes/homebridge-yoto/issues"