@homebridge-plugins/homebridge-govee 11.34.2-beta.7 → 11.34.2-beta.8

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
@@ -21,6 +21,8 @@ All notable changes to `@homebridge-plugins/homebridge-govee` will be documented
21
21
  - fix: reject a refresh time so large it would make the plugin poll every millisecond
22
22
  - fix(H5054): don't let a slow leak warning block other sensors (#1348)
23
23
  - fix: mark sensor batteries low at the configured threshold
24
+ - feat: show whether the kettle is heating, keeping warm or idle (#1337)
25
+ - fix: give the H5106 a battery service, so its low battery setting does something
24
26
 
25
27
  ## v11.34.1 (2026-08-06)
26
28
 
@@ -28,12 +28,16 @@ import GoveeDevice from './base.js'
28
28
  /**
29
29
  * The water temperature out of a status frame, in celsius.
30
30
  *
31
- * The frame carries it as two bytes of hundredths of a degree fahrenheit. That
32
- * layout has never been confirmed against a kettle sitting at a known
33
- * temperature, so the result is checked before it is used: a kettle holds
34
- * water, and a figure outside freezing to a little past boiling is not a
35
- * temperature at all - it means this model packs the frame differently.
36
- * Returning nothing then leaves the tile absent rather than wrong.
31
+ * The frame carries it as two bytes of hundredths of a degree fahrenheit. An
32
+ * owner confirmed that layout against three known points on an H7171 (#1337):
33
+ * cold water read 0x1edc = 79.00F, the moment the kettle beeped at its 142F
34
+ * target it read 0x3778 = 142.00F, and it drifted to 0x39d0 = 148.00F after.
35
+ *
36
+ * The result is still checked before it is used, because only the one model has
37
+ * been seen: a kettle holds water, and a figure outside freezing to a little
38
+ * past boiling is not a temperature at all - it means that model packs the
39
+ * frame differently. Returning nothing then leaves the tile absent rather than
40
+ * wrong.
37
41
  *
38
42
  * @param {string[]} hexParts the frame split into bytes
39
43
  * @returns {number|undefined} the reading in celsius, or undefined if the
@@ -51,6 +55,33 @@ function readingToCelsius(hexParts) {
51
55
  return farToCen(fahrenheit)
52
56
  }
53
57
 
58
+ /**
59
+ * What the kettle says it is doing, from the state byte of an `aa 19` frame.
60
+ *
61
+ * Worked out from a full run an owner instrumented for us (#1337), matching the
62
+ * frames against a running commentary of what the kettle was doing:
63
+ *
64
+ * 00 idle two seconds after they switched it off
65
+ * 01 heating the moment they turned it on, and a second after setting a
66
+ * temperature on an earlier run
67
+ * 04 reached as the water hit the target and the kettle beeped
68
+ * 02 warming held for the next three and a half minutes of keep warm
69
+ *
70
+ * `04` and `02` arrive in the same second, so which of the pair marks reaching
71
+ * the target and which marks entering keep warm is read from their order, not
72
+ * proven separately. Both leave the kettle no longer heating, which is what the
73
+ * sensor reflects, so the distinction only affects the wording in the log.
74
+ *
75
+ * Only these four have been seen, all with keep warm switched on. Anything else
76
+ * falls through to the unknown-data report, which is how these were found.
77
+ */
78
+ const HEATING_STATES = {
79
+ '00': { label: 'idle', heating: false },
80
+ '01': { label: 'heating', heating: true },
81
+ '02': { label: 'keeping warm', heating: false },
82
+ '04': { label: 'reached target', heating: false },
83
+ }
84
+
54
85
  export default class extends GoveeDevice {
55
86
  constructor(platform, accessory) {
56
87
  super(platform, accessory)
@@ -294,6 +325,45 @@ export default class extends GoveeDevice {
294
325
  }
295
326
  }
296
327
 
328
+ /**
329
+ * Reflect what the kettle says it is doing.
330
+ *
331
+ * The tile is added the first time a state frame arrives rather than up
332
+ * front, for the same reason as the temperature tile above: only the H7171
333
+ * has been seen sending these, and a tile that never updates cannot be told
334
+ * apart from a real reading.
335
+ *
336
+ * @param {string} stateByte the state byte of an `aa 19` frame
337
+ */
338
+ updateHeatingState(stateByte) {
339
+ const state = HEATING_STATES[stateByte]
340
+ if (!state) {
341
+ return
342
+ }
343
+
344
+ if (!this.serviceHeating) {
345
+ this.serviceHeating = this.accessory.getService('Heating')
346
+ if (!this.serviceHeating) {
347
+ this.serviceHeating = this.accessory.addService(this.hapServ.ContactSensor, 'Heating', 'heating')
348
+ this.serviceHeating.addCharacteristic(this.hapChar.ConfiguredName)
349
+ this.serviceHeating.updateCharacteristic(this.hapChar.ConfiguredName, 'Heating')
350
+ }
351
+ }
352
+
353
+ if (this.cacheHeatingState === state.label) {
354
+ return
355
+ }
356
+ this.cacheHeatingState = state.label
357
+
358
+ this.serviceHeating.updateCharacteristic(
359
+ this.hapChar.ContactSensorState,
360
+ state.heating
361
+ ? this.hapChar.ContactSensorState.CONTACT_DETECTED
362
+ : this.hapChar.ContactSensorState.CONTACT_NOT_DETECTED,
363
+ )
364
+ this.accessory.log(`current status [${state.label}]`)
365
+ }
366
+
297
367
  externalUpdate(params) {
298
368
  // Handle OpenAPI workMode
299
369
  if (params.workMode) {
@@ -369,13 +439,23 @@ export default class extends GoveeDevice {
369
439
  case '2301': { // scheduled start on
370
440
  break
371
441
  }
442
+ // What the kettle is doing - see HEATING_STATES above. `1900` used to be
443
+ // treated as a startup frame, which it only looked like because a kettle
444
+ // is usually idle when it connects; it is really the idle state and is
445
+ // sent whenever the kettle reaches it.
446
+ case '1900':
447
+ case '1901':
448
+ case '1902':
449
+ case '1904': {
450
+ this.updateHeatingState(getTwoItemPosition(hexParts, 3))
451
+ break
452
+ }
372
453
  // Also sent on startup, alongside the preset list above. What each one
373
454
  // carries has not been worked out, and nothing here needs them, so they
374
455
  // are known-and-ignored rather than reported every time (#1337)
375
456
  case '1701': // a second form of the on-base report
376
457
  case '1f06':
377
- case '1f08':
378
- case '1900': {
458
+ case '1f08': {
379
459
  this.accessory.logDebug(`startup status [${deviceFunction}]: ${hexString}`)
380
460
  break
381
461
  }
@@ -28,6 +28,15 @@ export default class extends GoveeDevice {
28
28
  this.cacheTemp = this.tempService.getCharacteristic(this.hapChar.CurrentTemperature).value
29
29
  this.updateCache()
30
30
 
31
+ // Add the battery service if it doesn't already exist. The device reports a
32
+ // battery in the HTTP device list the same way the thermo sensors do, and
33
+ // this handler already took a lowBattThreshold from the config - it just had
34
+ // nowhere to put the reading, so the setting did nothing and the level never
35
+ // reached HomeKit
36
+ this.battService = this.accessory.getService(this.hapServ.Battery)
37
+ || this.accessory.addService(this.hapServ.Battery)
38
+ this.cacheBatt = this.battService.getCharacteristic(this.hapChar.BatteryLevel).value
39
+
31
40
  // Add the humidity service if it doesn't already exist
32
41
  this.humiService = this.accessory.getService(this.hapServ.HumiditySensor)
33
42
  || this.accessory.addService(this.hapServ.HumiditySensor)
@@ -59,6 +68,8 @@ export default class extends GoveeDevice {
59
68
  // where the AWS channel never delivers for this model. The fields use the
60
69
  // same semantics as the thermo-hygrometer sensors: hundredths of a degree
61
70
  // and hundredths of a percent
71
+ this.updateBattery(params.battery)
72
+
62
73
  if (hasProperty(params, 'temperature')) {
63
74
  let newTemp = Number.parseInt(params.temperature + this.accessory.context.offTemp, 10)
64
75
  newTemp /= 100
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.34.2-beta.7",
5
+ "version": "11.34.2-beta.8",
6
6
  "description": "Homebridge plugin to integrate Govee devices into HomeKit.",
7
7
  "author": {
8
8
  "name": "bwp91",