@homebridge-plugins/homebridge-govee 11.32.0-beta.2 → 11.32.0-beta.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.
@@ -23,10 +23,17 @@
23
23
  border-color: #444444;
24
24
  color: #eeeeee;
25
25
  }
26
+
27
+ .text-muted {
28
+ color: #aaaaaa !important;
29
+ }
26
30
  }
27
31
  .min-w-50 {
28
32
  min-width: 50px;
29
33
  }
34
+ .action-desc {
35
+ font-size: 0.85rem;
36
+ }
30
37
  select {
31
38
  background-image:
32
39
  linear-gradient(45deg, transparent 50%, gray 50%),
@@ -82,31 +89,57 @@
82
89
  </div>
83
90
  <div id="deviceInfo">
84
91
  <form>
85
- <div class="form-group">
92
+ <div class="form-group mb-0">
86
93
  <select class="form-control" id="deviceSelect"></select>
87
94
  </div>
88
95
  </form>
89
- <ul class="list-group mt-2 mb-3 mx-0" id="configActionList" style="display: none;">
96
+ <ul class="list-group mt-4 mb-4 mx-0" id="configActionList" style="display: none;">
90
97
  <li class="list-group-item">
91
98
  <div class="d-flex justify-content-between align-items-center">
92
- <span class="pr-2">Adds all devices to the config.</span>
93
- <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="addAllBtn">Add All</button>
99
+ <div class="pr-2">
100
+ <div>Add All Devices</div>
101
+ <div class="text-muted action-desc">Add all devices to the config that are not already in the config.</div>
102
+ </div>
103
+ <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="addAllBtn" aria-label="Add all devices to the config">
104
+ <i aria-hidden="true" class="fas fa-arrow-right"></i>
105
+ </button>
94
106
  </div>
95
107
  </li>
96
108
  <li class="list-group-item">
97
109
  <div class="d-flex justify-content-between align-items-center">
98
- <span class="pr-2" id="deviceConfigText">Adds this device to the config.</span>
99
- <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="deviceConfigBtn">Add</button>
110
+ <div class="pr-2">
111
+ <div>Remove All Devices</div>
112
+ <div class="text-muted action-desc" id="removeAllText">Remove every device from the plugin config.</div>
113
+ </div>
114
+ <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="removeAllBtn" aria-label="Remove all devices from the config">
115
+ <i aria-hidden="true" class="fas fa-arrow-right"></i>
116
+ </button>
100
117
  </div>
101
118
  </li>
102
119
  <li class="list-group-item">
103
120
  <div class="d-flex justify-content-between align-items-center">
104
- <span class="pr-2" id="hideDeviceText">Hide this device from HomeKit.</span>
105
- <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="hideDeviceBtn">Hide</button>
121
+ <div class="pr-2">
122
+ <div id="deviceConfigTitle">Add This Device</div>
123
+ <div class="text-muted action-desc" id="deviceConfigText">Add this device to the plugin config.</div>
124
+ </div>
125
+ <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="deviceConfigBtn" aria-label="Add this device to the config">
126
+ <i aria-hidden="true" class="fas fa-arrow-right"></i>
127
+ </button>
128
+ </div>
129
+ </li>
130
+ <li class="list-group-item">
131
+ <div class="d-flex justify-content-between align-items-center">
132
+ <div class="pr-2">
133
+ <div id="hideDeviceTitle">Hide This Device</div>
134
+ <div class="text-muted action-desc" id="hideDeviceText">Hide this device from HomeKit. This adds the device to the config if needed.</div>
135
+ </div>
136
+ <button type="button" class="btn btn-primary m-0 ml-3 py-1 min-w-50" id="hideDeviceBtn" aria-label="Hide this device from HomeKit">
137
+ <i aria-hidden="true" class="fas fa-arrow-right"></i>
138
+ </button>
106
139
  </div>
107
140
  </li>
108
141
  </ul>
109
- <table class="table w-100 mt-3" id="deviceTable" style="display: none;">
142
+ <table class="table w-100 mt-0" id="deviceTable" style="display: none;">
110
143
  <thead>
111
144
  <tr class="table-active">
112
145
  <th scope="col" style="width: 40%;">Device Name</th>
@@ -197,138 +230,69 @@
197
230
  </table>
198
231
  </div>
199
232
  </div>
200
- <div id="pageSupport" class="mt-4" style="display: none;">
233
+ <div id="pageSupport" class="mt-4" style="display: none">
201
234
  <p class="text-center lead">Thank you for using <strong>homebridge-govee</strong></p>
202
- <p class="text-center">The links below will take you to our GitHub wiki</p>
203
- <h4>Setup</h4>
204
- <ul>
205
- <li>
206
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Installation" target="_blank"
207
- >Installation</a
208
- >
209
- </li>
210
- <li>
211
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Configuration" target="_blank"
212
- >Configuration</a
213
- >
214
- </li>
215
- <li>
216
- <a href="https://github.com/homebridge/homebridge/wiki/How-to-Install-Alternate-Plugin-Versions" target="_blank"
217
- >Beta Version</a
218
- >
219
- </li>
220
- <li>
221
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Node-Version" target="_blank"
222
- >Node Version</a
223
- >
224
- </li>
225
- </ul>
226
- <h4>Features</h4>
235
+ <h5>Plugin Information</h5>
236
+ <ul>
237
+ <li>This plugin allows you to view and control your Govee devices within HomeKit. The plugin:</li>
227
238
  <ul>
228
- <li>
229
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Supported-Devices" target="_blank"
230
- >Supported Devices</a
231
- >
232
- </li>
233
- <li>
234
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Connection-Methods" target="_blank"
235
- >Connection Methods</a
236
- >
237
- </li>
238
- <ul>
239
- <li>
240
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/LAN-Control" target="_blank"
241
- >LAN Control</a
242
- >
243
- </li>
244
- <li>
245
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/API-Control" target="_blank"
246
- >API Control</a
247
- >
248
- </li>
249
- <li>
250
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/AWS-Control" target="_blank"
251
- >AWS Control</a
252
- >
253
- </li>
254
- <li>
255
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Bluetooth-Control" target="_blank"
256
- >BLE Control</a
257
- >
258
- </li>
259
- </ul>
260
- <li>
261
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Scene%2C-Music%2C-DIY-Modes" target="_blank"
262
- >Scene, Music, DIY Modes</a
263
- >
264
- </li>
239
+ <li>supports Govee OpenAPI discovery/state/control when an OpenAPI key is provided</li>
240
+ <li>requires your Govee credentials for most Cloud/AWS/BLE connections</li>
241
+ <li>can control certain models locally via LAN control without any Govee credentials</li>
242
+ <li>may use different connection methods depending on device model and account capabilities</li>
265
243
  </ul>
266
- <h4>Help/About</h4>
244
+ </ul>
245
+ <h5>Prerequisites</h5>
246
+ <ul>
247
+ <li>To use this plugin, you will need to already have:</li>
267
248
  <ul>
268
- <li>
269
- <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Common-Errors" target="_blank"
270
- >Common Errors</a
271
- >
272
- </li>
273
- <li>
274
- <a href="https://github.com/homebridge-plugins/homebridge-govee/issues/new/choose" target="_blank"
275
- >Support Request</a
276
- >
277
- </li>
278
- <li>
279
- <a href="https://github.com/homebridge-plugins/homebridge-govee/blob/latest/CHANGELOG.md" target="_blank"
280
- >Changelog</a
281
- >
282
- </li>
283
- <li><a href="https://github.com/sponsors/bwp91" target="_blank">About Me</a></li>
249
+ <li><a href="https://nodejs.org" target="_blank">Node</a>: latest version of <code>v22</code> or <code>v24</code> - any other major version is not supported.</li>
250
+ <li><a href="https://homebridge.io" target="_blank">Homebridge</a>: <code>v1.6</code> or above - refer to link for more information and installation instructions.</li>
251
+ <li>For bluetooth connectivity, it may be necessary to install extra packages on your system, see <a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Bluetooth-Control" target="_blank">Bluetooth Control</a>. Bluetooth works best when using a Raspberry Pi, not been tested on Windows, and Mac devices are unsupported.</li>
284
252
  </ul>
285
- <h4>Credits</h4>
253
+ </ul>
254
+ <h5>Setup</h5>
255
+ <ul>
256
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Installation" target="_blank">Installation</a></li>
257
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Configuration" target="_blank">Configuration</a></li>
258
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Beta-Version" target="_blank">Beta Version</a></li>
259
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Node-Version" target="_blank">Node Version</a></li>
260
+ </ul>
261
+ <h5>Features</h5>
262
+ <ul>
263
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Supported-Devices" target="_blank">Supported Devices</a></li>
264
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Connection-Methods" target="_blank">Connection Methods</a></li>
286
265
  <ul>
287
- <li>To all users who have shared their devices to enable functionality.</li>
288
- <li>
289
- To the creator/owner of the
290
- <a href="https://www.npmjs.com/package/govee-led-client" target="_blank">govee-led-client</a>
291
- library which made the bluetooth connection possible.
292
- </li>
293
- <li>
294
- To the creator/owner of the
295
- <a href="https://github.com/towlerj/govee_api" target="_blank">govee_api</a>
296
- library which made the AWS connection possible.
297
- </li>
298
- <li>
299
- To <a href="https://github.com/alboiuvlad29" target="_blank">@alboiuvlad29</a> who made the LAN connection possible.
300
- </li>
301
- <li>
302
- To <a href="https://github.com/JeremyDunn" target="_blank">@JeremyDunn</a> for his code from
303
- <a href="https://github.com/JeremyDunn/homebridge-govee-water-detectors" target="_blank"
304
- >homebridge-govee-water-detectors</a
305
- >
306
- for leak sensor support.
307
- </li>
308
- <li>
309
- To the creator/owner of the
310
- <a href="https://www.npmjs.com/package/govee-bt-client" target="_blank"
311
- >govee-bt-client</a
312
- >
313
- library which made the BLE connection to sensors possible.
314
- </li>
315
- <li>
316
- To the creator of the awesome plugin header logo:
317
- <a href="https://www.instagram.com/keryan.me" target="_blank">Keryan Belahcene</a>.
318
- </li>
319
- <li>
320
- To the creators/contributors of
321
- <a href="https://homebridge.io" target="_blank">Homebridge</a> who make this plugin possible.
322
- </li>
323
- </ul>
324
- <h4>Disclaimer</h4>
325
- <ul>
326
- <li>
327
- I am in no way affiliated with Govee and this plugin is a personal project that I maintain in
328
- my free time.
329
- </li>
330
- <li>Use this plugin entirely at your own risk - please see licence for more information.</li>
266
+ <li>OpenAPI Control</li>
267
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/LAN-Control" target="_blank">LAN Control</a></li>
268
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/AWS-Control" target="_blank">AWS Control</a></li>
269
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Bluetooth-Control" target="_blank">BLE Control</a></li>
331
270
  </ul>
271
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Scene%2C-Music%2C-DIY-Modes" target="_blank">Scene, Music, DIY Modes</a></li>
272
+ </ul>
273
+ <h5>Help/About</h5>
274
+ <ul>
275
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/wiki/Common-Errors" target="_blank">Common Errors</a></li>
276
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/issues/new/choose" target="_blank">Support Request</a></li>
277
+ <li><a href="https://github.com/homebridge-plugins/homebridge-govee/blob/latest/CHANGELOG.md" target="_blank">Changelog</a></li>
278
+ <li><a href="https://github.com/sponsors/bwp91" target="_blank">About Me</a></li>
279
+ </ul>
280
+ <h5>Credits</h5>
281
+ <ul>
282
+ <li>To all users who have shared their devices to enable functionality.</li>
283
+ <li>To the creator/owner of the <a href="https://www.npmjs.com/package/govee-led-client" target="_blank">govee-led-client</a> library which made the BLE connection possible.</li>
284
+ <li>To the creator/owner of the <a href="https://github.com/towlerj/govee_api" target="_blank">govee_api</a> library which made the AWS connection possible.</li>
285
+ <li>To <a href="https://github.com/alboiuvlad29" target="_blank">@alboiuvlad29</a> who made the LAN connection possible.</li>
286
+ <li>To <a href="https://github.com/JeremyDunn" target="_blank">@JeremyDunn</a> for his code from <a href="https://github.com/JeremyDunn/homebridge-govee-water-detectors" target="_blank">homebridge-govee-water-detectors</a> for leak sensor support.</li>
287
+ <li>To the creator/owner of the <a href="https://www.npmjs.com/package/govee-bt-client" target="_blank">govee-bt-client</a> library which made the BLE connection to sensors possible.</li>
288
+ <li>To the creator of the awesome plugin header logo: <a href="https://www.instagram.com/keryan.me" target="_blank">Keryan Belahcene</a>.</li>
289
+ <li>To the creators/contributors of <a href="https://homebridge.io" target="_blank">Homebridge</a> who make this plugin possible.</li>
290
+ </ul>
291
+ <h5>Disclaimer</h5>
292
+ <ul>
293
+ <li>I am in no way affiliated with Govee and this plugin is a personal project that I maintain in my free time.</li>
294
+ <li>Use this plugin entirely at your own risk - please see licence for more information.</li>
295
+ </ul>
332
296
  </div>
333
297
  <script>
334
298
  ;(async () => {
@@ -355,6 +319,7 @@
355
319
  let pickerAccessories = []
356
320
  let pickerModelMap = {}
357
321
  let removeConfirmTimer
322
+ let removeAllConfirmTimer
358
323
 
359
324
  // Compare device ids ignoring case, colons and underscores, so an entry
360
325
  // typed by hand in a different format still counts as the same device
@@ -381,13 +346,19 @@
381
346
  return null
382
347
  }
383
348
 
349
+ // The action buttons always show the settings-page arrow; the row text
350
+ // (and the button's aria-label) says what the arrow will do
351
+ const actionArrow = '<i aria-hidden="true" class="fas fa-arrow-right"></i>'
352
+
384
353
  const setHideControl = hidden => {
385
354
  const btn = document.getElementById('hideDeviceBtn')
386
355
  btn.dataset.hidden = hidden ? 'yes' : 'no'
387
- btn.innerHTML = hidden ? 'Show' : 'Hide'
356
+ btn.innerHTML = actionArrow
357
+ btn.setAttribute('aria-label', hidden ? 'Show this device in HomeKit' : 'Hide this device from HomeKit')
358
+ document.getElementById('hideDeviceTitle').innerHTML = hidden ? 'Show This Device' : 'Hide This Device'
388
359
  document.getElementById('hideDeviceText').innerHTML = hidden
389
- ? 'Show this device in HomeKit.'
390
- : 'Hide this device from HomeKit.'
360
+ ? 'Show this device in HomeKit again. The config entry is kept.'
361
+ : 'Hide this device from HomeKit. This adds the device to the config if needed.'
391
362
  }
392
363
 
393
364
  const setConfigButton = inConfig => {
@@ -397,10 +368,12 @@
397
368
  btn.dataset.state = inConfig ? 'remove' : 'add'
398
369
  btn.classList.remove('btn-danger')
399
370
  btn.classList.add('btn-primary')
400
- btn.innerHTML = inConfig ? 'Remove' : 'Add'
371
+ btn.innerHTML = actionArrow
372
+ btn.setAttribute('aria-label', inConfig ? 'Remove this device from the config' : 'Add this device to the config')
373
+ document.getElementById('deviceConfigTitle').innerHTML = inConfig ? 'Remove This Device' : 'Add This Device'
401
374
  document.getElementById('deviceConfigText').innerHTML = inConfig
402
- ? 'Removes this device from the config.'
403
- : 'Adds this device to the config.'
375
+ ? 'Remove this device from the plugin config.'
376
+ : 'Add this device to the plugin config.'
404
377
  }
405
378
 
406
379
  const addDeviceToConfig = (config, acc) => {
@@ -453,8 +426,9 @@
453
426
  btn.dataset.confirm = 'yes'
454
427
  btn.classList.remove('btn-primary')
455
428
  btn.classList.add('btn-danger')
456
- btn.innerHTML = 'Confirm'
457
- document.getElementById('deviceConfigText').innerHTML = 'This entry has extra settings which will be lost - click again to remove it.'
429
+ btn.innerHTML = actionArrow
430
+ btn.setAttribute('aria-label', 'Confirm removing this device from the config')
431
+ document.getElementById('deviceConfigText').innerHTML = 'This entry has extra settings which will be lost. Click again to remove it.'
458
432
  removeConfirmTimer = setTimeout(() => setConfigButton(true), 5000)
459
433
  return
460
434
  }
@@ -546,6 +520,57 @@
546
520
  homebridge.toast.error(err.message, 'Error')
547
521
  }
548
522
  })
