homebridge-yoto 0.0.36 → 0.0.38

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
@@ -92,8 +92,8 @@ export class YotoPlayerAccessory {
92
92
  /** @type {Service | undefined} */ bluetoothService
93
93
  /** @type {Service | undefined} */ dayMaxVolumeService
94
94
  /** @type {Service | undefined} */ nightMaxVolumeService
95
- // Volume state for mute/unmute (0-16 steps)
96
- /** @type {number} */ #lastNonZeroVolume = 8
95
+ // Volume state for mute/unmute (0-100 percent)
96
+ /** @type {number} */ #lastNonZeroVolume = 50
97
97
  // Nightlight color state for restore-on-ON
98
98
  /** @type {string} */ #lastDayColor = '0xffffff'
99
99
  /** @type {string} */ #lastNightColor = '0xffffff'
@@ -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
 
@@ -325,7 +325,7 @@ export class YotoPlayerAccessory {
325
325
  .getCharacteristic(Characteristic.Brightness)
326
326
  .setProps({
327
327
  minValue: 0,
328
- maxValue: 16,
328
+ maxValue: 100,
329
329
  minStep: 1,
330
330
  })
331
331
  .onGet(this.getVolume.bind(this))
@@ -598,7 +598,7 @@ export class YotoPlayerAccessory {
598
598
 
599
599
  dayService
600
600
  .getCharacteristic(Characteristic.Brightness)
601
- .setProps({ minValue: 0, maxValue: 16, minStep: 1 })
601
+ .setProps({ minValue: 0, maxValue: 100, minStep: 1 })
602
602
  .onGet(this.getDayMaxVolume.bind(this))
603
603
  .onSet(this.setDayMaxVolume.bind(this))
604
604
 
@@ -622,7 +622,7 @@ export class YotoPlayerAccessory {
622
622
 
623
623
  nightService
624
624
  .getCharacteristic(Characteristic.Brightness)
625
- .setProps({ minValue: 0, maxValue: 16, minStep: 1 })
625
+ .setProps({ minValue: 0, maxValue: 100, minStep: 1 })
626
626
  .onGet(this.getNightMaxVolume.bind(this))
627
627
  .onSet(this.setNightMaxVolume.bind(this))
628
628
 
@@ -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
  })
@@ -922,15 +917,23 @@ export class YotoPlayerAccessory {
922
917
  }
923
918
 
924
919
  /**
925
- * Get volume level (0-16 steps) from live status
920
+ * Get volume level as percentage (mapped from 0-16 steps)
926
921
  * @returns {Promise<CharacteristicValue>}
927
922
  */
928
923
  async getVolume () {
929
- return this.#deviceModel.status.volume
924
+ const volumeSteps = this.#deviceModel.status.volume
925
+ const normalizedSteps = Number.isFinite(volumeSteps) ? volumeSteps : 0
926
+ const clampedSteps = Math.max(0, Math.min(normalizedSteps, 16))
927
+ const percent = Math.round((clampedSteps / 16) * 100)
928
+ this.#log.debug(
929
+ LOG_PREFIX.ACCESSORY,
930
+ `[${this.#device.name}] Get volume rawSteps=${volumeSteps} steps=${clampedSteps} percent=${percent}`
931
+ )
932
+ return percent
930
933
  }
931
934
 
932
935
  /**
933
- * Set volume level (0-16 steps)
936
+ * Set volume level as percentage (mapped to 0-16 steps)
934
937
  * @param {CharacteristicValue} value
935
938
  * @returns {Promise<void>}
936
939
  */
@@ -938,21 +941,28 @@ export class YotoPlayerAccessory {
938
941
  const deviceModel = this.#deviceModel
939
942
  this.#log.debug(LOG_PREFIX.ACCESSORY, `[${this.#device.name}] Set volume:`, value)
940
943
 
941
- const requestedSteps = typeof value === 'number' ? value : Number(value)
942
- if (!Number.isFinite(requestedSteps)) {
944
+ const requestedPercent = typeof value === 'number' ? value : Number(value)
945
+ if (!Number.isFinite(requestedPercent)) {
943
946
  throw new this.#platform.api.hap.HapStatusError(
944
947
  this.#platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST
945
948
  )
946
949
  }
947
950
 
951
+ const normalizedPercent = Math.max(0, Math.min(Math.round(requestedPercent), 100))
952
+ const requestedSteps = Math.round((normalizedPercent / 100) * 16)
948
953
  const maxVolumeSteps = Number.isFinite(deviceModel.status.maxVolume)
949
954
  ? deviceModel.status.maxVolume
950
955
  : 16
951
956
  const steps = Math.max(0, Math.min(Math.round(requestedSteps), maxVolumeSteps))
957
+ const resultPercent = Math.round((steps / 16) * 100)
958
+ this.#log.debug(
959
+ LOG_PREFIX.ACCESSORY,
960
+ `[${this.#device.name}] Set volume raw=${value} normalizedPercent=${normalizedPercent} requestedSteps=${requestedSteps} -> steps=${steps} percent=${resultPercent} (maxSteps=${maxVolumeSteps})`
961
+ )
952
962
 
953
963
  // Track last non-zero volume for unmute
954
964
  if (steps > 0) {
955
- this.#lastNonZeroVolume = steps
965
+ this.#lastNonZeroVolume = Math.round((steps / 16) * 100)
956
966
  }
957
967
 
958
968
  try {
@@ -964,10 +974,11 @@ export class YotoPlayerAccessory {
964
974
  .getCharacteristic(Characteristic.On)
965
975
  .updateValue(steps > 0)
966
976
 
967
- if (steps !== requestedSteps) {
977
+ if (steps !== requestedSteps || normalizedPercent !== requestedPercent) {
978
+ const clampedPercent = Math.round((steps / 16) * 100)
968
979
  this.volumeService
969
980
  .getCharacteristic(Characteristic.Brightness)
970
- .updateValue(steps)
981
+ .updateValue(clampedPercent)
971
982
  }
972
983
  }
973
984
  } catch (error) {
@@ -1555,54 +1566,80 @@ export class YotoPlayerAccessory {
1555
1566
  // ==================== Volume Limit Lightbulb Getters/Setters ====================
1556
1567
 
1557
1568
  /**
1558
- * Get day max volume limit
1569
+ * Get day max volume limit as percentage (mapped from 0-16 steps)
1559
1570
  * @returns {Promise<CharacteristicValue>}
1560
1571
  */
1561
1572
  async getDayMaxVolume () {
1562
1573
  const limit = this.#deviceModel.config.maxVolumeLimit
1563
- return Number.isFinite(limit) ? limit : 16
1574
+ const steps = Number.isFinite(limit) ? limit : 16
1575
+ const clampedSteps = Math.max(0, Math.min(steps, 16))
1576
+ const percent = Math.round((clampedSteps / 16) * 100)
1577
+ this.#log.debug(
1578
+ LOG_PREFIX.ACCESSORY,
1579
+ `[${this.#device.name}] Get day max volume limit rawSteps=${limit} steps=${clampedSteps} percent=${percent}`
1580
+ )
1581
+ return percent
1564
1582
  }
1565
1583
 
1566
1584
  /**
1567
- * Set day max volume limit
1585
+ * Set day max volume limit as percentage (mapped to 0-16 steps)
1568
1586
  * @param {CharacteristicValue} value
1569
1587
  */
1570
1588
  async setDayMaxVolume (value) {
1571
- const requested = typeof value === 'number' ? value : Number(value)
1572
- if (!Number.isFinite(requested)) {
1589
+ const requestedPercent = typeof value === 'number' ? value : Number(value)
1590
+ if (!Number.isFinite(requestedPercent)) {
1573
1591
  throw new this.#platform.api.hap.HapStatusError(
1574
1592
  this.#platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST
1575
1593
  )
1576
1594
  }
1577
1595
 
1578
- 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`)
1596
+ const normalizedPercent = Math.max(0, Math.min(Math.round(requestedPercent), 100))
1597
+ const requestedSteps = Math.round((normalizedPercent / 100) * 16)
1598
+ const limit = Math.max(0, Math.min(Math.round(requestedSteps), 16))
1599
+ const limitPercent = Math.round((limit / 16) * 100)
1600
+ this.#log.debug(
1601
+ LOG_PREFIX.ACCESSORY,
1602
+ `[${this.#device.name}] Set day max volume limit raw=${value} normalizedPercent=${normalizedPercent} requestedSteps=${requestedSteps} -> steps=${limit} percent=${limitPercent}`
1603
+ )
1580
1604
  await this.#deviceModel.updateConfig({ maxVolumeLimit: limit })
1581
1605
  }
1582
1606
 
1583
1607
  /**
1584
- * Get night max volume limit
1608
+ * Get night max volume limit as percentage (mapped from 0-16 steps)
1585
1609
  * @returns {Promise<CharacteristicValue>}
1586
1610
  */
1587
1611
  async getNightMaxVolume () {
1588
1612
  const limit = this.#deviceModel.config.nightMaxVolumeLimit
1589
- return Number.isFinite(limit) ? limit : 10
1613
+ const steps = Number.isFinite(limit) ? limit : 10
1614
+ const clampedSteps = Math.max(0, Math.min(steps, 16))
1615
+ const percent = Math.round((clampedSteps / 16) * 100)
1616
+ this.#log.debug(
1617
+ LOG_PREFIX.ACCESSORY,
1618
+ `[${this.#device.name}] Get night max volume limit rawSteps=${limit} steps=${clampedSteps} percent=${percent}`
1619
+ )
1620
+ return percent
1590
1621
  }
1591
1622
 
1592
1623
  /**
1593
- * Set night max volume limit
1624
+ * Set night max volume limit as percentage (mapped to 0-16 steps)
1594
1625
  * @param {CharacteristicValue} value
1595
1626
  */
1596
1627
  async setNightMaxVolume (value) {
1597
- const requested = typeof value === 'number' ? value : Number(value)
1598
- if (!Number.isFinite(requested)) {
1628
+ const requestedPercent = typeof value === 'number' ? value : Number(value)
1629
+ if (!Number.isFinite(requestedPercent)) {
1599
1630
  throw new this.#platform.api.hap.HapStatusError(
1600
1631
  this.#platform.api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST
1601
1632
  )
1602
1633
  }
1603
1634
 
1604
- 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`)
1635
+ const normalizedPercent = Math.max(0, Math.min(Math.round(requestedPercent), 100))
1636
+ const requestedSteps = Math.round((normalizedPercent / 100) * 16)
1637
+ const limit = Math.max(0, Math.min(Math.round(requestedSteps), 16))
1638
+ const limitPercent = Math.round((limit / 16) * 100)
1639
+ this.#log.debug(
1640
+ LOG_PREFIX.ACCESSORY,
1641
+ `[${this.#device.name}] Set night max volume limit raw=${value} normalizedPercent=${normalizedPercent} requestedSteps=${requestedSteps} -> steps=${limit} percent=${limitPercent}`
1642
+ )
1606
1643
  await this.#deviceModel.updateConfig({ nightMaxVolumeLimit: limit })
1607
1644
  }
1608
1645
 
@@ -1631,33 +1668,19 @@ export class YotoPlayerAccessory {
1631
1668
 
1632
1669
  const { Characteristic } = this.#platform
1633
1670
  if (volumeSteps > 0) {
1634
- this.#lastNonZeroVolume = volumeSteps
1671
+ this.#lastNonZeroVolume = Math.round((volumeSteps / 16) * 100)
1635
1672
  }
1636
1673
 
1674
+ const normalizedVolume = Number.isFinite(volumeSteps) ? volumeSteps : 0
1675
+ const clampedVolume = Math.max(0, Math.min(normalizedVolume, 16))
1676
+ const percent = Math.round((clampedVolume / 16) * 100)
1677
+ this.#log.debug(
1678
+ LOG_PREFIX.ACCESSORY,
1679
+ `[${this.#device.name}] Update volume characteristic rawSteps=${volumeSteps} steps=${clampedVolume} percent=${percent}`
1680
+ )
1637
1681
  this.volumeService
1638
1682
  .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`)
1683
+ .updateValue(percent)
1661
1684
  }
1662
1685
 
1663
1686
  /**
@@ -1920,16 +1943,28 @@ export class YotoPlayerAccessory {
1920
1943
 
1921
1944
  if (this.dayMaxVolumeService) {
1922
1945
  const limit = Number.isFinite(config.maxVolumeLimit) ? config.maxVolumeLimit : 16
1946
+ const clampedLimit = Math.max(0, Math.min(limit, 16))
1947
+ const percent = Math.round((clampedLimit / 16) * 100)
1948
+ this.#log.debug(
1949
+ LOG_PREFIX.ACCESSORY,
1950
+ `[${this.#device.name}] Update day max volume characteristic rawSteps=${limit} steps=${clampedLimit} percent=${percent}`
1951
+ )
1923
1952
  this.dayMaxVolumeService
1924
1953
  .getCharacteristic(Characteristic.Brightness)
1925
- .updateValue(limit)
1954
+ .updateValue(percent)
1926
1955
  }
1927
1956
 
1928
1957
  if (this.nightMaxVolumeService) {
1929
1958
  const limit = Number.isFinite(config.nightMaxVolumeLimit) ? config.nightMaxVolumeLimit : 10
1959
+ const clampedLimit = Math.max(0, Math.min(limit, 16))
1960
+ const percent = Math.round((clampedLimit / 16) * 100)
1961
+ this.#log.debug(
1962
+ LOG_PREFIX.ACCESSORY,
1963
+ `[${this.#device.name}] Update night max volume characteristic rawSteps=${limit} steps=${clampedLimit} percent=${percent}`
1964
+ )
1930
1965
  this.nightMaxVolumeService
1931
1966
  .getCharacteristic(Characteristic.Brightness)
1932
- .updateValue(limit)
1967
+ .updateValue(percent)
1933
1968
  }
1934
1969
  }
1935
1970
 
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 }) => {
@@ -188,68 +188,96 @@ export class YotoPlatform {
188
188
  this.log.info(`Device offline: ${deviceId}${reason}`)
189
189
  })
