homebridge-bluos 1.1.1 → 1.1.2
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 +7 -0
- package/dist/platform.js +7 -4
- package/dist/utils/reboot-grace.d.ts +23 -3
- package/dist/utils/reboot-grace.js +34 -4
- package/docs/README-DETAILED.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.2](https://github.com/tbaur/homebridge-bluos/compare/v1.1.1...v1.1.2) (2026-08-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* do not end the reboot quiet window on a last-gasp poll ([#26](https://github.com/tbaur/homebridge-bluos/issues/26)) ([91651a4](https://github.com/tbaur/homebridge-bluos/commit/91651a4c4b114c8a8e7bfba0cc7c4d6148375b7d))
|
|
9
|
+
|
|
3
10
|
## [1.1.1](https://github.com/tbaur/homebridge-bluos/compare/v1.1.0...v1.1.1) (2026-08-30)
|
|
4
11
|
|
|
5
12
|
|
package/dist/platform.js
CHANGED
|
@@ -529,9 +529,10 @@ class BluOSPlatform {
|
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
publish(deviceId, observation, reason) {
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
|
|
532
|
+
// Ends the window only after the box has already gone quiet. A last-gasp
|
|
533
|
+
// reading while the ports are still up must not cancel it, or the real
|
|
534
|
+
// outage that follows is logged as a surprise.
|
|
535
|
+
this.rebootGrace.clearIfRecovered(this.hostOf(deviceId));
|
|
535
536
|
for (const handler of this.handlers.get(deviceId) ?? []) {
|
|
536
537
|
try {
|
|
537
538
|
handler.applyObservation(observation, reason);
|
|
@@ -542,7 +543,9 @@ class BluOSPlatform {
|
|
|
542
543
|
}
|
|
543
544
|
}
|
|
544
545
|
reportUnavailable(deviceId, error) {
|
|
545
|
-
|
|
546
|
+
const host = this.hostOf(deviceId);
|
|
547
|
+
if (this.rebootGrace.isExpected(host)) {
|
|
548
|
+
this.rebootGrace.noteSilence(host);
|
|
546
549
|
return;
|
|
547
550
|
}
|
|
548
551
|
for (const handler of this.handlers.get(deviceId) ?? []) {
|
|
@@ -10,20 +10,40 @@
|
|
|
10
10
|
* address would otherwise warn and later claim to have recovered. That is noise:
|
|
11
11
|
* we asked it to go down. This clock is what lets the platform stay quiet until
|
|
12
12
|
* the player answers again, or until the window ends and a real outage remains.
|
|
13
|
+
*
|
|
14
|
+
* A reading that arrives before the box actually drops is not recovery. The
|
|
15
|
+
* control ports often answer one more time after POST /reboot. That last gasp
|
|
16
|
+
* must not end the window, or the real outage that follows is logged as usual.
|
|
17
|
+
* Recovery is a successful poll after we have already seen the expected silence.
|
|
13
18
|
*/
|
|
14
19
|
/** Hosts we have just asked to reboot, and when that expectation expires. */
|
|
15
20
|
export declare class RebootGrace {
|
|
16
21
|
private readonly graceMs;
|
|
17
22
|
private readonly now;
|
|
18
23
|
private readonly expiresAt;
|
|
24
|
+
/** Addresses that have gone quiet at least once inside their window. */
|
|
25
|
+
private readonly sawSilence;
|
|
19
26
|
constructor(graceMs: number, now?: () => number);
|
|
20
27
|
/** Start (or refresh) the quiet window for this address. */
|
|
21
28
|
expect(host: string): void;
|
|
22
29
|
/**
|
|
23
|
-
*
|
|
30
|
+
* Record that this address failed while a reboot was still expected.
|
|
31
|
+
*
|
|
32
|
+
* The next successful poll is then treated as the player coming back, not as
|
|
33
|
+
* a last gasp from a box that has not gone down yet.
|
|
34
|
+
*/
|
|
35
|
+
noteSilence(host: string | undefined): void;
|
|
36
|
+
/**
|
|
37
|
+
* End the window if this address already went quiet and has now answered.
|
|
38
|
+
*
|
|
39
|
+
* A success with no prior silence is ignored: the box has not dropped yet.
|
|
40
|
+
*/
|
|
41
|
+
clearIfRecovered(host: string | undefined): void;
|
|
42
|
+
/**
|
|
43
|
+
* End the window now.
|
|
24
44
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
45
|
+
* Used when the clock runs out, and by tests. Recovery goes through
|
|
46
|
+
* {@link clearIfRecovered} so a last-gasp reading cannot cancel the window.
|
|
27
47
|
*/
|
|
28
48
|
clear(host: string | undefined): void;
|
|
29
49
|
/** True while a reboot of this address is still expected to look like an outage. */
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* address would otherwise warn and later claim to have recovered. That is noise:
|
|
12
12
|
* we asked it to go down. This clock is what lets the platform stay quiet until
|
|
13
13
|
* the player answers again, or until the window ends and a real outage remains.
|
|
14
|
+
*
|
|
15
|
+
* A reading that arrives before the box actually drops is not recovery. The
|
|
16
|
+
* control ports often answer one more time after POST /reboot. That last gasp
|
|
17
|
+
* must not end the window, or the real outage that follows is logged as usual.
|
|
18
|
+
* Recovery is a successful poll after we have already seen the expected silence.
|
|
14
19
|
*/
|
|
15
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
21
|
exports.RebootGrace = void 0;
|
|
@@ -19,6 +24,8 @@ class RebootGrace {
|
|
|
19
24
|
graceMs;
|
|
20
25
|
now;
|
|
21
26
|
expiresAt = new Map();
|
|
27
|
+
/** Addresses that have gone quiet at least once inside their window. */
|
|
28
|
+
sawSilence = new Set();
|
|
22
29
|
constructor(graceMs, now = Date.now) {
|
|
23
30
|
this.graceMs = graceMs;
|
|
24
31
|
this.now = now;
|
|
@@ -26,16 +33,39 @@ class RebootGrace {
|
|
|
26
33
|
/** Start (or refresh) the quiet window for this address. */
|
|
27
34
|
expect(host) {
|
|
28
35
|
this.expiresAt.set(host, this.now() + this.graceMs);
|
|
36
|
+
this.sawSilence.delete(host);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Record that this address failed while a reboot was still expected.
|
|
40
|
+
*
|
|
41
|
+
* The next successful poll is then treated as the player coming back, not as
|
|
42
|
+
* a last gasp from a box that has not gone down yet.
|
|
43
|
+
*/
|
|
44
|
+
noteSilence(host) {
|
|
45
|
+
if (host !== undefined && this.isExpected(host)) {
|
|
46
|
+
this.sawSilence.add(host);
|
|
47
|
+
}
|
|
29
48
|
}
|
|
30
49
|
/**
|
|
31
|
-
* End the window
|
|
50
|
+
* End the window if this address already went quiet and has now answered.
|
|
32
51
|
*
|
|
33
|
-
* A
|
|
34
|
-
|
|
52
|
+
* A success with no prior silence is ignored: the box has not dropped yet.
|
|
53
|
+
*/
|
|
54
|
+
clearIfRecovered(host) {
|
|
55
|
+
if (host !== undefined && this.sawSilence.has(host)) {
|
|
56
|
+
this.clear(host);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* End the window now.
|
|
61
|
+
*
|
|
62
|
+
* Used when the clock runs out, and by tests. Recovery goes through
|
|
63
|
+
* {@link clearIfRecovered} so a last-gasp reading cannot cancel the window.
|
|
35
64
|
*/
|
|
36
65
|
clear(host) {
|
|
37
66
|
if (host !== undefined) {
|
|
38
67
|
this.expiresAt.delete(host);
|
|
68
|
+
this.sawSilence.delete(host);
|
|
39
69
|
}
|
|
40
70
|
}
|
|
41
71
|
/** True while a reboot of this address is still expected to look like an outage. */
|
|
@@ -48,7 +78,7 @@ class RebootGrace {
|
|
|
48
78
|
return false;
|
|
49
79
|
}
|
|
50
80
|
if (this.now() >= expires) {
|
|
51
|
-
this.
|
|
81
|
+
this.clear(host);
|
|
52
82
|
return false;
|
|
53
83
|
}
|
|
54
84
|
return true;
|
package/docs/README-DETAILED.md
CHANGED
|
@@ -209,7 +209,7 @@ A per-player reboot switch on a shared chassis, at startup:
|
|
|
209
209
|
[BluOS] Zone One Reboot: will also reboot Zone Two: they are zones of one chassis, and BluOS reboots the whole box
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
-
After a reboot the other accessories stay quiet. The player is expected to stop answering, so they do not log `is not responding` or `is responding again`. The
|
|
212
|
+
After a reboot the other accessories stay quiet. The player is expected to stop answering, so they do not log `is not responding` or `is responding again`. A reading that arrives before the box actually drops is ignored for that purpose: the control ports often answer once more after the reboot is sent. The first successful poll after the silence writes whatever the player is actually doing into HomeKit. A player that is still silent after that window is logged as not responding, the usual way.
|
|
213
213
|
|
|
214
214
|
A configuration the plugin will not act on:
|
|
215
215
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-bluos",
|
|
3
3
|
"displayName": "Homebridge BluOS",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.2",
|
|
5
5
|
"description": "Homebridge plugin for BluOS players — per-zone volume, mute, volume-preset and battery accessories over the LAN Custom Integration API. Verified on NAD and Bluesound hardware",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|