523
+ const resetRemoveAllButton = () => {
524
+ const btn = document.getElementById('removeAllBtn')
525
+ clearTimeout(removeAllConfirmTimer)
526
+ delete btn.dataset.confirm
527
+ btn.classList.remove('btn-danger')
528
+ btn.classList.add('btn-primary')
529
+ btn.innerHTML = actionArrow
530
+ btn.setAttribute('aria-label', 'Remove all devices from the config')
531
+ document.getElementById('removeAllText').innerHTML = 'Remove every device from the plugin config.'
532
+ }
533
+
534
+ document.getElementById('removeAllBtn').addEventListener('click', async () => {
535
+ try {
536
+ const btn = document.getElementById('removeAllBtn')
537
+ const blocks = await getConfigBlocks()
538
+ const config = blocks[0]
539
+ let total = 0
540
+ deviceArrayKeys.forEach((key) => {
541
+ if (Array.isArray(config[key])) {
542
+ total += config[key].length
543
+ }
544
+ })
545
+ if (total === 0) {
546
+ resetRemoveAllButton()
547
+ homebridge.toast.info('There are no devices in the config.', 'Nothing To Remove')
548
+ return
549
+ }
550
+ if (btn.dataset.confirm !== 'yes') {
551
+ // Removing everything always asks for a second click, since any
552
+ // settings on the entries would be lost
553
+ btn.dataset.confirm = 'yes'
554
+ btn.classList.remove('btn-primary')
555
+ btn.classList.add('btn-danger')
556
+ btn.innerHTML = actionArrow
557
+ btn.setAttribute('aria-label', 'Confirm removing all devices from the config')
558
+ document.getElementById('removeAllText').innerHTML = 'Any settings on these entries will be lost. Click again to remove all ' + total + (total === 1 ? ' entry.' : ' entries.')
559
+ removeAllConfirmTimer = setTimeout(resetRemoveAllButton, 5000)
560
+ return
561
+ }
562
+ deviceArrayKeys.forEach((key) => {
563
+ delete config[key]
564
+ })
565
+ await homebridge.updatePluginConfig(blocks)
566
+ resetRemoveAllButton()
567
+ setConfigButton(false)
568
+ setHideControl(false)
569
+ homebridge.toast.success(total + ' device entr' + (total === 1 ? 'y' : 'ies') + ' removed - press Save to apply.', 'Removed From Config')
570
+ } catch (err) {
571
+ homebridge.toast.error(err.message, 'Error')
572
+ }
573
+ })
549
574
  // ===== End device picker =====