190
190
 
191
+ const getDeviceLabel = (deviceId) => {
192
+ const deviceName = this.yotoAccount?.getDevice(deviceId)?.device?.name
193
+ return deviceName || deviceId
194
+ }
195
+
196
+ const formatLegacyStatusFields = (message) => {
197
+ const status = message?.status
198
+ if (!status || typeof status !== 'object') return ''
199
+ const fields = Object.keys(status)
200
+ if (!fields.length) return ''
201
+ const preview = fields.slice(0, 8).join(', ')
202
+ const suffix = fields.length > 8 ? `, +${fields.length - 8} more` : ''
203
+ return ` fields: ${preview}${suffix}`
204
+ }
205
+
191
206
  this.yotoAccount.on('statusUpdate', ({ deviceId, source, changedFields }) => {
207
+ const label = getDeviceLabel(deviceId)
192
208
  const fields = Array.from(changedFields).join(', ')
193
- this.log.debug(`Status update [${deviceId} ${source}]: ${fields}`)
209
+ this.log.debug(`Status update [${label} ${source}]: ${fields}`)
194
210
  })
195
211
 
196
212
  this.yotoAccount.on('configUpdate', ({ deviceId, changedFields }) => {
213
+ const label = getDeviceLabel(deviceId)
197
214
  const fields = Array.from(changedFields).join(', ')
198
- this.log.debug(`Config update [${deviceId}]: ${fields}`)
215
+ this.log.debug(`Config update [${label}]: ${fields}`)
199
216
  })
