homebridge-nest-accfactory 0.3.4 → 0.3.5
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 +17 -0
- package/README.md +29 -21
- package/dist/config.js +10 -10
- package/dist/plugins/thermostat.js +123 -27
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `homebridge-nest-accfactory` will be documented in this file. This project tries to adhere to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## v0.3.5 (2025/11/21)
|
|
6
|
+
|
|
7
|
+
- General code cleanup and stability improvements
|
|
8
|
+
- Fixed battery level reporting for Nest Thermostat (2020)
|
|
9
|
+
- Updated instructions for obtaining access token for Nest accounts
|
|
10
|
+
- Updated instructions for obtaining issue token and cookie for Google accounts
|
|
11
|
+
- Updated disclaimer in `README.md` to clarify support for official Homebridge installations only
|
|
12
|
+
- Added debug logging for thermostat mode and temperature changes received from outside of HomeKit [@MorelloCherry](https://github.com/MorelloCherry)
|
|
13
|
+
|
|
14
|
+
### Known Issues
|
|
15
|
+
|
|
16
|
+
- The ip npm package has a known security advisory [GHSA-2p57-rm9w-gvfp](https://github.com/advisories/GHSA-2p57-rm9w-gvfp); this is used indirectly via the werift library
|
|
17
|
+
|
|
5
18
|
## v0.3.4 (2025/10/17)
|
|
6
19
|
|
|
7
20
|
- General code cleanup and stability improvements
|
|
@@ -12,6 +25,10 @@ All notable changes to `homebridge-nest-accfactory` will be documented in this f
|
|
|
12
25
|
- Fixed periodic camera snapshots when camera is turned off
|
|
13
26
|
- Updated camera resource assets
|
|
14
27
|
|
|
28
|
+
### Known Issues
|
|
29
|
+
|
|
30
|
+
- The ip npm package has a known security advisory [GHSA-2p57-rm9w-gvfp](https://github.com/advisories/GHSA-2p57-rm9w-gvfp); this is used indirectly via the werift library
|
|
31
|
+
|
|
15
32
|
## v0.3.3 (2025/08/23)
|
|
16
33
|
|
|
17
34
|
- Refined timeout warnings for camera and doorbell snapshot capture
|
package/README.md
CHANGED
|
@@ -36,32 +36,35 @@ The accessory supports connection to Nest using a Nest account AND/OR a Google (
|
|
|
36
36
|
|
|
37
37
|
### Nest Account
|
|
38
38
|
|
|
39
|
-
If you have a Nest
|
|
39
|
+
If you have a **Nest Account**, you’ll need to obtain an **access token** from the Nest web app.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
1. Go to [home.nest.com](https://home.nest.com) and click **Sign in with Nest**.
|
|
42
|
+
2. After logging in, open [home.nest.com/session](https://home.nest.com/session).
|
|
43
|
+
3. You’ll see a JSON string similar to:
|
|
44
|
+
```json
|
|
45
|
+
{"2fa_state":"enrolled","access_token":"XXX", ...}
|
|
46
|
+
4. Copy the value of **access_token** near the start (a long string beginning with `b`) and paste it into your Homebridge configuration.
|
|
47
|
+
- Ignore any other `access_token` entries further down the string.
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
**Note:** Do **not** log out of [home.nest.com](https://home.nest.com), as this will invalidate your credentials.
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
### Obtaining a Google cookie token for a Google Account (Safari)
|
|
46
52
|
|
|
47
|
-
**
|
|
53
|
+
Google Accounts require an **"issueToken"** and **"cookie"**, which are unique to your account. You only need to do this once as long as you stay logged into your Google Account.
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
1. Open Safari in a **Private Window**
|
|
56
|
+
2. Enable the **Develop Menu** (Safari ▸ Settings ▸ Advanced ▸ check *Show Develop menu in menu bar*)
|
|
57
|
+
3. Open **Develop ▸ Show Web Inspector**, then select the **Network** tab
|
|
58
|
+
4. Ensure **Preserve Log** is checked
|
|
59
|
+
5. In the filter box, type **issueToken**
|
|
60
|
+
6. Go to [home.nest.com](https://home.nest.com) and click **Sign in with Google**
|
|
61
|
+
7. After login, click the **iframerpc** network request
|
|
62
|
+
8. In **Headers**, copy:
|
|
63
|
+
- **Request URL** - this is your **Issue Token**
|
|
64
|
+
- **cookie:** value under *Request Headers* - this is your **Cookie**
|
|
65
|
+
9. Enter both into your Homebridge configuration
|
|
50
66
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
1. Open a Chrome browser tab in Incognito Mode
|
|
54
|
-
2. Open Developer Tools (View/Developer/Developer Tools).
|
|
55
|
-
3. Click on 'Network' tab. Make sure 'Preserve Log' is checked.
|
|
56
|
-
4. In the 'Filter' box, enter issueToken
|
|
57
|
-
5. Go to home.nest.com, and click 'Sign in with Google'. Log into your account.
|
|
58
|
-
6. One network call (beginning with iframerpc) will appear in the Dev Tools window. Click on it.
|
|
59
|
-
7. In the Headers tab, under General, copy the entire Request URL (beginning with https://accounts.google.com). This is your "Issue Token" which can be entered into the plugin-configuration within Homebridge.
|
|
60
|
-
8. In the 'Filter' box, enter oauth2/iframe
|
|
61
|
-
9. Several network calls will appear in the Dev Tools window. Click on the last iframe call.
|
|
62
|
-
10. In the Headers tab, under Request Headers, copy the entire cookie (include the whole string which is several lines long and has many field/value pairs - do not include the cookie: name). This is your "Cookie" which can be entered into the plugin-configuration within Homebridge.
|
|
63
|
-
|
|
64
|
-
**Do not log out of home.nest.com, as this will invalidate your credentials. Just close the browser tab**
|
|
67
|
+
**Note:** Do **not** log out of [home.nest.com](https://home.nest.com), as this will invalidate your credentials.
|
|
65
68
|
|
|
66
69
|
## config.json configuration
|
|
67
70
|
|
|
@@ -154,4 +157,9 @@ By default, we look in /usr/local/bin for an ffmpeg binary, however, you can spe
|
|
|
154
157
|
|
|
155
158
|
This is a personal hobby project, provided "as-is," with no warranty whatsoever, express or implied, including but not limited to warranties of merchantability or fitness for a particular purpose. Building and running this project is done entirely at your own risk.
|
|
156
159
|
|
|
157
|
-
|
|
160
|
+
This plugin is only supported when used with **official Homebridge installations**.
|
|
161
|
+
Other platforms or forks such as **HOOBS**, **Home Assistant**, or similar derivatives are **not officially supported** and may not function as intended.
|
|
162
|
+
|
|
163
|
+
Please note that I am not affiliated with any companies, including but not limited to Google, Apple, or any other entities. The author of this project shall not be held liable for any damages or issues arising from its use.
|
|
164
|
+
|
|
165
|
+
If you encounter a problem with the source code, please [raise an issue](https://github.com/n0rt0nthec4t/homebridge-nest-accfactory/issues) on the project's **GitHub repository** so it can be reviewed and investigated.
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Configuration validation and processing
|
|
2
2
|
// Part of homebridge-nest-accfactory
|
|
3
3
|
//
|
|
4
|
-
// Code version 2025.
|
|
4
|
+
// Code version 2025.10.21
|
|
5
5
|
// Mark Hulskamp
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -27,7 +27,7 @@ function processConfig(config, log) {
|
|
|
27
27
|
? Number(config.options.elevation)
|
|
28
28
|
: 0;
|
|
29
29
|
|
|
30
|
-
// Controls what APIs we use, default is to use both Nest and
|
|
30
|
+
// Controls what APIs we use, default is to use both Nest and Google APIs
|
|
31
31
|
options.useNestAPI = config.options?.useNestAPI === true || config.options?.useNestAPI === undefined;
|
|
32
32
|
options.useGoogleAPI = config.options?.useGoogleAPI === true || config.options?.useGoogleAPI === undefined;
|
|
33
33
|
|
|
@@ -143,15 +143,15 @@ function buildConnections(config) {
|
|
|
143
143
|
Object.keys(config).forEach((key) => {
|
|
144
144
|
let section = config[key];
|
|
145
145
|
|
|
146
|
-
if (typeof section?.access_token === 'string' && section.access_token !== '') {
|
|
146
|
+
if (typeof section?.access_token === 'string' && section.access_token.trim() !== '') {
|
|
147
147
|
let fieldTest = section?.fieldTest === true;
|
|
148
148
|
connections[crypto.randomUUID()] = {
|
|
149
149
|
name: key,
|
|
150
150
|
type: ACCOUNT_TYPE.NEST,
|
|
151
151
|
authorised: false,
|
|
152
152
|
allowRetry: true,
|
|
153
|
-
access_token: section.access_token,
|
|
154
|
-
fieldTest,
|
|
153
|
+
access_token: section.access_token.trim(),
|
|
154
|
+
fieldTest: fieldTest,
|
|
155
155
|
referer: fieldTest ? 'home.ft.nest.com' : 'home.nest.com',
|
|
156
156
|
restAPIHost: fieldTest ? 'home.ft.nest.com' : 'home.nest.com',
|
|
157
157
|
cameraAPIHost: fieldTest ? 'camera.home.ft.nest.com' : 'camera.home.nest.com',
|
|
@@ -161,9 +161,9 @@ function buildConnections(config) {
|
|
|
161
161
|
|
|
162
162
|
if (
|
|
163
163
|
typeof section?.issuetoken === 'string' &&
|
|
164
|
-
section.issuetoken !== '' &&
|
|
164
|
+
section.issuetoken.trim() !== '' &&
|
|
165
165
|
typeof section?.cookie === 'string' &&
|
|
166
|
-
section.cookie !== ''
|
|
166
|
+
section.cookie.trim() !== ''
|
|
167
167
|
) {
|
|
168
168
|
let fieldTest = section?.fieldTest === true;
|
|
169
169
|
connections[crypto.randomUUID()] = {
|
|
@@ -171,9 +171,9 @@ function buildConnections(config) {
|
|
|
171
171
|
type: ACCOUNT_TYPE.GOOGLE,
|
|
172
172
|
authorised: false,
|
|
173
173
|
allowRetry: true,
|
|
174
|
-
issuetoken: section.issuetoken,
|
|
175
|
-
cookie: section.cookie,
|
|
176
|
-
fieldTest,
|
|
174
|
+
issuetoken: section.issuetoken.trim(),
|
|
175
|
+
cookie: section.cookie.trim(),
|
|
176
|
+
fieldTest: fieldTest,
|
|
177
177
|
referer: fieldTest ? 'home.ft.nest.com' : 'home.nest.com',
|
|
178
178
|
restAPIHost: fieldTest ? 'home.ft.nest.com' : 'home.nest.com',
|
|
179
179
|
cameraAPIHost: fieldTest ? 'camera.home.ft.nest.com' : 'camera.home.nest.com',
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
|
|
29
29
|
export default class NestThermostat extends HomeKitDevice {
|
|
30
30
|
static TYPE = 'Thermostat';
|
|
31
|
-
static VERSION = '2025.
|
|
31
|
+
static VERSION = '2025.11.20'; // Code version
|
|
32
32
|
|
|
33
33
|
thermostatService = undefined;
|
|
34
34
|
batteryService = undefined;
|
|
@@ -263,6 +263,7 @@ export default class NestThermostat extends HomeKitDevice {
|
|
|
263
263
|
this.accessory.removeService(this.humidityService);
|
|
264
264
|
this.accessory.removeService(this.fanService);
|
|
265
265
|
this.accessory.removeService(this.dehumidifierService);
|
|
266
|
+
this.accessory.removeService(this.humidifierService);
|
|
266
267
|
this.thermostatService = undefined;
|
|
267
268
|
this.batteryService = undefined;
|
|
268
269
|
this.occupancyService = undefined;
|
|
@@ -434,7 +435,31 @@ export default class NestThermostat extends HomeKitDevice {
|
|
|
434
435
|
this?.log?.info?.('Heating/cooling setup on thermostat on "%s" has changed', deviceData.description);
|
|
435
436
|
}
|
|
436
437
|
|
|
437
|
-
// Update current mode temperatures
|
|
438
|
+
// Update current mode, temperatures and log any changes
|
|
439
|
+
if (deviceData.target_temperature_low !== this.deviceData.target_temperature_low) {
|
|
440
|
+
this.#logTemperatureChange(
|
|
441
|
+
'Thermostat',
|
|
442
|
+
'heating',
|
|
443
|
+
deviceData.target_temperature_low,
|
|
444
|
+
deviceData.hvac_mode?.toUpperCase?.().includes('ECO') === true,
|
|
445
|
+
deviceData.temperature_scale?.toUpperCase?.() === 'F' ? 'F' : 'C',
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (deviceData.target_temperature_high !== this.deviceData.target_temperature_high) {
|
|
450
|
+
this.#logTemperatureChange(
|
|
451
|
+
'Thermostat',
|
|
452
|
+
'cooling',
|
|
453
|
+
deviceData.target_temperature_high,
|
|
454
|
+
deviceData.hvac_mode?.toUpperCase?.().includes('ECO') === true,
|
|
455
|
+
deviceData.temperature_scale?.toUpperCase?.() === 'F' ? 'F' : 'C',
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (deviceData.hvac_mode.toUpperCase() !== this.deviceData.hvac_mode.toUpperCase()) {
|
|
460
|
+
this.#logModeChange('Thermostat', deviceData.hvac_mode);
|
|
461
|
+
}
|
|
462
|
+
|
|
438
463
|
if (
|
|
439
464
|
deviceData.can_heat === true &&
|
|
440
465
|
(deviceData.hvac_mode.toUpperCase() === 'HEAT' || deviceData.hvac_mode.toUpperCase() === 'ECOHEAT')
|
|
@@ -786,7 +811,7 @@ export default class NestThermostat extends HomeKitDevice {
|
|
|
786
811
|
this?.log?.info?.('Set temperature units on thermostat "%s" to "%s"', this.deviceData.description, unit === 'C' ? '°C' : '°F');
|
|
787
812
|
}
|
|
788
813
|
|
|
789
|
-
setMode(thermostatMode) {
|
|
814
|
+
async setMode(thermostatMode) {
|
|
790
815
|
if (thermostatMode !== this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value) {
|
|
791
816
|
// Work out based on the HomeKit requested mode, what can the thermostat really switch too
|
|
792
817
|
// We may over-ride the requested HomeKit mode
|
|
@@ -847,9 +872,9 @@ export default class NestThermostat extends HomeKitDevice {
|
|
|
847
872
|
mode = 'range';
|
|
848
873
|
}
|
|
849
874
|
|
|
850
|
-
this.message(HomeKitDevice.SET, { uuid: this.deviceData.nest_google_uuid, hvac_mode: mode });
|
|
875
|
+
await this.message(HomeKitDevice.SET, { uuid: this.deviceData.nest_google_uuid, hvac_mode: mode });
|
|
851
876
|
|
|
852
|
-
this
|
|
877
|
+
this.#logModeChange('HomeKit', mode);
|
|
853
878
|
}
|
|
854
879
|
}
|
|
855
880
|
|
|
@@ -877,50 +902,49 @@ export default class NestThermostat extends HomeKitDevice {
|
|
|
877
902
|
return currentMode;
|
|
878
903
|
}
|
|
879
904
|
|
|
880
|
-
setTemperature(characteristic, temperature) {
|
|
905
|
+
async setTemperature(characteristic, temperature) {
|
|
881
906
|
if (typeof characteristic !== 'function' || typeof characteristic?.UUID !== 'string') {
|
|
882
907
|
return;
|
|
883
908
|
}
|
|
884
909
|
|
|
885
910
|
let mode = this.thermostatService.getCharacteristic(this.hap.Characteristic.TargetHeatingCoolingState).value;
|
|
886
|
-
let isEco = this.deviceData.hvac_mode?.toUpperCase?.().includes('ECO') === true;
|
|
887
|
-
let scale = this.deviceData.temperature_scale?.toUpperCase?.() === 'F' ? 'F' : 'C';
|
|
888
|
-
let tempDisplay = (scale === 'F' ? (temperature * 9) / 5 + 32 : temperature).toFixed(1);
|
|
889
|
-
let tempUnit = scale === 'F' ? '°F' : '°C';
|
|
890
|
-
let ecoPrefix = isEco ? 'eco mode ' : '';
|
|
891
|
-
|
|
892
911
|
let targetKey = undefined;
|
|
893
912
|
let modeLabel = '';
|
|
894
913
|
|
|
895
914
|
if (
|
|
896
915
|
characteristic.UUID === this.hap.Characteristic.TargetTemperature.UUID &&
|
|
897
|
-
mode
|
|
916
|
+
mode === this.hap.Characteristic.TargetHeatingCoolingState.HEAT
|
|
898
917
|
) {
|
|
899
|
-
targetKey = '
|
|
900
|
-
modeLabel =
|
|
918
|
+
targetKey = 'target_temperature_low';
|
|
919
|
+
modeLabel = 'heating';
|
|
920
|
+
} else if (
|
|
921
|
+
characteristic.UUID === this.hap.Characteristic.TargetTemperature.UUID &&
|
|
922
|
+
mode === this.hap.Characteristic.TargetHeatingCoolingState.COOL
|
|
923
|
+
) {
|
|
924
|
+
targetKey = 'target_temperature_high';
|
|
925
|
+
modeLabel = 'cooling';
|
|
901
926
|
} else if (
|
|
902
927
|
characteristic.UUID === this.hap.Characteristic.HeatingThresholdTemperature.UUID &&
|
|
903
|
-
mode === this.hap.Characteristic.TargetHeatingCoolingState.AUTO
|
|
928
|
+
(mode === this.hap.Characteristic.TargetHeatingCoolingState.HEAT || mode === this.hap.Characteristic.TargetHeatingCoolingState.AUTO)
|
|
904
929
|
) {
|
|
905
930
|
targetKey = 'target_temperature_low';
|
|
906
931
|
modeLabel = 'heating';
|
|
907
932
|
} else if (
|
|
908
933
|
characteristic.UUID === this.hap.Characteristic.CoolingThresholdTemperature.UUID &&
|
|
909
|
-
mode === this.hap.Characteristic.TargetHeatingCoolingState.AUTO
|
|
934
|
+
(mode === this.hap.Characteristic.TargetHeatingCoolingState.COOL || mode === this.hap.Characteristic.TargetHeatingCoolingState.AUTO)
|
|
910
935
|
) {
|
|
911
936
|
targetKey = 'target_temperature_high';
|
|
912
937
|
modeLabel = 'cooling';
|
|
913
938
|
}
|
|
914
939
|
|
|
915
940
|
if (targetKey !== undefined) {
|
|
916
|
-
this.message(HomeKitDevice.SET, { uuid: this.deviceData.nest_google_uuid, [targetKey]: temperature });
|
|
917
|
-
this
|
|
918
|
-
'
|
|
919
|
-
ecoPrefix,
|
|
941
|
+
await this.message(HomeKitDevice.SET, { uuid: this.deviceData.nest_google_uuid, [targetKey]: temperature });
|
|
942
|
+
this.#logTemperatureChange(
|
|
943
|
+
'HomeKit',
|
|
920
944
|
modeLabel,
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
945
|
+
temperature,
|
|
946
|
+
this.deviceData.hvac_mode?.toUpperCase?.().includes('ECO') === true,
|
|
947
|
+
this.deviceData.temperature_scale?.toUpperCase?.() === 'F' ? 'F' : 'C',
|
|
924
948
|
);
|
|
925
949
|
}
|
|
926
950
|
|
|
@@ -1077,6 +1101,62 @@ export default class NestThermostat extends HomeKitDevice {
|
|
|
1077
1101
|
|
|
1078
1102
|
return loadedModule;
|
|
1079
1103
|
}
|
|
1104
|
+
|
|
1105
|
+
#logTemperatureChange(source, modeLabel, temperature, isEco, scale) {
|
|
1106
|
+
if (typeof temperature !== 'number') {
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
let unitScale = typeof scale === 'string' ? scale.toUpperCase() : this.deviceData.temperature_scale?.toUpperCase?.();
|
|
1111
|
+
let isFahrenheit = unitScale === 'F';
|
|
1112
|
+
let tempDisplay = (isFahrenheit ? (temperature * 9) / 5 + 32 : temperature).toFixed(1);
|
|
1113
|
+
let tempUnit = isFahrenheit ? '°F' : '°C';
|
|
1114
|
+
let ecoPrefix = isEco === true ? 'eco mode ' : '';
|
|
1115
|
+
|
|
1116
|
+
modeLabel = modeLabel.charAt(0).toUpperCase() + modeLabel.slice(1).toLowerCase();
|
|
1117
|
+
|
|
1118
|
+
if (source === 'Thermostat') {
|
|
1119
|
+
this?.log?.debug?.(
|
|
1120
|
+
'%s%s temperature on "%s" changed to "%s %s"',
|
|
1121
|
+
ecoPrefix,
|
|
1122
|
+
modeLabel,
|
|
1123
|
+
this.deviceData.description,
|
|
1124
|
+
tempDisplay,
|
|
1125
|
+
tempUnit,
|
|
1126
|
+
);
|
|
1127
|
+
} else {
|
|
1128
|
+
this?.log?.info?.(
|
|
1129
|
+
'Set %s%s temperature on "%s" to "%s %s"',
|
|
1130
|
+
ecoPrefix,
|
|
1131
|
+
modeLabel,
|
|
1132
|
+
this.deviceData.description,
|
|
1133
|
+
tempDisplay,
|
|
1134
|
+
tempUnit,
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
#logModeChange(source, modeLabel) {
|
|
1140
|
+
if (typeof modeLabel !== 'string' || modeLabel.trim() === '') {
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
modeLabel = modeLabel.charAt(0).toUpperCase() + modeLabel.slice(1).toLowerCase();
|
|
1145
|
+
|
|
1146
|
+
if (source === 'Thermostat') {
|
|
1147
|
+
this?.log?.debug?.(
|
|
1148
|
+
'Mode on "%s" changed to "%s"',
|
|
1149
|
+
this.deviceData.description,
|
|
1150
|
+
modeLabel.toLowerCase().includes('range') === true ? 'Heat/Cool' : modeLabel,
|
|
1151
|
+
);
|
|
1152
|
+
} else {
|
|
1153
|
+
this?.log?.info?.(
|
|
1154
|
+
'Set mode on "%s" to "%s"',
|
|
1155
|
+
this.deviceData.description,
|
|
1156
|
+
modeLabel.toLowerCase().includes('range') === true ? 'Heat/Cool' : modeLabel,
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1080
1160
|
}
|
|
1081
1161
|
|
|
1082
1162
|
// Function to process our RAW Nest or Google for this device type
|
|
@@ -1105,7 +1185,6 @@ export function processRawData(log, rawData, config, deviceType = undefined) {
|
|
|
1105
1185
|
data.target_temperature = adjustTemperature(data.target_temperature, 'C', 'C', true);
|
|
1106
1186
|
data.backplate_temperature = adjustTemperature(data.backplate_temperature, 'C', 'C', true);
|
|
1107
1187
|
data.current_temperature = adjustTemperature(data.current_temperature, 'C', 'C', true);
|
|
1108
|
-
data.battery_level = scaleValue(data.battery_level, 3.6, 3.9, 0, 100);
|
|
1109
1188
|
|
|
1110
1189
|
processed = data;
|
|
1111
1190
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -1182,7 +1261,24 @@ export function processRawData(log, rawData, config, deviceType = undefined) {
|
|
|
1182
1261
|
Array.isArray(value.value?.display?.thermostatState) === true && value.value.display.thermostatState.includes('bpd') === true;
|
|
1183
1262
|
RESTTypeData.backplate_temperature = parseFloat(value.value.backplate_temperature.temperatureValue.temperature.value);
|
|
1184
1263
|
RESTTypeData.current_temperature = parseFloat(value.value.current_temperature.temperatureValue.temperature.value);
|
|
1185
|
-
|
|
1264
|
+
if (value.value.device_info.typeName === 'google.resource.GoogleZirconium1Resource') {
|
|
1265
|
+
// Lower battery voltages for the "2020" mirror thermostat. Levels are a "guestimate" :-)
|
|
1266
|
+
RESTTypeData.battery_level = scaleValue(
|
|
1267
|
+
parseFloat(value.value.battery_voltage.batteryValue.batteryVoltage.value),
|
|
1268
|
+
2.9,
|
|
1269
|
+
3.15,
|
|
1270
|
+
0,
|
|
1271
|
+
100,
|
|
1272
|
+
);
|
|
1273
|
+
} else {
|
|
1274
|
+
RESTTypeData.battery_level = scaleValue(
|
|
1275
|
+
parseFloat(value.value.battery_voltage.batteryValue.batteryVoltage.value),
|
|
1276
|
+
3.6,
|
|
1277
|
+
3.9,
|
|
1278
|
+
0,
|
|
1279
|
+
100,
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1186
1282
|
RESTTypeData.online = value.value?.liveness?.status === 'LIVENESS_DEVICE_STATUS_ONLINE';
|
|
1187
1283
|
RESTTypeData.leaf = value.value?.leaf?.active === true;
|
|
1188
1284
|
RESTTypeData.can_cool =
|
|
@@ -1404,7 +1500,7 @@ export function processRawData(log, rawData, config, deviceType = undefined) {
|
|
|
1404
1500
|
RESTTypeData.removed_from_base = value.value.nlclient_state.toUpperCase() === 'BPD';
|
|
1405
1501
|
RESTTypeData.backplate_temperature = value.value.backplate_temperature;
|
|
1406
1502
|
RESTTypeData.current_temperature = value.value.backplate_temperature;
|
|
1407
|
-
RESTTypeData.battery_level = value.value.battery_level;
|
|
1503
|
+
RESTTypeData.battery_level = scaleValue(value.value.battery_level, 3.6, 3.9, 0, 100);
|
|
1408
1504
|
RESTTypeData.online = rawData?.['track.' + value.value.serial_number]?.value?.online === true;
|
|
1409
1505
|
RESTTypeData.leaf = value.value.leaf === true;
|
|
1410
1506
|
RESTTypeData.has_humidifier = value.value.has_humidifier === true;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "homebridge-nest-accfactory",
|
|
3
3
|
"displayName": "Nest Accfactory",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.5",
|
|
6
6
|
"description": "Homebridge support for Nest/Google devices including HomeKit Secure Video (HKSV) support for doorbells and cameras",
|
|
7
7
|
"author": "n0rt0nthec4t",
|
|
8
8
|
"license": "Apache-2.0",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
],
|
|
37
37
|
"main": "dist/index.js",
|
|
38
38
|
"engines": {
|
|
39
|
-
"node": "^20
|
|
40
|
-
"homebridge": "^1.
|
|
39
|
+
"node": "^20 || ^22 || ^24",
|
|
40
|
+
"homebridge": "^1.11.1 || ^2.0.0-beta.0"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"LICENSE",
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
"prepublishOnly": "npm run lint && npm run build"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@eslint/js": "^9.
|
|
58
|
-
"eslint": "^9.
|
|
59
|
-
"@stylistic/eslint-plugin": "^5.
|
|
60
|
-
"@types/node": "^24.
|
|
61
|
-
"@typescript-eslint/parser": "^8.
|
|
57
|
+
"@eslint/js": "^9.39.1",
|
|
58
|
+
"eslint": "^9.39.1",
|
|
59
|
+
"@stylistic/eslint-plugin": "^5.6.1",
|
|
60
|
+
"@types/node": "^24.10.1",
|
|
61
|
+
"@typescript-eslint/parser": "^8.47.0",
|
|
62
62
|
"prettier": "^3.6.2",
|
|
63
63
|
"prettier-eslint": "^16.4.2",
|
|
64
64
|
"copyfiles": "^2.4.1",
|
|
65
|
-
"rimraf": "^6.0
|
|
66
|
-
"homebridge": "^2.0.0-beta.0"
|
|
65
|
+
"rimraf": "^6.1.0",
|
|
66
|
+
"homebridge": "^1.11.1 || ^2.0.0-beta.0"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@evan/opus": "^1.0.3",
|