550
575
 
551
576
  showIntro = () => {
@@ -31,6 +31,12 @@ const categoryToArray = {
31
31
  template: 'lightDevices',
32
32
  }
33
33
 
34
+ // Models whose category does not say what the device actually is, so the
35
+ // picker would file them in the wrong config section without an override
36
+ const modelOverrides = {
37
+ H5085: 'switchDevices', // dual smart plug, in the template category
38
+ }
39
+
34
40
  class PluginUiServer extends HomebridgePluginUiServer {
35
41
  constructor() {
36
42
  super()
@@ -44,6 +50,7 @@ class PluginUiServer extends HomebridgePluginUiServer {
44
50
  modelToArray[model] = arrayKey
45
51
  })
46
52
  })
53
+ Object.assign(modelToArray, modelOverrides)
47
54
  return modelToArray
48
55
  })
49
56
 
package/lib/platform.js CHANGED
@@ -1444,6 +1444,18 @@ export default class {
1444
1444
  accessory.context.hasOpenApiControl = true
1445
1445
  accessory.context.useOpenApiControl = !!this.openApiClient
1446
1446
  accessory.context.openApiCapabilities = device.openApiInfo.byInstance || {}
1447
+
1448
+ // Log what Govee says this device can do. Working out whether a model
1449
+ // exposes something like a night light otherwise means reverse
1450
+ // engineering its status messages, when the answer is right here
1451
+ const instances = Object.entries(accessory.context.openApiCapabilities)
1452
+ .map(([instance, capability]) => `${instance} (${capability?.type ?? 'unknown type'})`)
1453
+ this.log.debug(
1454
+ '[%s] openapi category [%s] capabilities [%s].',
1455
+ accessory.displayName,
1456
+ device.openApiInfo.category || 'unknown',
1457
+ instances.join(', ') || 'none',
1458
+ )
1447
1459
  }