200
217
 
201
218
  this.yotoAccount.on('playbackUpdate', ({ deviceId, changedFields }) => {
219
+ const label = getDeviceLabel(deviceId)
202
220
  const fields = Array.from(changedFields).join(', ')
203
- this.log.debug(`Playback update [${deviceId}]: ${fields}`)
221
+ this.log.debug(`Playback update [${label}]: ${fields}`)
204
222
  })
205
223
 
206
224
  this.yotoAccount.on('mqttConnect', ({ deviceId }) => {
207
- const deviceName = this.yotoAccount?.getDevice(deviceId)?.device?.name
208
- const label = deviceName ? `${deviceName} (${deviceId})` : deviceId
225
+ const label = getDeviceLabel(deviceId)
209
226
  this.log.debug(`MQTT connected: ${label}`)
210
227
  })
211
228
 
212
229
  this.yotoAccount.on('mqttDisconnect', ({ deviceId, metadata }) => {
230
+ const label = getDeviceLabel(deviceId)
213
231
  const reasonCode = metadata?.packet?.reasonCode
214
232
  const reason = typeof reasonCode === 'number' ? ` (code ${reasonCode})` : ''
215
- this.log.warn(`MQTT disconnected: ${deviceId}${reason}`)
233
+ this.log.warn(`MQTT disconnected: ${label}${reason}`)
216
234
  })
