homebridge-mynest 1.0.0 → 2.0.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 +18 -2
- package/README.md +24 -23
- package/SECURITY.md +10 -6
- package/config.schema.json +16 -4
- package/dist/accessories/base.d.ts +8 -2
- package/dist/accessories/base.js +41 -5
- package/dist/accessories/global-eco.d.ts +8 -1
- package/dist/accessories/global-eco.js +53 -14
- package/dist/accessories/protect.d.ts +0 -1
- package/dist/accessories/protect.js +52 -27
- package/dist/accessories/temperature-sensor.d.ts +0 -1
- package/dist/accessories/temperature-sensor.js +1 -8
- package/dist/accessories/thermostat.d.ts +0 -1
- package/dist/accessories/thermostat.js +20 -17
- package/dist/api/batch-update.d.ts +0 -1
- package/dist/api/batch-update.js +7 -4
- package/dist/api/circuit-breaker.d.ts +0 -1
- package/dist/api/circuit-breaker.js +0 -1
- package/dist/api/framing.d.ts +0 -1
- package/dist/api/framing.js +84 -5
- package/dist/api/http.d.ts +2 -3
- package/dist/api/http.js +86 -6
- package/dist/api/observe.d.ts +1 -1
- package/dist/api/observe.js +86 -13
- package/dist/api/protobuf.d.ts +17 -2
- package/dist/api/protobuf.js +74 -26
- package/dist/api/rest.d.ts +25 -4
- package/dist/api/rest.js +78 -13
- package/dist/api/session.d.ts +0 -1
- package/dist/api/session.js +34 -11
- package/dist/api/thermostat-write.d.ts +10 -5
- package/dist/api/thermostat-write.js +47 -14
- package/dist/api/transport.d.ts +18 -1
- package/dist/api/transport.js +367 -44
- package/dist/diagnostics/collector.d.ts +0 -1
- package/dist/diagnostics/collector.js +6 -1
- package/dist/diagnostics/format.d.ts +0 -1
- package/dist/diagnostics/format.js +1 -4
- package/dist/diagnostics/types.d.ts +3 -2
- package/dist/diagnostics/types.js +0 -1
- package/dist/errors/index.d.ts +2 -3
- package/dist/errors/index.js +21 -14
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/platform.d.ts +0 -1
- package/dist/platform.js +250 -50
- package/dist/settings.d.ts +195 -7
- package/dist/settings.js +203 -8
- package/dist/state/classify.d.ts +0 -1
- package/dist/state/classify.js +20 -3
- package/dist/state/observe-state.d.ts +17 -1
- package/dist/state/observe-state.js +53 -7
- package/dist/state/protect-state.d.ts +10 -5
- package/dist/state/protect-state.js +10 -5
- package/dist/state/registry.d.ts +0 -1
- package/dist/state/registry.js +43 -13
- package/dist/state/sensor-state.d.ts +0 -1
- package/dist/state/sensor-state.js +7 -2
- package/dist/state/thermostat-state.d.ts +0 -1
- package/dist/state/thermostat-state.js +20 -13
- package/dist/state/traits.d.ts +0 -10
- package/dist/state/traits.js +5 -2
- package/dist/types/config.d.ts +8 -2
- package/dist/types/config.js +0 -1
- package/dist/types/device.d.ts +0 -1
- package/dist/types/device.js +0 -1
- package/dist/types/nest.d.ts +33 -4
- package/dist/types/nest.js +0 -1
- package/dist/utils/bound-characteristics.d.ts +7 -2
- package/dist/utils/bound-characteristics.js +31 -3
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +0 -1
- package/dist/utils/retry.d.ts +13 -3
- package/dist/utils/retry.js +32 -6
- package/dist/utils/sanitizers.d.ts +0 -1
- package/dist/utils/sanitizers.js +23 -3
- package/dist/utils/validators.d.ts +0 -1
- package/dist/utils/validators.js +5 -10
- package/package.json +20 -30
- package/.env.example +0 -20
- package/CONTRIBUTING.md +0 -40
- package/DEVELOPMENT.md +0 -73
- package/RELEASING.md +0 -55
- package/dist/accessories/base.d.ts.map +0 -1
- package/dist/accessories/base.js.map +0 -1
- package/dist/accessories/global-eco.d.ts.map +0 -1
- package/dist/accessories/global-eco.js.map +0 -1
- package/dist/accessories/protect.d.ts.map +0 -1
- package/dist/accessories/protect.js.map +0 -1
- package/dist/accessories/temperature-sensor.d.ts.map +0 -1
- package/dist/accessories/temperature-sensor.js.map +0 -1
- package/dist/accessories/thermostat.d.ts.map +0 -1
- package/dist/accessories/thermostat.js.map +0 -1
- package/dist/api/batch-update.d.ts.map +0 -1
- package/dist/api/batch-update.js.map +0 -1
- package/dist/api/circuit-breaker.d.ts.map +0 -1
- package/dist/api/circuit-breaker.js.map +0 -1
- package/dist/api/framing.d.ts.map +0 -1
- package/dist/api/framing.js.map +0 -1
- package/dist/api/http.d.ts.map +0 -1
- package/dist/api/http.js.map +0 -1
- package/dist/api/observe.d.ts.map +0 -1
- package/dist/api/observe.js.map +0 -1
- package/dist/api/protobuf.d.ts.map +0 -1
- package/dist/api/protobuf.js.map +0 -1
- package/dist/api/rest.d.ts.map +0 -1
- package/dist/api/rest.js.map +0 -1
- package/dist/api/session.d.ts.map +0 -1
- package/dist/api/session.js.map +0 -1
- package/dist/api/thermostat-write.d.ts.map +0 -1
- package/dist/api/thermostat-write.js.map +0 -1
- package/dist/api/transport.d.ts.map +0 -1
- package/dist/api/transport.js.map +0 -1
- package/dist/diagnostics/collector.d.ts.map +0 -1
- package/dist/diagnostics/collector.js.map +0 -1
- package/dist/diagnostics/format.d.ts.map +0 -1
- package/dist/diagnostics/format.js.map +0 -1
- package/dist/diagnostics/types.d.ts.map +0 -1
- package/dist/diagnostics/types.js.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/platform.d.ts.map +0 -1
- package/dist/platform.js.map +0 -1
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js.map +0 -1
- package/dist/state/classify.d.ts.map +0 -1
- package/dist/state/classify.js.map +0 -1
- package/dist/state/observe-state.d.ts.map +0 -1
- package/dist/state/observe-state.js.map +0 -1
- package/dist/state/protect-state.d.ts.map +0 -1
- package/dist/state/protect-state.js.map +0 -1
- package/dist/state/registry.d.ts.map +0 -1
- package/dist/state/registry.js.map +0 -1
- package/dist/state/sensor-state.d.ts.map +0 -1
- package/dist/state/sensor-state.js.map +0 -1
- package/dist/state/thermostat-state.d.ts.map +0 -1
- package/dist/state/thermostat-state.js.map +0 -1
- package/dist/state/traits.d.ts.map +0 -1
- package/dist/state/traits.js.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js.map +0 -1
- package/dist/types/device.d.ts.map +0 -1
- package/dist/types/device.js.map +0 -1
- package/dist/types/nest.d.ts.map +0 -1
- package/dist/types/nest.js.map +0 -1
- package/dist/utils/bound-characteristics.d.ts.map +0 -1
- package/dist/utils/bound-characteristics.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/retry.js.map +0 -1
- package/dist/utils/sanitizers.d.ts.map +0 -1
- package/dist/utils/sanitizers.js.map +0 -1
- package/dist/utils/validators.d.ts.map +0 -1
- package/dist/utils/validators.js.map +0 -1
- package/docs/AUTH.md +0 -26
- package/docs/PROTOCOL.md +0 -97
- package/scripts/verify.mjs +0 -222
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here. From the first release onward, [release-please](https://github.com/googleapis/release-please) owns this file — do not hand-edit version headings after `0.1.0` ships.
|
|
4
4
|
|
|
5
|
+
## [2.0.0](https://github.com/tbaur/homebridge-mynest/compare/v1.1.0...v2.0.0) (2026-08-05)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
* Homebridge 1.x is no longer supported. Requires Homebridge 2.x and Node.js 22 or newer, which is what Homebridge 2 itself requires.
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* harden for Homebridge 2 — breaking release to 2.0.0 ([#29](https://github.com/tbaur/homebridge-mynest/issues/29)) ([02a2b7d](https://github.com/tbaur/homebridge-mynest/commit/02a2b7de0da39db36a4884f010534ab9a095daa5))
|
|
15
|
+
|
|
16
|
+
## [1.1.0](https://github.com/tbaur/homebridge-mynest/compare/v1.0.0...v1.1.0) (2026-08-01)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* allow diagnostics interval up to 24h as a number field ([#27](https://github.com/tbaur/homebridge-mynest/issues/27)) ([9f988f7](https://github.com/tbaur/homebridge-mynest/commit/9f988f7d2cc21c5e6e072ce1914b0d767aa0168e))
|
|
22
|
+
|
|
5
23
|
## [1.0.0](https://github.com/tbaur/homebridge-mynest/compare/v0.1.10...v1.0.0) (2026-07-31)
|
|
6
24
|
|
|
7
25
|
|
|
@@ -79,8 +97,6 @@ All notable changes to this project are documented here. From the first release
|
|
|
79
97
|
|
|
80
98
|
* show Ignored Device IDs input in Homebridge UI ([#3](https://github.com/tbaur/homebridge-mynest/issues/3)) ([0e7d966](https://github.com/tbaur/homebridge-mynest/commit/0e7d9668ae1e9dc01da5fd5e04c8aceeae361fe6))
|
|
81
99
|
|
|
82
|
-
## [Unreleased]
|
|
83
|
-
|
|
84
100
|
## 0.1.0
|
|
85
101
|
|
|
86
102
|
Initial public release of **homebridge-mynest**.
|
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
[](https://github.com/tbaur/homebridge-mynest/actions/workflows/test.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/homebridge-mynest)
|
|
5
5
|
[](https://www.npmjs.com/package/homebridge-mynest)
|
|
6
|
-
[](https://nodejs.org)
|
|
7
|
+
[](https://homebridge.io)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
10
10
|
Expose Nest thermostats, Nest Protect smoke/CO alarms, and Nest Temperature Sensors in Apple HomeKit through Homebridge, using a **Nest Account** access token only.
|
|
@@ -33,10 +33,10 @@ Expose Nest thermostats, Nest Protect smoke/CO alarms, and Nest Temperature Sens
|
|
|
33
33
|
### Quality
|
|
34
34
|
|
|
35
35
|
<!-- Canonical test count lives here only; keep other docs number-free to avoid multi-place updates. -->
|
|
36
|
-
- **Homebridge 2
|
|
37
|
-
- **
|
|
38
|
-
- **Strict TypeScript** — `strict` mode with unused locals/params and no implicit returns
|
|
39
|
-
- **Secret hygiene** — Access tokens are redacted from logs
|
|
36
|
+
- **Built for Homebridge 2** — Live updates use stored getters + `updateValue(...)`, not the removed `getValue()` or stale `.value` reads
|
|
37
|
+
- **576 tests** — Jest suite with a per-area coverage floor on every source directory (statements, branches, functions, and lines), and a CI step that fails if the suite leaks an open handle
|
|
38
|
+
- **Strict TypeScript** — `strict` mode with unused locals/params and no implicit returns, plus type-aware linting (no floating promises)
|
|
39
|
+
- **Secret hygiene** — Access tokens are redacted from logs, and untrusted Nest responses cannot reach `Object.prototype`
|
|
40
40
|
- **No analytics** — Zero tracking or data collection
|
|
41
41
|
|
|
42
42
|
## Quick Start
|
|
@@ -53,7 +53,7 @@ npm install -g homebridge-mynest
|
|
|
53
53
|
|
|
54
54
|
### 2. Get a Nest Account token
|
|
55
55
|
|
|
56
|
-
You need a Nest Account (not Google-only) access token from [home.nest.com/session](https://home.nest.com/session). Steps and threat model: [docs/AUTH.md](docs/AUTH.md).
|
|
56
|
+
You need a Nest Account (not Google-only) access token from [home.nest.com/session](https://home.nest.com/session). Steps and threat model: [docs/AUTH.md](https://github.com/tbaur/homebridge-mynest/blob/main/docs/AUTH.md).
|
|
57
57
|
|
|
58
58
|
Prefer treating that token like a password; Nest Account sessions are account-scoped credentials.
|
|
59
59
|
|
|
@@ -75,7 +75,7 @@ Use the Homebridge UI, or add the platform to `config.json`:
|
|
|
75
75
|
|
|
76
76
|
### 4. Restart Homebridge
|
|
77
77
|
|
|
78
|
-
Thermostats, Protects, and temperature sensors appear in the Home app as Nest reports them. The log should show `Connected to Nest`, then device adds, then `Platform ready`.
|
|
78
|
+
Thermostats, Protects, and temperature sensors appear in the Home app as Nest reports them. The log should show `Connected to Nest (REST up; Observe connecting)`, then device adds, then `Platform ready`.
|
|
79
79
|
|
|
80
80
|
Thermostat control (mode, setpoints, Eco) stays off until you enable **Allow thermostat control**. Optionally enable **Expose global Eco switch** for a house-wide Nest Eco Mode tile.
|
|
81
81
|
|
|
@@ -87,7 +87,7 @@ Thermostat control (mode, setpoints, Eco) stays off until you enable **Allow the
|
|
|
87
87
|
| Nest Protect | Smoke + CO (+ optional occupancy / temp) | Smoke/CO require REST `topaz`; occupancy is ~10-minute presence |
|
|
88
88
|
| Temperature Sensor | Temperature Sensor | Battery + temperature |
|
|
89
89
|
|
|
90
|
-
Cameras, doorbells, locks,
|
|
90
|
+
Cameras, doorbells, Yale locks, Home/Away structure switches, and Google-account-only homes are out of scope.
|
|
91
91
|
|
|
92
92
|
## Configuration Options
|
|
93
93
|
|
|
@@ -96,14 +96,14 @@ Cameras, doorbells, locks, and Home/Away structure switches are out of scope.
|
|
|
96
96
|
| Option | Default | Description |
|
|
97
97
|
| --- | --- | --- |
|
|
98
98
|
| `name` | `MyNest` | Required. Plugin instance name shown in Homebridge logs. |
|
|
99
|
-
| `accessToken` | — | Required. Nest Account `access_token` from [docs/AUTH.md](docs/AUTH.md). |
|
|
99
|
+
| `accessToken` | — | Required. Nest Account `access_token` from [docs/AUTH.md](https://github.com/tbaur/homebridge-mynest/blob/main/docs/AUTH.md). |
|
|
100
100
|
| `allowThermostatControl` | `false` | Opt in to send mode/setpoint/Eco changes to Nest via BatchUpdateState. |
|
|
101
101
|
| `exposeGlobalEcoSwitch` | `false` | Publish a Nest Eco Mode switch that sets Eco on every thermostat. Writes require `allowThermostatControl`. |
|
|
102
102
|
| `exposeProtectOccupancy` | `true` | Occupancy from REST `auto_away` when Nest computes it on a mains-powered Protect. |
|
|
103
103
|
| `exposeProtectTemperature` | `false` | Temperature/humidity measured by each Protect. |
|
|
104
104
|
| `ignoredDeviceIds` | `[]` | Device IDs or serials to leave out of HomeKit. |
|
|
105
105
|
| `fieldTest` | `false` | Use Nest field-test hosts. |
|
|
106
|
-
| `diagnosticsInterval` | `0` | Seconds between health heartbeats in the log; `0` off, else `30`–`
|
|
106
|
+
| `diagnosticsInterval` | `0` | Seconds between health heartbeats in the log; `0` off, else `30`–`86400` (24h). |
|
|
107
107
|
| `structuredLogs` | `false` | When diagnostics are enabled, also emit a machine-readable JSON line alongside each human summary. |
|
|
108
108
|
| `debug` | `false` | Verbose logging; tokens are redacted. |
|
|
109
109
|
|
|
@@ -113,9 +113,9 @@ Nest does not expose a reliable Protect motion event stream to third-party clien
|
|
|
113
113
|
|
|
114
114
|
## Not Working?
|
|
115
115
|
|
|
116
|
-
1. **Authentication error** — Token missing, truncated, Google JWT/`ya29.`, or revoked. Capture a fresh Nest Account token ([docs/AUTH.md](docs/AUTH.md)).
|
|
117
|
-
2. **Thermostat missing** — Modern thermostats are Observe-only on some accounts. With default logging the plugin warns within about a minute if Observe produced no frames; set `debug: true` for stream detail
|
|
118
|
-
3. **Thermostat in Settings but no room tile** — Remove and re-add the
|
|
116
|
+
1. **Authentication error** — Token missing, truncated, Google JWT/`ya29.`, or revoked. Capture a fresh Nest Account token ([docs/AUTH.md](https://github.com/tbaur/homebridge-mynest/blob/main/docs/AUTH.md)).
|
|
117
|
+
2. **Thermostat missing** — Modern thermostats are Observe-only on some accounts. With default logging the plugin warns within about a minute if Observe produced no frames, and again every five minutes if a connected stream goes quiet; set `debug: true` for stream detail. From a git checkout you can also run `npm run verify` against the live account — see [DEVELOPMENT.md](https://github.com/tbaur/homebridge-mynest/blob/main/DEVELOPMENT.md).
|
|
118
|
+
3. **Thermostat in Settings but no room tile** — Remove and re-add the **MyNest** child bridge in the Home app (or whatever you set as **Name**), then assign rooms again. Same-UUID republish does not clear Apple Home's stuck presentation.
|
|
119
119
|
4. **Eco / setpoint changes snap back** — Enable **Allow thermostat control**. With control off, HomeKit can still move the UI (required for tiles) but Nest ignores the write and the plugin reverts.
|
|
120
120
|
5. **Protect without smoke/CO** — Likely Observe-only (missing from REST). The accessory still appears; alarm tiles wait for REST.
|
|
121
121
|
6. **No occupancy** — Battery Protect, power unknown, Observe-only Protect, or `exposeProtectOccupancy` off.
|
|
@@ -124,24 +124,25 @@ Nest does not expose a reliable Protect motion event stream to third-party clien
|
|
|
124
124
|
|
|
125
125
|
This plugin holds a Nest Account `access_token` in Homebridge's plaintext `config.json`. That token is account-scoped Nest credentials — anyone who can read the file can act as the Nest web app for the home. Secure the host, prefer rotating the token if it may have leaked, and never paste tokens into issues or logs.
|
|
126
126
|
|
|
127
|
-
Details: [SECURITY.md](SECURITY.md) and [docs/AUTH.md](docs/AUTH.md).
|
|
127
|
+
Details: [SECURITY.md](https://github.com/tbaur/homebridge-mynest/blob/main/SECURITY.md) and [docs/AUTH.md](https://github.com/tbaur/homebridge-mynest/blob/main/docs/AUTH.md).
|
|
128
128
|
|
|
129
129
|
Nest publishes no consumer API and can change or revoke sessions without notice. This plugin uses Nest Account tokens only and rejects Google cookie / `ya29.` shapes on purpose.
|
|
130
130
|
|
|
131
131
|
## Requirements
|
|
132
132
|
|
|
133
|
-
-
|
|
134
|
-
-
|
|
133
|
+
- **Homebridge 2.x** — Homebridge 1.x is not supported
|
|
134
|
+
- **Node.js 22 or 24** — this mirrors Homebridge 2's own `engines` constraint, so newer Node versions are excluded until Homebridge supports them; CI tests both
|
|
135
135
|
- A Nest Account (not Google-only) with an `access_token` from [home.nest.com/session](https://home.nest.com/session)
|
|
136
136
|
|
|
137
137
|
## More Info
|
|
138
138
|
|
|
139
|
-
- [Authentication](docs/AUTH.md) — capturing the Nest Account token
|
|
140
|
-
- [Protocol notes](docs/PROTOCOL.md) — reverse-engineered Nest behaviour
|
|
141
|
-
- [Development](DEVELOPMENT.md) — architecture and local setup
|
|
142
|
-
- [Contributing](CONTRIBUTING.md)
|
|
143
|
-
- [
|
|
144
|
-
- [
|
|
139
|
+
- [Authentication](https://github.com/tbaur/homebridge-mynest/blob/main/docs/AUTH.md) — capturing the Nest Account token
|
|
140
|
+
- [Protocol notes](https://github.com/tbaur/homebridge-mynest/blob/main/docs/PROTOCOL.md) — reverse-engineered Nest behaviour
|
|
141
|
+
- [Development](https://github.com/tbaur/homebridge-mynest/blob/main/DEVELOPMENT.md) — architecture and local setup
|
|
142
|
+
- [Contributing](https://github.com/tbaur/homebridge-mynest/blob/main/CONTRIBUTING.md)
|
|
143
|
+
- [Code of conduct](https://github.com/tbaur/homebridge-mynest/blob/main/CODE_OF_CONDUCT.md)
|
|
144
|
+
- [Security policy](https://github.com/tbaur/homebridge-mynest/blob/main/SECURITY.md)
|
|
145
|
+
- [Changelog](https://github.com/tbaur/homebridge-mynest/blob/main/CHANGELOG.md)
|
|
145
146
|
- [Report issues](https://github.com/tbaur/homebridge-mynest/issues)
|
|
146
147
|
|
|
147
148
|
## License
|
package/SECURITY.md
CHANGED
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported Versions
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Only the latest published release is supported. Fixes are shipped forward rather than backported, so the answer to "is my version supported?" is always "upgrade to the newest release".
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
6
8
|
| ------- | ----------------- |
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
+
| Latest release | ✅ Active support |
|
|
10
|
+
| Anything older | ❌ Unsupported — upgrade |
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
The next release requires **Homebridge 2.x and Node.js 22 or newer**; Homebridge 1.x is no longer supported.
|
|
11
13
|
|
|
12
14
|
## Reporting a Vulnerability
|
|
13
15
|
|
|
14
16
|
If you discover a security vulnerability, please report it responsibly:
|
|
15
17
|
|
|
16
18
|
1. **Do NOT open a public issue**
|
|
17
|
-
2.
|
|
19
|
+
2. Use GitHub's [private vulnerability reporting](https://github.com/tbaur/homebridge-mynest/security/advisories/new)
|
|
18
20
|
3. Include:
|
|
19
21
|
- Description of the vulnerability
|
|
20
22
|
- Steps to reproduce
|
|
@@ -47,4 +49,6 @@ This plugin implements:
|
|
|
47
49
|
|
|
48
50
|
## Scope limits
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
Out-of-scope devices are listed in the [README](README.md#supported-devices): cameras, doorbells, Yale locks, Home/Away structure switches, and Google-account-only homes.
|
|
53
|
+
|
|
54
|
+
Life-safety alarm state for Protects is only published while Nest REST can refresh it; Observe-only units and Protects whose REST feed is down do not keep a frozen all-clear.
|
package/config.schema.json
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"title": "Allow thermostat control",
|
|
29
29
|
"type": "boolean",
|
|
30
30
|
"default": false,
|
|
31
|
-
"description": "Opt in to let HomeKit change thermostat mode, setpoints, and Eco via Nest BatchUpdateState. Off by default so upgrades stay read-only until you enable control.
|
|
31
|
+
"description": "Opt in to let HomeKit change thermostat mode, setpoints, and Eco via Nest BatchUpdateState. Off by default so upgrades stay read-only until you enable control. Nest publishes no supported write API, so leave this off unless you want HomeKit driving your HVAC."
|
|
32
32
|
},
|
|
33
33
|
"exposeGlobalEcoSwitch": {
|
|
34
34
|
"title": "Expose global Eco switch",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"ignoredDeviceIds": {
|
|
52
52
|
"title": "Ignored device IDs",
|
|
53
53
|
"type": "array",
|
|
54
|
+
"default": [],
|
|
54
55
|
"required": false,
|
|
55
56
|
"description": "Nest device IDs or serial numbers to leave out of HomeKit. IDs appear in the Homebridge log when devices are added.",
|
|
56
57
|
"items": {
|
|
@@ -70,8 +71,11 @@
|
|
|
70
71
|
"type": "integer",
|
|
71
72
|
"default": 0,
|
|
72
73
|
"minimum": 0,
|
|
73
|
-
"maximum":
|
|
74
|
-
"
|
|
74
|
+
"maximum": 86400,
|
|
75
|
+
"x-schema-form": {
|
|
76
|
+
"type": "number"
|
|
77
|
+
},
|
|
78
|
+
"description": "Seconds between health heartbeats in the Homebridge log. 0 disables (default). Allowed range 0–86400 (24h); values 1–29 are raised to 30."
|
|
75
79
|
},
|
|
76
80
|
"structuredLogs": {
|
|
77
81
|
"title": "Structured diagnostic logs",
|
|
@@ -116,7 +120,15 @@
|
|
|
116
120
|
"title": "Advanced",
|
|
117
121
|
"expandable": true,
|
|
118
122
|
"expanded": false,
|
|
119
|
-
"items": [
|
|
123
|
+
"items": [
|
|
124
|
+
"fieldTest",
|
|
125
|
+
{
|
|
126
|
+
"key": "diagnosticsInterval",
|
|
127
|
+
"type": "number"
|
|
128
|
+
},
|
|
129
|
+
"structuredLogs",
|
|
130
|
+
"debug"
|
|
131
|
+
]
|
|
120
132
|
}
|
|
121
133
|
]
|
|
122
134
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* working on Homebridge 2 — see `utils/bound-characteristics.ts` for why the
|
|
13
13
|
* obvious alternatives silently stop updating.
|
|
14
14
|
*/
|
|
15
|
-
import type { PlatformAccessory, Service } from 'homebridge';
|
|
15
|
+
import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
|
|
16
16
|
import type { DeviceIdentity, DeviceKind, NestDevice } from '../types/device';
|
|
17
17
|
import type { ResolvedConfig } from '../types/config';
|
|
18
18
|
import type { Logger } from '../utils/logger';
|
|
@@ -78,5 +78,11 @@ export declare abstract class NestAccessory<TState> {
|
|
|
78
78
|
* reporting whatever value it last held.
|
|
79
79
|
*/
|
|
80
80
|
protected removeService(type: Parameters<PlatformAccessory['getService']>[0]): void;
|
|
81
|
+
/**
|
|
82
|
+
* Map a low-battery verdict onto HomeKit's enum.
|
|
83
|
+
*
|
|
84
|
+
* `undefined` when Nest has said nothing, so the binder leaves the last known
|
|
85
|
+
* value in place rather than publishing "normal" on no evidence.
|
|
86
|
+
*/
|
|
87
|
+
protected toLowBatteryValue(isBatteryLow: boolean | undefined): CharacteristicValue | undefined;
|
|
81
88
|
}
|
|
82
|
-
//# sourceMappingURL=base.d.ts.map
|
package/dist/accessories/base.js
CHANGED
|
@@ -17,6 +17,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
exports.NestAccessory = void 0;
|
|
18
18
|
const settings_1 = require("../settings");
|
|
19
19
|
const bound_characteristics_1 = require("../utils/bound-characteristics");
|
|
20
|
+
/** Whether any field published as Accessory Information differs. */
|
|
21
|
+
function hasIdentityChanged(previous, next) {
|
|
22
|
+
return previous.name !== next.name
|
|
23
|
+
|| previous.model !== next.model
|
|
24
|
+
|| previous.serialNumber !== next.serialNumber
|
|
25
|
+
|| previous.firmwareVersion !== next.firmwareVersion
|
|
26
|
+
|| previous.id !== next.id;
|
|
27
|
+
}
|
|
20
28
|
/** One HomeKit accessory backed by one Nest device. */
|
|
21
29
|
class NestAccessory {
|
|
22
30
|
platform;
|
|
@@ -35,7 +43,7 @@ class NestAccessory {
|
|
|
35
43
|
this.config = platform.resolvedConfig;
|
|
36
44
|
this.identity = device.identity;
|
|
37
45
|
this.state = device.state;
|
|
38
|
-
this.binder = new bound_characteristics_1.CharacteristicBinder(log);
|
|
46
|
+
this.binder = new bound_characteristics_1.CharacteristicBinder(log, device.identity.name);
|
|
39
47
|
this.#applyAccessoryInformation();
|
|
40
48
|
}
|
|
41
49
|
get deviceId() {
|
|
@@ -49,9 +57,15 @@ class NestAccessory {
|
|
|
49
57
|
* unconditionally is cheaper than diffing here.
|
|
50
58
|
*/
|
|
51
59
|
update(device) {
|
|
60
|
+
const previousIdentity = this.identity;
|
|
52
61
|
this.identity = device.identity;
|
|
53
62
|
this.state = device.state;
|
|
54
|
-
|
|
63
|
+
// Only when it actually changed. These four values essentially never do, and
|
|
64
|
+
// re-applying them ran HAP's full value-validation path four times per
|
|
65
|
+
// device per refresh on the hot update path.
|
|
66
|
+
if (hasIdentityChanged(previousIdentity, device.identity)) {
|
|
67
|
+
this.#applyAccessoryInformation();
|
|
68
|
+
}
|
|
55
69
|
this.onServicesMayChange();
|
|
56
70
|
this.binder.refresh();
|
|
57
71
|
this.#logSummary();
|
|
@@ -93,6 +107,21 @@ class NestAccessory {
|
|
|
93
107
|
this.binder.unbindService(existing);
|
|
94
108
|
this.accessory.removeService(existing);
|
|
95
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Map a low-battery verdict onto HomeKit's enum.
|
|
112
|
+
*
|
|
113
|
+
* `undefined` when Nest has said nothing, so the binder leaves the last known
|
|
114
|
+
* value in place rather than publishing "normal" on no evidence.
|
|
115
|
+
*/
|
|
116
|
+
toLowBatteryValue(isBatteryLow) {
|
|
117
|
+
const { StatusLowBattery } = this.platform.Characteristic;
|
|
118
|
+
if (isBatteryLow === undefined) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
return isBatteryLow
|
|
122
|
+
? StatusLowBattery.BATTERY_LEVEL_LOW
|
|
123
|
+
: StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
124
|
+
}
|
|
96
125
|
#applyAccessoryInformation() {
|
|
97
126
|
const { Characteristic, Service: HapService } = this.platform;
|
|
98
127
|
const service = this.accessory.getService(HapService.AccessoryInformation)
|
|
@@ -106,10 +135,18 @@ class NestAccessory {
|
|
|
106
135
|
service.setCharacteristic(Characteristic.FirmwareRevision, this.identity.firmwareVersion);
|
|
107
136
|
}
|
|
108
137
|
}
|
|
109
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Info on a change, debug otherwise, so the log follows the house.
|
|
140
|
+
*
|
|
141
|
+
* The first summary is also info: it is the only default-visible confirmation
|
|
142
|
+
* that a newly published device is actually reporting data. The "Added …"
|
|
143
|
+
* line carries no readings, so folding the first summary into the debug
|
|
144
|
+
* branch left an operator unable to tell a live device from a silent one.
|
|
145
|
+
*/
|
|
110
146
|
#logSummary() {
|
|
111
147
|
const summary = this.describeState();
|
|
112
|
-
|
|
148
|
+
const isFirst = this.#lastSummary === null;
|
|
149
|
+
if (!isFirst && this.#lastSummary === summary) {
|
|
113
150
|
this.log.debug(`${this.identity.name}: ${summary}`);
|
|
114
151
|
}
|
|
115
152
|
else {
|
|
@@ -119,4 +156,3 @@ class NestAccessory {
|
|
|
119
156
|
}
|
|
120
157
|
}
|
|
121
158
|
exports.NestAccessory = NestAccessory;
|
|
122
|
-
//# sourceMappingURL=base.js.map
|
|
@@ -24,5 +24,12 @@ export declare class GlobalEcoAccessory {
|
|
|
24
24
|
* matches within {@link PENDING_ECO_MAX_MS}, take Nest truth.
|
|
25
25
|
*/
|
|
26
26
|
updateAllEco(allEco: boolean): void;
|
|
27
|
+
/**
|
|
28
|
+
* Release the pending-write backstop.
|
|
29
|
+
*
|
|
30
|
+
* Called when the switch is removed or the platform stops. The timer is
|
|
31
|
+
* `unref`'d so it cannot hold the process open, but without this it could
|
|
32
|
+
* still fire up to 45s later and refresh a torn-down accessory.
|
|
33
|
+
*/
|
|
34
|
+
dispose(): void;
|
|
27
35
|
}
|
|
28
|
-
//# sourceMappingURL=global-eco.d.ts.map
|
|
@@ -15,7 +15,6 @@ exports.GlobalEcoAccessory = void 0;
|
|
|
15
15
|
const settings_1 = require("../settings");
|
|
16
16
|
const bound_characteristics_1 = require("../utils/bound-characteristics");
|
|
17
17
|
const sanitizers_1 = require("../utils/sanitizers");
|
|
18
|
-
const DISPLAY_NAME = 'Nest Eco Mode';
|
|
19
18
|
/**
|
|
20
19
|
* How long a successful HomeKit Eco write may ignore Nest aggregates that have
|
|
21
20
|
* not yet matched. After this, Nest truth wins (Nest app / failed Observe).
|
|
@@ -35,11 +34,19 @@ class GlobalEcoAccessory {
|
|
|
35
34
|
*/
|
|
36
35
|
#pendingAllEco = null;
|
|
37
36
|
#pendingSinceMs = null;
|
|
37
|
+
/**
|
|
38
|
+
* Backstop for {@link PENDING_ECO_MAX_MS}.
|
|
39
|
+
*
|
|
40
|
+
* The age check in {@link updateAllEco} only runs when Nest sends another
|
|
41
|
+
* update. If Observe dies right after a successful write, nothing would ever
|
|
42
|
+
* clear the optimistic value and the switch would hold it indefinitely.
|
|
43
|
+
*/
|
|
44
|
+
#pendingTimer = null;
|
|
38
45
|
constructor(platform, accessory, log) {
|
|
39
46
|
this.#platform = platform;
|
|
40
47
|
this.#accessory = accessory;
|
|
41
48
|
this.#log = log;
|
|
42
|
-
this.#binder = new bound_characteristics_1.CharacteristicBinder(log);
|
|
49
|
+
this.#binder = new bound_characteristics_1.CharacteristicBinder(log, settings_1.GLOBAL_ECO_DISPLAY_NAME);
|
|
43
50
|
this.#applyAccessoryInformation();
|
|
44
51
|
this.#bindSwitch();
|
|
45
52
|
this.#binder.refresh();
|
|
@@ -58,8 +65,7 @@ class GlobalEcoAccessory {
|
|
|
58
65
|
}
|
|
59
66
|
else if (this.#pendingSinceMs !== null
|
|
60
67
|
&& Date.now() - this.#pendingSinceMs >= PENDING_ECO_MAX_MS) {
|
|
61
|
-
this.#
|
|
62
|
-
this.#clearPending();
|
|
68
|
+
this.#expirePending();
|
|
63
69
|
}
|
|
64
70
|
else {
|
|
65
71
|
return;
|
|
@@ -68,15 +74,50 @@ class GlobalEcoAccessory {
|
|
|
68
74
|
this.#allEco = allEco;
|
|
69
75
|
this.#binder.refresh();
|
|
70
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Release the pending-write backstop.
|
|
79
|
+
*
|
|
80
|
+
* Called when the switch is removed or the platform stops. The timer is
|
|
81
|
+
* `unref`'d so it cannot hold the process open, but without this it could
|
|
82
|
+
* still fire up to 45s later and refresh a torn-down accessory.
|
|
83
|
+
*/
|
|
84
|
+
dispose() {
|
|
85
|
+
this.#clearPending();
|
|
86
|
+
}
|
|
71
87
|
#clearPending() {
|
|
72
88
|
this.#pendingAllEco = null;
|
|
73
89
|
this.#pendingSinceMs = null;
|
|
90
|
+
if (this.#pendingTimer) {
|
|
91
|
+
clearTimeout(this.#pendingTimer);
|
|
92
|
+
this.#pendingTimer = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Give up on an unconfirmed write and let Nest's own value stand. */
|
|
96
|
+
#expirePending() {
|
|
97
|
+
this.#log.warn(`${settings_1.GLOBAL_ECO_DISPLAY_NAME}: Nest did not confirm Eco change within ${PENDING_ECO_MAX_MS / 1000}s — following Nest`);
|
|
98
|
+
this.#clearPending();
|
|
99
|
+
}
|
|
100
|
+
/** Start the backstop that gives Nest the last word even if it goes quiet. */
|
|
101
|
+
#armPending(ecoOn) {
|
|
102
|
+
this.#clearPending();
|
|
103
|
+
this.#pendingAllEco = ecoOn;
|
|
104
|
+
this.#pendingSinceMs = Date.now();
|
|
105
|
+
this.#pendingTimer = setTimeout(() => {
|
|
106
|
+
this.#pendingTimer = null;
|
|
107
|
+
if (this.#pendingAllEco === null) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.#expirePending();
|
|
111
|
+
this.#binder.refresh();
|
|
112
|
+
}, PENDING_ECO_MAX_MS);
|
|
113
|
+
// Homebridge owns the process lifetime; this must not hold Node open.
|
|
114
|
+
this.#pendingTimer.unref?.();
|
|
74
115
|
}
|
|
75
116
|
#bindSwitch() {
|
|
76
117
|
const { Characteristic, Service: HapService } = this.#platform;
|
|
77
118
|
const service = this.#accessory.getService(HapService.Switch)
|
|
78
|
-
?? this.#accessory.addService(HapService.Switch,
|
|
79
|
-
service.setCharacteristic(Characteristic.Name,
|
|
119
|
+
?? this.#accessory.addService(HapService.Switch, settings_1.GLOBAL_ECO_DISPLAY_NAME);
|
|
120
|
+
service.setCharacteristic(Characteristic.Name, settings_1.GLOBAL_ECO_DISPLAY_NAME);
|
|
80
121
|
this.#accessory
|
|
81
122
|
.setPrimaryService?.(service);
|
|
82
123
|
this.#binder.bind(service, Characteristic.On, () => this.#allEco, {
|
|
@@ -91,22 +132,21 @@ class GlobalEcoAccessory {
|
|
|
91
132
|
if (!sent) {
|
|
92
133
|
this.#clearPending();
|
|
93
134
|
if (!this.#platform.resolvedConfig.allowThermostatControl) {
|
|
94
|
-
this.#log.warn(`${
|
|
135
|
+
this.#log.warn(`${settings_1.GLOBAL_ECO_DISPLAY_NAME}: ignoring HomeKit change — enable Allow thermostat control in config.`);
|
|
95
136
|
}
|
|
96
137
|
// HAP assigns On after onSet; revert to Nest-derived `#allEco`.
|
|
97
138
|
setImmediate(() => this.#binder.refresh());
|
|
98
139
|
return;
|
|
99
140
|
}
|
|
100
|
-
this.#log.info(`${
|
|
141
|
+
this.#log.info(`${settings_1.GLOBAL_ECO_DISPLAY_NAME}: ${ecoOn ? 'Updating all thermostats to Eco' : 'Clearing Eco on all thermostats'}`);
|
|
101
142
|
// Optimistic until Nest's all-Eco aggregate matches; do not refresh here
|
|
102
143
|
// or HAP's post-onSet value is overwritten by a stale read.
|
|
103
|
-
this.#
|
|
104
|
-
this.#pendingSinceMs = Date.now();
|
|
144
|
+
this.#armPending(ecoOn);
|
|
105
145
|
this.#allEco = ecoOn;
|
|
106
146
|
}
|
|
107
147
|
catch (error) {
|
|
108
148
|
this.#clearPending();
|
|
109
|
-
this.#log.warn(`${
|
|
149
|
+
this.#log.warn(`${settings_1.GLOBAL_ECO_DISPLAY_NAME}: Eco update failed: ${(0, sanitizers_1.sanitizeError)(error)}`);
|
|
110
150
|
setImmediate(() => this.#binder.refresh());
|
|
111
151
|
}
|
|
112
152
|
}
|
|
@@ -116,10 +156,9 @@ class GlobalEcoAccessory {
|
|
|
116
156
|
?? this.#accessory.addService(HapService.AccessoryInformation);
|
|
117
157
|
service
|
|
118
158
|
.setCharacteristic(Characteristic.Manufacturer, settings_1.MANUFACTURER)
|
|
119
|
-
.setCharacteristic(Characteristic.Name,
|
|
159
|
+
.setCharacteristic(Characteristic.Name, settings_1.GLOBAL_ECO_DISPLAY_NAME)
|
|
120
160
|
.setCharacteristic(Characteristic.Model, 'Eco Mode')
|
|
121
|
-
.setCharacteristic(Characteristic.SerialNumber,
|
|
161
|
+
.setCharacteristic(Characteristic.SerialNumber, settings_1.GLOBAL_ECO_DEVICE_ID);
|
|
122
162
|
}
|
|
123
163
|
}
|
|
124
164
|
exports.GlobalEcoAccessory = GlobalEcoAccessory;
|
|
125
|
-
//# sourceMappingURL=global-eco.js.map
|
|
@@ -66,11 +66,18 @@ class ProtectAccessory extends base_1.NestAccessory {
|
|
|
66
66
|
* Nest has said anything would show a working all-clear on no evidence.
|
|
67
67
|
* Once created, services stay through REST outages (rooms/automations keep
|
|
68
68
|
* their targets) and are marked inactive/faulted while the feed is stale.
|
|
69
|
+
*
|
|
70
|
+
* Each sensor is gated on *its own* reading. Nest returns different topaz
|
|
71
|
+
* subsets per firmware, and a bucket carrying `smoke_status` without
|
|
72
|
+
* `co_status` must not produce a CO tile: its reader would return undefined,
|
|
73
|
+
* the binder would skip it, and HomeKit would show a CO detector resting at
|
|
74
|
+
* HAP's default "levels normal" with nothing behind it.
|
|
69
75
|
*/
|
|
70
76
|
#ensureAlarmServices() {
|
|
71
77
|
const { Characteristic, Service: HapService } = this.platform;
|
|
72
|
-
const
|
|
73
|
-
|
|
78
|
+
const hasSmoke = this.state.smoke !== undefined;
|
|
79
|
+
const hasCarbonMonoxide = this.state.carbonMonoxide !== undefined;
|
|
80
|
+
if (!hasSmoke && !hasCarbonMonoxide) {
|
|
74
81
|
this.removeService(HapService.SmokeSensor);
|
|
75
82
|
this.removeService(HapService.CarbonMonoxideSensor);
|
|
76
83
|
this.#smokeService = null;
|
|
@@ -92,25 +99,53 @@ class ProtectAccessory extends base_1.NestAccessory {
|
|
|
92
99
|
this.#didLogAlarmFeedStale = false;
|
|
93
100
|
this.log.info(`${this.identity.name}: REST alarm feed restored — Smoke/CO live again.`);
|
|
94
101
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
if (hasSmoke) {
|
|
103
|
+
this.#smokeService = this.resolveService(HapService.SmokeSensor);
|
|
104
|
+
this.#smokeService.setCharacteristic(Characteristic.Name, `${this.identity.name} Smoke`);
|
|
105
|
+
this.binder.bind(this.#smokeService, Characteristic.SmokeDetected, () => this.#toSmokeValue(this.state.smoke));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.removeService(HapService.SmokeSensor);
|
|
109
|
+
this.#smokeService = null;
|
|
110
|
+
}
|
|
111
|
+
if (hasCarbonMonoxide) {
|
|
112
|
+
this.#coService = this.resolveService(HapService.CarbonMonoxideSensor);
|
|
113
|
+
this.#coService.setCharacteristic(Characteristic.Name, `${this.identity.name} CO`);
|
|
114
|
+
this.binder.bind(this.#coService, Characteristic.CarbonMonoxideDetected, () => this.#toCarbonMonoxideValue(this.state.carbonMonoxide));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this.removeService(HapService.CarbonMonoxideSensor);
|
|
118
|
+
this.#coService = null;
|
|
119
|
+
}
|
|
120
|
+
// Whichever tiles exist carry the shared health characteristics, so the
|
|
121
|
+
// Home app shows a flat battery, an offline device, or a stale REST feed.
|
|
103
122
|
for (const service of [this.#smokeService, this.#coService]) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
? Characteristic.StatusFault.NO_FAULT
|
|
108
|
-
: Characteristic.StatusFault.GENERAL_FAULT);
|
|
123
|
+
if (service) {
|
|
124
|
+
this.#bindHealth(service);
|
|
125
|
+
}
|
|
109
126
|
}
|
|
110
127
|
// Alarm tiles carry battery; the standalone Battery service is only for
|
|
111
128
|
// Observe-only Protects that have no smoke/CO yet.
|
|
112
129
|
this.removeService(HapService.Battery);
|
|
113
130
|
}
|
|
131
|
+
/** Battery plus liveness, for the alarm tiles. */
|
|
132
|
+
#bindHealth(service) {
|
|
133
|
+
this.binder.bind(service, this.platform.Characteristic.StatusLowBattery, () => this.#toLowBatteryValue());
|
|
134
|
+
this.#bindLiveness(service);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Mark a service inactive and faulted whenever its reading is not live.
|
|
138
|
+
*
|
|
139
|
+
* Shared by the alarm tiles and the occupancy tile: all three are backed by
|
|
140
|
+
* the same REST feed, so they go stale together.
|
|
141
|
+
*/
|
|
142
|
+
#bindLiveness(service) {
|
|
143
|
+
const { Characteristic } = this.platform;
|
|
144
|
+
this.binder.bind(service, Characteristic.StatusActive, () => this.#isAlarmReadingLive());
|
|
145
|
+
this.binder.bind(service, Characteristic.StatusFault, () => this.#isAlarmReadingLive()
|
|
146
|
+
? Characteristic.StatusFault.NO_FAULT
|
|
147
|
+
: Characteristic.StatusFault.GENERAL_FAULT);
|
|
148
|
+
}
|
|
114
149
|
/** Alarm readings are live only while online and the REST feed is fresh. */
|
|
115
150
|
#isAlarmReadingLive() {
|
|
116
151
|
return this.state.isOnline !== false && this.state.isAlarmFeedStale !== true;
|
|
@@ -184,10 +219,7 @@ class ProtectAccessory extends base_1.NestAccessory {
|
|
|
184
219
|
: this.state.isOccupied
|
|
185
220
|
? Characteristic.OccupancyDetected.OCCUPANCY_DETECTED
|
|
186
221
|
: Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED);
|
|
187
|
-
this
|
|
188
|
-
this.binder.bind(service, Characteristic.StatusFault, () => this.#isAlarmReadingLive()
|
|
189
|
-
? Characteristic.StatusFault.NO_FAULT
|
|
190
|
-
: Characteristic.StatusFault.GENERAL_FAULT);
|
|
222
|
+
this.#bindLiveness(service);
|
|
191
223
|
if (!_a.#didLogOccupancyHint) {
|
|
192
224
|
_a.#didLogOccupancyHint = true;
|
|
193
225
|
this.log.info(`Protect occupancy is Nest ~${settings_1.PROTECT_OCCUPANCY_HOLD_OFF_SEC / 60}-min presence (auto_away), not motion.`);
|
|
@@ -251,13 +283,7 @@ class ProtectAccessory extends base_1.NestAccessory {
|
|
|
251
283
|
: Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL;
|
|
252
284
|
}
|
|
253
285
|
#toLowBatteryValue() {
|
|
254
|
-
|
|
255
|
-
if (this.state.isBatteryLow === undefined) {
|
|
256
|
-
return undefined;
|
|
257
|
-
}
|
|
258
|
-
return this.state.isBatteryLow
|
|
259
|
-
? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
|
|
260
|
-
: Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
286
|
+
return this.toLowBatteryValue(this.state.isBatteryLow);
|
|
261
287
|
}
|
|
262
288
|
/**
|
|
263
289
|
* HomeKit wants 0–100; Nest REST reports millivolts as `battery_level`.
|
|
@@ -293,4 +319,3 @@ class ProtectAccessory extends base_1.NestAccessory {
|
|
|
293
319
|
}
|
|
294
320
|
exports.ProtectAccessory = ProtectAccessory;
|
|
295
321
|
_a = ProtectAccessory;
|
|
296
|
-
//# sourceMappingURL=protect.js.map
|
|
@@ -36,13 +36,7 @@ class TemperatureSensorAccessory extends base_1.NestAccessory {
|
|
|
36
36
|
this.binder.bind(battery, Characteristic.StatusLowBattery, () => this.#toLowBatteryValue());
|
|
37
37
|
}
|
|
38
38
|
#toLowBatteryValue() {
|
|
39
|
-
|
|
40
|
-
if (this.state.isBatteryLow === undefined) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
return this.state.isBatteryLow
|
|
44
|
-
? Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
|
|
45
|
-
: Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
39
|
+
return this.toLowBatteryValue(this.state.isBatteryLow);
|
|
46
40
|
}
|
|
47
41
|
describeState() {
|
|
48
42
|
const parts = [];
|
|
@@ -59,4 +53,3 @@ class TemperatureSensorAccessory extends base_1.NestAccessory {
|
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
55
|
exports.TemperatureSensorAccessory = TemperatureSensorAccessory;
|
|
62
|
-
//# sourceMappingURL=temperature-sensor.js.map
|