@homebridge-plugins/homebridge-govee 11.35.0 → 11.35.1-beta.1
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 +8 -0
- package/lib/device/kettle.js +108 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@homebridge-plugins/homebridge-govee` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v11.35.1 (Pending Release)
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- fix: stop reading an h7173 startup frame as a kettle preset temperature (#1351)
|
|
10
|
+
- fix: call the kettle's first temperature slot the target, not preset 1 (#1337)
|
|
11
|
+
- feat: show how long the kettle will keep the water warm for (#1337)
|
|
12
|
+
|
|
5
13
|
## v11.35.0 (2026-08-09)
|
|
6
14
|
|
|
7
15
|
### Changed
|
package/lib/device/kettle.js
CHANGED
|
@@ -75,6 +75,18 @@ function readingToCelsius(hexParts) {
|
|
|
75
75
|
* Only these four have been seen, all with keep warm switched on. Anything else
|
|
76
76
|
* falls through to the unknown-data report, which is how these were found.
|
|
77
77
|
*/
|
|
78
|
+
// The range a kettle preset can plausibly sit in. The lowest Govee kettles go
|
|
79
|
+
// is around 40°C for delicate teas, and water stops being water at 100°C - so
|
|
80
|
+
// 90°F to 212°F covers every real preset with room to spare, while excluding
|
|
81
|
+
// the 0 and 1 the H7173 puts in that byte (#1351).
|
|
82
|
+
const KETTLE_PRESET_MIN_F = 90
|
|
83
|
+
const KETTLE_PRESET_MAX_F = 212
|
|
84
|
+
|
|
85
|
+
// The app offers a keep warm of 30 minutes to 2 hours. A day is far past
|
|
86
|
+
// anything a kettle would offer, while leaving room for a model that allows
|
|
87
|
+
// more than the H7171 does.
|
|
88
|
+
const KETTLE_KEEP_WARM_MAX_MINS = 1440
|
|
89
|
+
|
|
78
90
|
const HEATING_STATES = {
|
|
79
91
|
'00': { label: 'idle', heating: false },
|
|
80
92
|
'01': { label: 'heating', heating: true },
|
|
@@ -364,6 +376,55 @@ export default class extends GoveeDevice {
|
|
|
364
376
|
this.accessory.log(`current status [${state.label}]`)
|
|
365
377
|
}
|
|
366
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Show how long the kettle will hold the water once it has boiled.
|
|
381
|
+
*
|
|
382
|
+
* An `aa 22 01` frame carries the keep warm duration twice over, as two
|
|
383
|
+
* sixteen-bit counts of minutes. The owner in #1337 proved that outright:
|
|
384
|
+
* with the app set to two hours the frame read `aa 22 01 00 78 00 78`, and
|
|
385
|
+
* seconds after they changed it to thirty minutes the kettle sent
|
|
386
|
+
* `aa 22 01 00 1e 00 1e`. 0x78 is 120 and 0x1e is 30.
|
|
387
|
+
*
|
|
388
|
+
* The frame's checksum did not move between those two, which fits: the two
|
|
389
|
+
* copies are equal, so whatever one contributes the other takes back out.
|
|
390
|
+
*
|
|
391
|
+
* What the second copy is for is still unknown - a remaining time alongside
|
|
392
|
+
* the configured one would explain it, but it has only ever been seen equal
|
|
393
|
+
* to the first. So a pair that disagrees is treated as a frame this plugin
|
|
394
|
+
* cannot read rather than quietly ignored, which is what would get it
|
|
395
|
+
* decoded.
|
|
396
|
+
*
|
|
397
|
+
* @param {string} hexString the whole frame, for the log if it cannot be read
|
|
398
|
+
* @param {string[]} hexParts the frame split into bytes
|
|
399
|
+
*/
|
|
400
|
+
updateKeepWarmDuration(hexString, hexParts) {
|
|
401
|
+
const configured = hexToDecimal(`${getTwoItemPosition(hexParts, 4)}${getTwoItemPosition(hexParts, 5)}`)
|
|
402
|
+
const second = hexToDecimal(`${getTwoItemPosition(hexParts, 6)}${getTwoItemPosition(hexParts, 7)}`)
|
|
403
|
+
|
|
404
|
+
// A keep warm that runs for no time at all, or for more than a day, is not
|
|
405
|
+
// a duration - it means this model packs the frame differently
|
|
406
|
+
const plausible = Number.isFinite(configured)
|
|
407
|
+
&& configured > 0
|
|
408
|
+
&& configured <= KETTLE_KEEP_WARM_MAX_MINS
|
|
409
|
+
|
|
410
|
+
if (!plausible || configured !== second) {
|
|
411
|
+
logUnknownData(this.accessory, {
|
|
412
|
+
kind: 'status',
|
|
413
|
+
source: 'AWS',
|
|
414
|
+
raw: hexString,
|
|
415
|
+
hex: hexParts.join(''),
|
|
416
|
+
extra: { note: 'aa 22 01 frame that does not carry one keep warm duration twice' },
|
|
417
|
+
})
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (this.cacheKeepWarmMins === configured) {
|
|
422
|
+
return
|
|
423
|
+
}
|
|
424
|
+
this.cacheKeepWarmMins = configured
|
|
425
|
+
this.accessory.log(`keep warm is set to ${configured} minutes`)
|
|
426
|
+
}
|
|
427
|
+
|
|
367
428
|
externalUpdate(params) {
|
|
368
429
|
// Handle OpenAPI workMode
|
|
369
430
|
if (params.workMode) {
|
|
@@ -383,15 +444,52 @@ export default class extends GoveeDevice {
|
|
|
383
444
|
|
|
384
445
|
const deviceFunction = `${getTwoItemPosition(hexParts, 2)}${getTwoItemPosition(hexParts, 3)}`
|
|
385
446
|
|
|
386
|
-
// On startup the kettle lists the temperature each of its
|
|
387
|
-
//
|
|
388
|
-
// thing from aa 05 00 <mode> below, which is a mode
|
|
389
|
-
// byte is what tells them apart. Nothing here needs the
|
|
390
|
-
// are logged for reference rather than reported as
|
|
447
|
+
// On startup the kettle lists the temperature held in each of its
|
|
448
|
+
// temperature slots, one frame per slot: aa 05 <slot> <degrees F>. That
|
|
449
|
+
// is a different thing from aa 05 00 <mode> below, which is a mode
|
|
450
|
+
// change - the slot byte is what tells them apart. Nothing here needs the
|
|
451
|
+
// values, so they are logged for reference rather than reported as
|
|
452
|
+
// unrecognised (#1337)
|
|
453
|
+
//
|
|
454
|
+
// Slot 01 is NOT a memory preset, it is the target the kettle is set to
|
|
455
|
+
// right now. Two runs from the owner in #1337 show it moving on its own:
|
|
456
|
+
// 212°F on 3 Aug, then 142°F on both 8 and 11 Aug, matching the `setTem`
|
|
457
|
+
// of 14200 carried in the same message, while slots 02-05 never budged.
|
|
458
|
+
// The memory presets are therefore slots 02-05, which is why the number
|
|
459
|
+
// printed below is one less than the slot.
|
|
460
|
+
//
|
|
461
|
+
// Those four are also not in the same order as the mode commands at the
|
|
462
|
+
// top of this file. The owner's app lists M1-M4 as black tea 212, green
|
|
463
|
+
// tea 180, oolong 195, coffee 205, which is the order slots 02-05 arrive
|
|
464
|
+
// in - whereas the switch commands number green tea 02, oolong 03, coffee
|
|
465
|
+
// 04 and black tea 05. So the slots follow the app's list rather than the
|
|
466
|
+
// mode ids, and no attempt is made here to name them.
|
|
391
467
|
if (getTwoItemPosition(hexParts, 2) === '05' && getTwoItemPosition(hexParts, 3) !== '00') {
|
|
392
468
|
const slot = Number.parseInt(getTwoItemPosition(hexParts, 3), 16)
|
|
393
469
|
const tempInF = Number.parseInt(getTwoItemPosition(hexParts, 4), 16)
|
|
394
|
-
|
|
470
|
+
|
|
471
|
+
// ⚠️ The `aa 05 <slot> <degrees F>` reading came from ONE kettle, the
|
|
472
|
+
// H7171 in #1337. The H7173 sends the same two leading bytes with a
|
|
473
|
+
// fourth byte of 0 or 1, which is plainly not a temperature - and the
|
|
474
|
+
// plugin cheerfully announced "preset 1 is set to 0°F [-18°C]" (#1351).
|
|
475
|
+
//
|
|
476
|
+
// So the shape alone is not enough to claim a meaning. A preset a
|
|
477
|
+
// kettle could actually hold is the test; anything else is a frame this
|
|
478
|
+
// plugin cannot read yet, and reporting it as such is what gets it
|
|
479
|
+
// decoded rather than guessed at.
|
|
480
|
+
if (tempInF < KETTLE_PRESET_MIN_F || tempInF > KETTLE_PRESET_MAX_F) {
|
|
481
|
+
logUnknownData(this.accessory, {
|
|
482
|
+
kind: 'status',
|
|
483
|
+
source: 'AWS',
|
|
484
|
+
raw: hexString,
|
|
485
|
+
hex: hexParts.join(''),
|
|
486
|
+
extra: { note: 'aa 05 frame whose fourth byte is not a preset temperature' },
|
|
487
|
+
})
|
|
488
|
+
return
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const label = slot === 1 ? 'target temperature' : `memory preset ${slot - 1}`
|
|
492
|
+
const line = `${label} is set to ${tempInF}°F [${Math.round(((tempInF - 32) * 5) / 9)}°C]`
|
|
395
493
|
// Worth seeing in a normal log, but only once each. The kettle sends
|
|
396
494
|
// these when it connects, and if a firmware ever repeats them on a
|
|
397
495
|
// timer this keeps five lines from becoming five every few minutes
|
|
@@ -433,8 +531,11 @@ export default class extends GoveeDevice {
|
|
|
433
531
|
}
|
|
434
532
|
break
|
|
435
533
|
}
|
|
534
|
+
case '2201': { // keep warm on, and for how long
|
|
535
|
+
this.updateKeepWarmDuration(hexString, hexParts)
|
|
536
|
+
break
|
|
537
|
+
}
|
|
436
538
|
case '2200': // keep warm off
|
|
437
|
-
case '2201': // keep warm on
|
|
438
539
|
case '2300': // scheduled start off
|
|
439
540
|
case '2301': { // scheduled start on
|
|
440
541
|
break
|
package/package.json
CHANGED