217
235
 
218
236
  this.yotoAccount.on('mqttClose', ({ deviceId, metadata }) => {
237
+ const label = getDeviceLabel(deviceId)
219
238
  const reason = metadata?.reason ? ` (${metadata.reason})` : ''
220
- this.log.debug(`MQTT closed: ${deviceId}${reason}`)
239
+ this.log.debug(`MQTT closed: ${label}${reason}`)
221
240
  })
222
241
 
223
242
  this.yotoAccount.on('mqttReconnect', ({ deviceId }) => {
224
- this.log.debug(`MQTT reconnecting: ${deviceId}`)
243
+ const label = getDeviceLabel(deviceId)
244
+ this.log.debug(`MQTT reconnecting: ${label}`)
225
245
  })
226
246
 
227
247
  this.yotoAccount.on('mqttOffline', ({ deviceId }) => {
228
- this.log.debug(`MQTT offline: ${deviceId}`)
248
+ const label = getDeviceLabel(deviceId)
249
+ this.log.debug(`MQTT offline: ${label}`)
229
250
  })
230
251
 
231
252
  this.yotoAccount.on('mqttEnd', ({ deviceId }) => {
232
- this.log.debug(`MQTT ended: ${deviceId}`)
253
+ const label = getDeviceLabel(deviceId)
254
+ this.log.debug(`MQTT ended: ${label}`)
233
255
  })
