@homebridge-plugins/homebridge-govee 11.32.2-beta.27 → 11.32.2-beta.29

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
@@ -44,7 +44,8 @@ All notable changes to `@homebridge-plugins/homebridge-govee` will be documented
44
44
  - feat: recognise eleven more meat thermometers, which report what they send so their readings can be worked out, and stop the one already known printing its whole update as a warning
45
45
  - feat: add the H5161 power strip and give the H7184 kitchen composter an on and off switch
46
46
  - feat: recognise three more sensors, which report what they send, and say plainly in the log why a sensor has no tile yet
47
- - feat: add ble encryption for the H3001 solar string lights, which silently ignore unencrypted frames (#1328)
47
+ - feat: add ble encryption for the H3001 solar string lights, which silently ignore unencrypted frames (#1328) (@dominick-han)
48
+ - fix: only mention an unrecognised bluetooth broadcast when the device is on your own account, since a scan also hears the neighbours
48
49
 
49
50
  ## v11.32.1 (2026-08-02)
50
51
 
@@ -124,17 +124,22 @@ export default class BLEConnection {
124
124
  try {
125
125
  const { uuid, address, rssi, advertisement } = peripheral
126
126
 
127
- // Skip if not a valid Govee sensor. Sensors whose broadcast the plugin
128
- // cannot decode yet are logged rather than dropped in silence, since the
129
- // hex is the one thing needed to add a model, and there is no other way
130
- // for an owner to get hold of it.
127
+ // A broadcast this plugin cannot decode is worth reporting rather than
128
+ // dropping in silence, since the hex is the one thing needed to add a
129
+ // model and an owner has no other way to get hold of it.
131
130
  if (!isValidPeripheral(peripheral)) {
132
131
  const unknownName = advertisement?.localName
133
132
  if (unknownName && unknownName.toLowerCase().startsWith('govee')) {
134
133
  const raw = advertisement.manufacturerData
135
134
  ? advertisement.manufacturerData.toString('hex')
136
135
  : 'no manufacturer data'
137
- const line = `[BLE] unrecognised govee sensor - please include this line if you report it: ${JSON.stringify({
136
+
137
+ // Bluetooth is a broadcast medium, so this scan hears every Govee
138
+ // device in range - including a neighbour's, which is not something
139
+ // the owner can do anything about. Only a device on this account is
140
+ // worth putting in front of them.
141
+ const isOurs = this.platform.hasBleDevice(address)
142
+ const line = `[BLE] unrecognised broadcast from a govee device - please include this line if you report it: ${JSON.stringify({
138
143
  name: unknownName,
139
144
  address,
140
145
  source: 'BLE',
@@ -143,8 +148,8 @@ export default class BLEConnection {
143
148
  })}`
144
149
 
145
150
  // Say it once so it is actually noticed, then stay quiet about this
146
- // sensor for the rest of the session - it rebroadcasts constantly.
147
- if (isFirstReport(`ble|${address}|${raw}`)) {
151
+ // device for the rest of the session - it rebroadcasts constantly.
152
+ if (isOurs && isFirstReport(`ble|${address}|${raw}`)) {
148
153
  this.log.warn(line)
149
154
  } else {
150
155
  this.log.debug(line)
@@ -319,7 +324,7 @@ export default class BLEConnection {
319
324
  * Unlike every other path in this client we need a reply, which arrives as a
320
325
  * notification on UUID.NOTIFY. Note that we deliberately do NOT subscribe:
321
326
  * these lights push notifications without the client ever writing the client
322
- * characteristic configuration descriptor, exactly as the official app does.
327
+ * characteristic configuration descriptor.
323
328
  * Calling subscribeAsync() here makes the descriptor write hang and the light
324
329
  * then drops the connection, so listening is both sufficient and required.
325
330
  *
@@ -9,7 +9,8 @@ import platformLang from '../utils/lang-en.js'
9
9
  import GoveeDevice from './base.js'
10
10
 
11
11
  // HomeKit triggers "CO2 detected" abnormal flag at this threshold (ppm).
12
- // Govee app default warn level is 1000 ppm; override via deviceConf.co2AbnormalThreshold.
12
+ // 1000 ppm is the usual warning level for these sensors; override it with
13
+ // deviceConf.co2AbnormalThreshold.
13
14
  const DEFAULT_CO2_ABNORMAL_PPM = 1000
14
15
 
15
16
  export default class extends GoveeDevice {
package/lib/platform.js CHANGED
@@ -1060,6 +1060,23 @@ export default class {
1060
1060
  }
1061
1061
  }
1062
1062
 
1063
+ /**
1064
+ * Whether a bluetooth address belongs to one of this account's devices.
1065
+ *
1066
+ * Bluetooth is a broadcast medium: a scan hears every Govee device in range,
1067
+ * including a neighbour's. Without this the plugin cannot tell the two apart,
1068
+ * and asking someone to report a device that is not theirs is noise.
1069
+ */
1070
+ hasBleDevice(address) {
1071
+ if (!address) {
1072
+ return false
1073
+ }
1074
+ const wanted = String(address).toLowerCase()
1075
+ return [...devicesInHB.values()].some(
1076
+ accessory => String(accessory.context.bleAddress || '').toLowerCase() === wanted,
1077
+ )
1078
+ }
1079
+
1063
1080
  configureAccessory(accessory) {
1064
1081
  // Set the correct firmware version if we can
1065
1082
  if (this.api && accessory.context.firmware) {
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Govee "V2" BLE encryption (EncryptionManagerV2 in the Govee Home app).
2
+ * The wrapped bluetooth transport that newer Govee devices require.
3
3
  *
4
- * Used by newer Bluetooth-only devices such as the H3001 solar string lights.
5
- * Older devices take the plain 20-byte frame; these wrap it in AES-128-GCM and
6
- * silently ignore anything that is not wrapped.
4
+ * Most devices take the plain 20-byte frame. These wrap it in AES-128-GCM and
5
+ * silently ignore anything that is not wrapped, which is why a light like the
6
+ * H3001 appears to accept every command and then does nothing at all.
7
7
  *
8
8
  * Wire format on 00010203-0405-0607-0809-0a0b0c0d2b11 (write) and ...2b10 (notify):
9
9
  *
@@ -31,12 +31,12 @@
31
31
  import { Buffer } from 'node:buffer'
32
32
  import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'
33
33
 
34
- // Application-wide constants, recovered from the Govee Home app's string
35
- // resources (LibTools KEY_COMMUNICATION_X / _Y).
34
+ // Fixed across every device that uses this transport, rather than being tied
35
+ // to an account or a pairing, so no lookup is needed to talk to one.
36
36
  const KEY_HANDSHAKE = Buffer.from('fc03783c7c42cb83e202a1643648aff6', 'hex')
37
37
  const KEY_DEVICE = Buffer.from('ae028b630bae6ecc4bff1b249e22f955', 'hex')
38
38
 
39
- const TAG_LEN = 12 // 96-bit tag; the app falls back to 128-bit
39
+ const TAG_LEN = 12 // 96-bit tag
40
40
  const HDR = { MAGIC0: 0xE7, MAGIC1: 0x11, SUB_REQUEST: 0x01, STATUS_OK: 0x00 }
41
41
 
42
42
  /** True if `data` is an e711 handshake frame rather than a data frame. */
@@ -74,8 +74,8 @@ export const BLE_STAGE_TIMEOUTS = {
74
74
  connect: 10000,
75
75
  write: 5000,
76
76
  // Encrypted models exchange a session key before the first real command (see
77
- // ble-crypto.js). Captures of the official app show the light replying in
78
- // about 30ms, so this is a generous ceiling rather than an expected cost.
77
+ // ble-crypto.js). A light answers in about 30ms, so this is a generous
78
+ // ceiling rather than an expected cost.
79
79
  // Models that need no handshake never spend any of it.
80
80
  handshake: 5000,
81
81
  settle: 100, // let the controller transmit before disconnecting
@@ -56,8 +56,8 @@ const modelOverrides = {
56
56
 
57
57
  // Bluetooth-only solar string lights (#1328). Every frame is wrapped in
58
58
  // AES-128-GCM, so this model needs the key handshake in ble-crypto.js before
59
- // any command is accepted. Its colour and brightness formats were read off a
60
- // capture of the official app:
59
+ // any command is accepted. Its colour and brightness formats, observed on
60
+ // the wire:
61
61
  // colour 33 05 15 01 RR GG BB 00 00 00 00 00 FF (then zero padding)
62
62
  // brightness 33 04 <0-100> e.g. 0x1e for 30%, 0x64 for 100%
63
63
  // Writes are unacknowledged, so without the wrapper the light silently
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.2-beta.27",
5
+ "version": "11.32.2-beta.29",
6
6
  "description": "Homebridge plugin to integrate Govee devices into HomeKit.",
7
7
  "author": {
8
8
  "name": "bwp91",