@homebridge-plugins/homebridge-govee 11.25.0 → 11.26.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to `@homebridge-plugins/homebridge-govee` will be documented in this file.
4
4
 
5
+ ## v11.26.0 (2026-07-12)
6
+
7
+ ### Changes
8
+
9
+ - feat: add support for the H609D Galaxy Light Projector (#1298)
10
+ - fix: never remove accessories based on a failed device fetch (#1264)
11
+ - fix: skip Govee app device groups from the OpenAPI device list (#1309)
12
+ - feat: add under-construction support for the H1370 fan (#1307)
13
+ - fix: use the segment-based BLE colour command for the H617E (#1290)
14
+ - fix: send the on command first so scenes from off apply their colour (#1277)
15
+
5
16
  ## v11.25.0 (2026-07-12)
6
17
 
7
18
  ### Changes
@@ -250,8 +250,13 @@ export default class {
250
250
  return
251
251
  }
252
252
 
253
- // Await slightly longer than brightness and colour so on/off is sent last
254
- await sleep(400)
253
+ // Send off after colour/brightness (a colour command can implicitly power a
254
+ // bulb back on), but send on straight away so the bulb is awake in time to
255
+ // receive the colour and brightness a HomeKit scene sends alongside it, as
256
+ // some models ignore these commands while switched off (#1277)
257
+ if (newValue === 'off') {
258
+ await sleep(400)
259
+ }
255
260
 
256
261
  // Send the request to the platform sender function
257
262
  await this.platform.sendDeviceUpdate(this.accessory, {
@@ -316,9 +321,11 @@ export default class {
316
321
  async internalBrightnessUpdate(value) {
317
322
  try {
318
323
  // This acts like a debounce function when endlessly sliding the brightness scale
324
+ // If the light is off (e.g. a HomeKit scene turning it on), wait longer so the
325
+ // on command sent by internalStateUpdate has time to wake the bulb first (#1277)
319
326
  const updateKeyBright = generateRandomString(5)
320
327
  this.updateKeyBright = updateKeyBright
321
- await sleep(350)
328
+ await sleep(this.cacheState !== 'on' ? 1050 : 350)
322
329
  if (updateKeyBright !== this.updateKeyBright) {
323
330
  return
324
331
  }
@@ -367,9 +374,11 @@ export default class {
367
374
  async internalColourUpdate(value, force = false) {
368
375
  try {
369
376
  // This acts like a debounce function when endlessly sliding the colour wheel
377
+ // If the light is off (e.g. a HomeKit scene turning it on), wait longer so the
378
+ // on command sent by internalStateUpdate has time to wake the bulb first (#1277)
370
379
  const updateKeyColour = generateRandomString(5)
371
380
  this.updateKeyColour = updateKeyColour
372
- await sleep(300)
381
+ await sleep(this.cacheState !== 'on' ? 1000 : 300)
373
382
  if (updateKeyColour !== this.updateKeyColour) {
374
383
  return
375
384
  }
@@ -440,9 +449,12 @@ export default class {
440
449
  async internalCTUpdate(value) {
441
450
  try {
442
451
  // This acts like a debounce function when endlessly sliding the colour wheel
452
+ // If the light is off (e.g. a HomeKit scene turning it on), wait longer so the
453
+ // on command sent by internalStateUpdate has time to wake the bulb first, and
454
+ // so the 'skip when off' check below sees the updated state (#1277)
443
455
  const updateKeyCT = generateRandomString(5)
444
456
  this.updateKeyCT = updateKeyCT
445
- await sleep(300)
457
+ await sleep(this.cacheState !== 'on' ? 1000 : 300)
446
458
  if (updateKeyCT !== this.updateKeyCT) {
447
459
  return
448
460
  }
package/lib/platform.js CHANGED
@@ -654,6 +654,12 @@ export default class {
654
654
 
655
655
  if (openApiDevices && openApiDevices.length > 0) {
656
656
  openApiDevices.forEach((openApiDevice) => {
657
+ // Skip Govee app device groups as these are not real devices (#1309)
658
+ if (['BaseGroup', 'SameModeGroup'].includes(openApiDevice.model)) {
659
+ this.log.debug('[%s] %s.', openApiDevice.deviceName, platformLang.devIsGroup)
660
+ return
661
+ }
662
+
657
663
  if (this.ignoredDevices.includes(openApiDevice.device)) {
658
664
  return
659
665
  }
@@ -717,18 +723,28 @@ export default class {
717
723
  }
718
724
 
719
725
  // Check for redundant Homebridge accessories
726
+ // Only treat 'not found in Govee' as a reason to remove an accessory when the
727
+ // configured cloud clients actually returned their device lists this session,
728
+ // otherwise a failed fetch would remove the accessory and destroy any HomeKit
729
+ // scenes and automations it is part of (#1264)
730
+ const cloudListsFetched = (!this.config.username || !!this.httpClient)
731
+ && (!!this.config.openApiDisable || !this.config.apiKey || !!this.openApiClient)
720
732
  devicesInHB.forEach((accessory) => {
721
- // If the accessory doesn't exist in Govee then remove it. Also remove it
722
- // if it is now being ignored, or if it is a matter model and the user has
723
- // enabled ignoreMatter (otherwise the cached accessory lingers even though
724
- // the device is skipped during setup) (#1287).
725
- if (
726
- (!httpDevices.some(el => el.device === accessory.context.gvDeviceId)
727
- && !lanDevices.some(el => el.device === accessory.context.gvDeviceId)
728
- && !openApiDevices.some(el => el.device === accessory.context.gvDeviceId))
729
- || this.ignoredDevices.includes(accessory.context.gvDeviceId)
733
+ // Remove the accessory if it is now being ignored, or if it is a matter model
734
+ // and the user has enabled ignoreMatter (otherwise the cached accessory lingers
735
+ // even though the device is skipped during setup) (#1287)
736
+ const isIgnored = this.ignoredDevices.includes(accessory.context.gvDeviceId)
730
737
  || (this.config.ignoreMatter && platformConsts.matterModels.includes(accessory.context.gvModel))
731
- ) {
738
+
739
+ // Also remove it if the device no longer exists in Govee, but never based on
740
+ // an incomplete cloud list, and never for LAN-only devices which may simply
741
+ // have missed a scan window (#1264)
742
+ const isMissing = !httpDevices.some(el => el.device === accessory.context.gvDeviceId)
743
+ && !lanDevices.some(el => el.device === accessory.context.gvDeviceId)
744
+ && !openApiDevices.some(el => el.device === accessory.context.gvDeviceId)
745
+ const isLanOnly = accessory.context.hasLanControl && !accessory.context.firmware
746
+
747
+ if (isIgnored || (isMissing && cloudListsFetched && !isLanOnly)) {
732
748
  this.removeAccessory(accessory)
733
749
  }
734
750
  })
@@ -219,6 +219,7 @@ export default {
219
219
  'H6097',
220
220
  'H6098',
221
221
  'H6099',
222
+ 'H609D',
222
223
  'H60A0',
223
224
  'H60A1',
224
225
  'H60A4',
@@ -542,6 +543,7 @@ export default {
542
543
  kettle: ['H7170', 'H7171', 'H7173', 'H7175', 'H717A'],
543
544
  template: [
544
545
  'H1162', // https://github.com/homebridge-plugins/homebridge-govee/issues/422
546
+ 'H1370', // https://github.com/homebridge-plugins/homebridge-govee/issues/1307
545
547
  'H1167', // https://github.com/homebridge-plugins/homebridge-govee/issues/1142
546
548
  'H5010', // https://github.com/homebridge-plugins/homebridge-govee/issues/1235
547
549
  'H5024', // https://github.com/homebridge-plugins/homebridge-govee/issues/835
@@ -14,12 +14,14 @@ const modelOverrides = {
14
14
  H6102: { bleColorCmd: [COLOR_SUB.RGB_ALT], bleBrightnessScale: 0x64 },
15
15
  H613B: { bleColorCmd: [COLOR_SUB.RGB_ALT], bleBrightnessScale: 0x64 },
16
16
  H613D: { bleColorCmd: [COLOR_SUB.RGB_ALT], bleBrightnessScale: 0x64 },
17
- H617E: { bleColorCmd: [COLOR_SUB.RGB_ALT], bleBrightnessScale: 0x64 },
18
17
 
19
18
  // BLE color sub-command: 0x15 0x01 (extended) with trailing bytes
20
19
  H6053: { bleColorCmd: COLOR_SUB.RGB_EXTENDED, bleColorCmdSuffix: [0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F] },
21
20
  H6072: { bleColorCmd: COLOR_SUB.RGB_EXTENDED, bleColorCmdSuffix: [0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F] },
22
21
  H6199: { bleColorCmd: COLOR_SUB.RGB_EXTENDED, bleColorCmdSuffix: [0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F] },
22
+ // H617E is an RGBIC strip which ignores the 0x0D command - it needs the
23
+ // segment-based extended format, as seen in its own TTR rule data (#1290)
24
+ H617E: { bleColorCmd: COLOR_SUB.RGB_EXTENDED, bleColorCmdSuffix: [0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F], bleBrightnessScale: 0x64 },
23
25
 
24
26
  // AWS outlet uses 17/16 for on/off instead of 1/0
25
27
  H5080: { awsPowerOn: 17, awsPowerOff: 16 },
@@ -68,6 +68,7 @@ export default {
68
68
  curWatt: 'current wattage',
69
69
  devAdd: 'has been added to Homebridge',
70
70
  devInit: 'initialised with id',
71
+ devIsGroup: 'is a Govee app device group so will not be added to Homebridge',
71
72
  devInitOpts: 'initialising with options',
72
73
  devMaySupp: 'is not currently supported, do create a Github issue with the following info',
73
74
  devNoBlePackage: 'will not be added as BLE is disabled in the config',
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.25.0",
5
+ "version": "11.26.0",
6
6
  "description": "Homebridge plugin to integrate Govee devices into HomeKit.",
7
7
  "author": {
8
8
  "name": "bwp91",