234
256
 
235
257
  this.yotoAccount.on('mqttStatus', ({ deviceId, topic }) => {
236
- this.log.debug(`MQTT status [${deviceId}]: ${topic}`)
258
+ const label = getDeviceLabel(deviceId)
259
+ this.log.debug(`MQTT status [${label}]: ${topic}`)
237
260
  })
238
261
 
239
262
  this.yotoAccount.on('mqttEvents', ({ deviceId, topic }) => {
240
- this.log.debug(`MQTT events [${deviceId}]: ${topic}`)
263
+ const label = getDeviceLabel(deviceId)
264
+ this.log.debug(`MQTT events [${label}]: ${topic}`)
241
265
  })
242
266
 
243
- this.yotoAccount.on('mqttStatusLegacy', ({ deviceId, topic }) => {
244
- this.log.debug(`MQTT legacy status [${deviceId}]: ${topic}`)
267
+ this.yotoAccount.on('mqttStatusLegacy', ({ deviceId, topic, message }) => {
268
+ const label = getDeviceLabel(deviceId)
269
+ const fields = formatLegacyStatusFields(message)
270
+ this.log.debug(`MQTT legacy status [${label}]: ${topic}${fields}`)
245
271
  })
246
272
 
247
273
  this.yotoAccount.on('mqttResponse', ({ deviceId, topic }) => {
248
- this.log.debug(`MQTT response [${deviceId}]: ${topic}`)
274
+ const label = getDeviceLabel(deviceId)
275
+ this.log.debug(`MQTT response [${label}]: ${topic}`)
249
276
  })
250
277
 
251
278
  this.yotoAccount.on('mqttUnknown', ({ deviceId, topic }) => {
252
- this.log.debug(`MQTT unknown [${deviceId}]: ${topic}`)
279
+ const label = getDeviceLabel(deviceId)
280
+ this.log.debug(`MQTT unknown [${label}]: ${topic}`)
253
281
  })
254
282
 
255
283
  // Start the account (discovers devices, creates device models, starts MQTT)
@@ -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.38",
5
5
  "author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/bcomnes/homebridge-yoto/issues"