1448
1460
 
1449
1461
  // Create the instance for this device type
@@ -1988,6 +2000,26 @@ export default class {
1988
2000
 
1989
2001
  const data = parseDeviceUpdate(params, accessory.context)
1990
2002
 
2003
+ // The payload above logs the device's own status codes as base64 buried in
2004
+ // a large JSON blob, which is unreadable when working out what a device can
2005
+ // do. Decode them so a code like `aa 36 01 01` can be spotted by eye, and
2006
+ // matched to the `33 36 01 01` needed to set it. Codes with no handler yet
2007
+ // are exactly the ones worth seeing, so log them all rather than guessing
2008
+ // which are already understood
2009
+ const statusCodes = (Array.isArray(data.commands) ? data.commands : [])
2010
+ .map((command) => {
2011
+ try {
2012
+ return base64ToHex(command)
2013
+ } catch {
2014
+ return ''
2015
+ }
2016
+ })
2017
+ .filter(hexString => hexString.startsWith('aa'))
2018
+ .map(hexString => hexString.replace(/../g, '$& ').trim())
2019
+ if (statusCodes.length > 0) {
2020
+ accessory.logDebug(`[${params.source}] ${platformLang.statusCodes}:\n ${statusCodes.join('\n ')}`)
2021
+ }
2022
+
1991
2023
  // Log if kelvin was out of range before clamping
1992
2024
  if (data.kelvinOutOfRange) {
1993
2025
  accessory.logDebug(`govee provided a kelvin out of range [${params.state?.colorTemInKelvin}]`)
@@ -229,6 +229,7 @@ export default {
229
229
  'H60B0',
230
230
  'H60B1',
231
231
  'H60B2',
232
+ 'H60B3',
232
233
  'H60C1',
233
234
  'H6101',
234
235
  'H6102',
@@ -506,8 +507,11 @@ export default {
506
507
  'R16D0',
507
508
  'R1741',
508
509
  ],
509
- switchSingle: ['H5001', 'H5080', 'H5081', 'H5083', 'H5086', 'H7014'],
510
- switchDouble: ['H5082'],
510
+ // H5901 is a water timer, controlled only through the OpenAPI - it has no
511
+ // usable AWS, BLE or LAN path, so it needs an API key configured. Set
512
+ // showAs to 'valve' or 'tap' for a more fitting HomeKit tile (#1324)
513
+ switchSingle: ['H5001', 'H5080', 'H5081', 'H5083', 'H5086', 'H5901', 'H7014'],
514
+ switchDouble: ['H5082', 'H5089'],
511
515
  switchTriple: ['H5160'],
512
516
  sensorLeak: ['H5054', 'H5058', 'H5059'],
513
517
  sensorThermo: [
@@ -562,7 +566,6 @@ export default {
562
566
  'H5042', // https://github.com/homebridge-plugins/homebridge-govee/issues/849
563
567
  'H5043', // https://github.com/homebridge-plugins/homebridge-govee/issues/558
564
568
  'H5085', // https://github.com/homebridge-plugins/homebridge-govee/issues/951
565
- 'H5089', // https://github.com/homebridge-plugins/homebridge-govee/issues/1242
566
569
  'H5111', // https://github.com/homebridge-plugins/homebridge-govee/issues/1117
567
570
  'H5121', // https://github.com/homebridge-plugins/homebridge-govee/issues/913
568
571
  'H5124', // https://github.com/homebridge-plugins/homebridge-govee/issues/1119
@@ -106,9 +106,44 @@ function statusToActionCode(statusCode) {
106
106
  return Buffer.from(generatedCode, 'base64').toString('hex')
107
107
  }
108
108
 
109
+ /**
110
+ * Convert a HomeKit RotationSpeed percentage into a device speed step.
111
+ *
112
+ * `steps` is the number of REAL speeds the model has, excluding auto (8 on the
113
+ * H7100 family, 12 on the H7105/H7107/H1370). A percentage of 0 means "off" and
114
+ * is handled by the caller, so this always returns a usable step in 1..steps.
115
+ *
116
+ * Auto is deliberately NOT reachable from here. Auto is a mode, not a speed, so
117
+ * 100% means maximum speed. Exposing auto at the top of the slider is what made
118
+ * the previous behaviour surprising - dragging to full would silently select
119
+ * auto instead of full power.
120
+ */
121
+ function hkPercentToFanSpeed(percent, steps) {
122
+ return Math.max(1, Math.min(steps, Math.round((percent * steps) / 100)))
123
+ }
124
+
125
+ /**
126
+ * Convert a device speed step into a HomeKit RotationSpeed percentage.
127
+ *
128
+ * When the device reports auto, HomeKit is shown 100%, but callers should keep
129
+ * their own cache at `autoSpeed`. That lets the plugin tell "the device is in
130
+ * auto" apart from "the user asked for maximum speed", and so avoid echoing a
131
+ * redundant command straight back to the device.
132
+ */
133
+ function fanSpeedToHkPercent(speed, steps, autoSpeed) {
134
+ if (autoSpeed !== undefined && speed === autoSpeed) {
135
+ return 100
136
+ }
137
+ if (!speed || speed < 1) {
138
+ return 0
139
+ }
140
+ return Math.min(100, Math.round((speed / steps) * 100))
141
+ }
142
+
109
143
  export {
110
144
  base64ToHex,
111
145
  cenToFar,
146
+ fanSpeedToHkPercent,
112
147
  farToCen,
113
148
  generateCodeFromHexValues,
114
149
  generateRandomString,
@@ -117,6 +152,7 @@ export {
117
152
  hexToBase64,
118
153
  hexToDecimal,
119
154
  hexToTwoItems,
155
+ hkPercentToFanSpeed,
120
156
  nearestHalf,
121
157
  parseDeviceId,
122
158
  parseError,
@@ -133,5 +133,6 @@ export default {
133
133
  syncFail: 'sync failed as',
134
134
  syncStillRunning: 'skipping this sync as the previous one has not finished yet',
135
135
  viaAL: 'via adaptive lighting',
136
+ statusCodes: 'device status codes',
136
137
  unknownCommand: 'unknown command in payload received',
137
138
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@homebridge-plugins/homebridge-govee",
3
3
  "alias": "Govee",
4
4
  "type": "module",
5
- "version": "11.32.0-beta.2",
5
+ "version": "11.32.0-beta.21",
6
6
  "description": "Homebridge plugin to integrate Govee devices into HomeKit.",
7
7
  "author": {
8
8
  "name": "bwp91",
@@ -38,6 +38,7 @@
38
38
  "keywords": [
39
39
  "homebridge",
40
40
  "homebridge-plugin",
41
+ "supports-hap",
41
42
  "homebridge-govee",
42
43
  "hoobs",
43
44
  "hoobs-plugin",
@@ -62,21 +63,24 @@
62
63
  "mqtt": "^5.15.2",
63
64
  "node-persist": "^4.0.4",
64
65
  "node-rsa": "^2.0.0",
65
- "p-queue": "^9.3.1",
66
+ "p-queue": "^9.3.3",
66
67
  "patch-package": "^8.0.1",
67
68
  "pem": "^1.14.8"
68
69
  },
69
70
  "optionalDependencies": {
70
71
  "@stoprocent/bluetooth-hci-socket": "^2.2.6",
71
- "@stoprocent/noble": "^2.5.7"
72
+ "@stoprocent/noble": "^2.5.9"
72
73
  },
73
74
  "devDependencies": {
74
- "@antfu/eslint-config": "^9.1.0"
75
+ "@antfu/eslint-config": "^9.2.0"
75
76
  },
76
77
  "allowScripts": {
77
78
  "@stoprocent/bluetooth-hci-socket@2.2.6": true,
78
- "@stoprocent/noble@2.5.7": true,
79
+ "@stoprocent/noble@2.5.9": true,
79
80
  "usb@2.18.0": true,
80
81
  "@serialport/bindings-cpp@12.0.1": true
82
+ },
83
+ "overrides": {
84
+ "help-me": "^5.0.0"
81
85
  }
82
86
  }