homebridge-nest-accfactory 0.3.0 → 0.3.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 +4 -0
- package/dist/HomeKitDevice.js +43 -34
- package/dist/config.js +10 -10
- package/dist/devices.js +14 -9
- package/dist/nexustalk.js +28 -28
- package/dist/plugins/camera.js +22 -20
- package/dist/plugins/protect.js +3 -3
- package/dist/plugins/tempsensor.js +3 -3
- package/dist/plugins/thermostat.js +3 -3
- package/dist/streamer.js +27 -27
- package/dist/system.js +79 -93
- package/dist/webrtc.js +12 -12
- package/package.json +5 -5
package/dist/system.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Nest System communications
|
|
2
2
|
// Part of homebridge-nest-accfactory
|
|
3
3
|
//
|
|
4
|
-
// Code version 2025.06.
|
|
4
|
+
// Code version 2025.06.15
|
|
5
5
|
// Mark Hulskamp
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -19,19 +19,19 @@ import { fileURLToPath } from 'node:url';
|
|
|
19
19
|
|
|
20
20
|
// Import our modules
|
|
21
21
|
import HomeKitDevice from './HomeKitDevice.js';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
22
|
+
import { DEVICE_TYPE, loadDeviceModules, getDeviceHKCategory } from './devices.js';
|
|
23
|
+
import { ACCOUNT_TYPE, processConfig, buildConnections } from './config.js';
|
|
24
24
|
|
|
25
25
|
// Define constants
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
26
|
+
const CAMERA_ALERT_POLLING = 2000; // Camera alerts polling timer
|
|
27
|
+
const CAMERA_ZONE_POLLING = 30000; // Camera zones changes polling timer
|
|
28
|
+
const WEATHER_POLLING = 300000; // Weather data polling timer
|
|
29
|
+
const NEST_API_TIMEOUT = 10000; // Nest API timeout
|
|
30
|
+
const USER_AGENT = 'Nest/5.78.0 (iOScom.nestlabs.jasper.release) os=18.0'; // User Agent string
|
|
31
31
|
const __dirname = path.dirname(fileURLToPath(import.meta.url)); // Make a defined for JS __dirname
|
|
32
32
|
const DATASOURCE = {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
NEST_API: 'Nest', // From the Nest API
|
|
34
|
+
PROTOBUF_API: 'Protobuf', // From the Protobuf API
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
// We handle the connections to Nest/Google
|
|
@@ -143,7 +143,7 @@ export default class NestAccfactory {
|
|
|
143
143
|
this.#connections[uuid].name,
|
|
144
144
|
this.#connections[uuid].fieldTest === true ? 'using field test endpoints' : '',
|
|
145
145
|
);
|
|
146
|
-
if (this.#connections[uuid].type ===
|
|
146
|
+
if (this.#connections[uuid].type === ACCOUNT_TYPE.GOOGLE) {
|
|
147
147
|
// Google cookie method as refresh token method no longer supported by Google since October 2022
|
|
148
148
|
// Instructions from homebridge_nest or homebridge_nest_cam to obtain this
|
|
149
149
|
this?.log?.debug?.('Performing authorisation using Google account for connection uuid "%s"', uuid);
|
|
@@ -151,7 +151,7 @@ export default class NestAccfactory {
|
|
|
151
151
|
await fetchWrapper('get', this.#connections[uuid].issuetoken, {
|
|
152
152
|
headers: {
|
|
153
153
|
referer: 'https://accounts.google.com/o/oauth2/iframe',
|
|
154
|
-
'User-Agent':
|
|
154
|
+
'User-Agent': USER_AGENT,
|
|
155
155
|
cookie: this.#connections[uuid].cookie,
|
|
156
156
|
'Sec-Fetch-Mode': 'cors',
|
|
157
157
|
'X-Requested-With': 'XmlHttpRequest',
|
|
@@ -167,7 +167,7 @@ export default class NestAccfactory {
|
|
|
167
167
|
{
|
|
168
168
|
headers: {
|
|
169
169
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
170
|
-
'User-Agent':
|
|
170
|
+
'User-Agent': USER_AGENT,
|
|
171
171
|
Authorization: data.token_type + ' ' + data.access_token,
|
|
172
172
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
173
173
|
},
|
|
@@ -184,7 +184,7 @@ export default class NestAccfactory {
|
|
|
184
184
|
await fetchWrapper('get', 'https://' + this.#connections[uuid].restAPIHost + '/session', {
|
|
185
185
|
headers: {
|
|
186
186
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
187
|
-
'User-Agent':
|
|
187
|
+
'User-Agent': USER_AGENT,
|
|
188
188
|
Authorization: 'Basic ' + googleToken,
|
|
189
189
|
},
|
|
190
190
|
})
|
|
@@ -227,7 +227,7 @@ export default class NestAccfactory {
|
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
if (this.#connections[uuid].type ===
|
|
230
|
+
if (this.#connections[uuid].type === ACCOUNT_TYPE.NEST) {
|
|
231
231
|
// Nest access token method. Get WEBSITE2 cookie for use with camera API calls if needed later
|
|
232
232
|
this?.log?.debug?.('Performing authorisation using Nest account for connection uuid "%s"', uuid);
|
|
233
233
|
|
|
@@ -238,7 +238,7 @@ export default class NestAccfactory {
|
|
|
238
238
|
withCredentials: true,
|
|
239
239
|
headers: {
|
|
240
240
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
241
|
-
'User-Agent':
|
|
241
|
+
'User-Agent': USER_AGENT,
|
|
242
242
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
243
243
|
},
|
|
244
244
|
},
|
|
@@ -255,7 +255,7 @@ export default class NestAccfactory {
|
|
|
255
255
|
await fetchWrapper('get', 'https://' + this.#connections[uuid].restAPIHost + '/session', {
|
|
256
256
|
headers: {
|
|
257
257
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
258
|
-
'User-Agent':
|
|
258
|
+
'User-Agent': USER_AGENT,
|
|
259
259
|
Authorization: 'Basic ' + this.#connections[uuid].access_token,
|
|
260
260
|
},
|
|
261
261
|
})
|
|
@@ -336,7 +336,7 @@ export default class NestAccfactory {
|
|
|
336
336
|
subscribeJSONData = { objects: [] };
|
|
337
337
|
Object.entries(this.#rawData)
|
|
338
338
|
// eslint-disable-next-line no-unused-vars
|
|
339
|
-
.filter(([object_key, object]) => object.source === DATASOURCE.
|
|
339
|
+
.filter(([object_key, object]) => object.source === DATASOURCE.NEST_API && object.connection === uuid)
|
|
340
340
|
.forEach(([object_key, object]) => {
|
|
341
341
|
subscribeJSONData.objects.push({
|
|
342
342
|
object_key: object_key,
|
|
@@ -356,7 +356,7 @@ export default class NestAccfactory {
|
|
|
356
356
|
{
|
|
357
357
|
headers: {
|
|
358
358
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
359
|
-
'User-Agent':
|
|
359
|
+
'User-Agent': USER_AGENT,
|
|
360
360
|
Authorization: 'Basic ' + this.#connections[uuid].token,
|
|
361
361
|
},
|
|
362
362
|
keepalive: true,
|
|
@@ -421,11 +421,11 @@ export default class NestAccfactory {
|
|
|
421
421
|
{
|
|
422
422
|
headers: {
|
|
423
423
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
424
|
-
'User-Agent':
|
|
424
|
+
'User-Agent': USER_AGENT,
|
|
425
425
|
[this.#connections[uuid].cameraAPI.key]:
|
|
426
426
|
this.#connections[uuid].cameraAPI.value + this.#connections[uuid].cameraAPI.token,
|
|
427
427
|
},
|
|
428
|
-
timeout:
|
|
428
|
+
timeout: NEST_API_TIMEOUT,
|
|
429
429
|
},
|
|
430
430
|
);
|
|
431
431
|
let data = await response.json();
|
|
@@ -451,11 +451,11 @@ export default class NestAccfactory {
|
|
|
451
451
|
{
|
|
452
452
|
headers: {
|
|
453
453
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
454
|
-
'User-Agent':
|
|
454
|
+
'User-Agent': USER_AGENT,
|
|
455
455
|
[this.#connections[uuid].cameraAPI.key]:
|
|
456
456
|
this.#connections[uuid].cameraAPI.value + this.#connections[uuid].cameraAPI.token,
|
|
457
457
|
},
|
|
458
|
-
timeout:
|
|
458
|
+
timeout: NEST_API_TIMEOUT,
|
|
459
459
|
},
|
|
460
460
|
);
|
|
461
461
|
let data = await response.json();
|
|
@@ -463,7 +463,7 @@ export default class NestAccfactory {
|
|
|
463
463
|
.filter((zone) => zone?.type?.toUpperCase() === 'ACTIVITY' || zone?.type?.toUpperCase() === 'REGION')
|
|
464
464
|
.map((zone) => ({
|
|
465
465
|
id: zone.id === 0 ? 1 : zone.id,
|
|
466
|
-
name: makeHomeKitName(zone.label),
|
|
466
|
+
name: HomeKitDevice.makeHomeKitName(zone.label),
|
|
467
467
|
hidden: zone.hidden === true,
|
|
468
468
|
uri: zone.nexusapi_image_uri,
|
|
469
469
|
}));
|
|
@@ -533,7 +533,7 @@ export default class NestAccfactory {
|
|
|
533
533
|
this.#rawData[value.object_key].object_revision = value.object_revision;
|
|
534
534
|
this.#rawData[value.object_key].object_timestamp = value.object_timestamp;
|
|
535
535
|
this.#rawData[value.object_key].connection = uuid;
|
|
536
|
-
this.#rawData[value.object_key].source = DATASOURCE.
|
|
536
|
+
this.#rawData[value.object_key].source = DATASOURCE.NEST_API;
|
|
537
537
|
this.#rawData[value.object_key].value = {};
|
|
538
538
|
}
|
|
539
539
|
|
|
@@ -629,11 +629,11 @@ export default class NestAccfactory {
|
|
|
629
629
|
// This also depends on the account type we connected with
|
|
630
630
|
// Nest accounts cannot observe camera/doorbell product traits
|
|
631
631
|
if (
|
|
632
|
-
(this.#connections[uuid].type ===
|
|
632
|
+
(this.#connections[uuid].type === ACCOUNT_TYPE.NEST &&
|
|
633
633
|
type.fullName.startsWith('.nest.trait.product.camera') === false &&
|
|
634
634
|
type.fullName.startsWith('.nest.trait.product.doorbell') === false &&
|
|
635
635
|
(type.fullName.startsWith('.nest.trait') === true || type.fullName.startsWith('.weave.') === true)) ||
|
|
636
|
-
(this.#connections[uuid].type ===
|
|
636
|
+
(this.#connections[uuid].type === ACCOUNT_TYPE.GOOGLE &&
|
|
637
637
|
(type.fullName.startsWith('.nest.trait') === true ||
|
|
638
638
|
type.fullName.startsWith('.weave.') === true ||
|
|
639
639
|
type.fullName.startsWith('.google.trait.product.camera') === true))
|
|
@@ -654,7 +654,7 @@ export default class NestAccfactory {
|
|
|
654
654
|
{
|
|
655
655
|
headers: {
|
|
656
656
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
657
|
-
'User-Agent':
|
|
657
|
+
'User-Agent': USER_AGENT,
|
|
658
658
|
Authorization: 'Basic ' + this.#connections[uuid].token,
|
|
659
659
|
'Content-Type': 'application/x-protobuf',
|
|
660
660
|
'X-Accept-Content-Transfer-Encoding': 'binary',
|
|
@@ -745,7 +745,7 @@ export default class NestAccfactory {
|
|
|
745
745
|
if (typeof this.#rawData[trait.traitId.resourceId] === 'undefined') {
|
|
746
746
|
this.#rawData[trait.traitId.resourceId] = {};
|
|
747
747
|
this.#rawData[trait.traitId.resourceId].connection = uuid;
|
|
748
|
-
this.#rawData[trait.traitId.resourceId].source = DATASOURCE.
|
|
748
|
+
this.#rawData[trait.traitId.resourceId].source = DATASOURCE.PROTOBUF_API;
|
|
749
749
|
this.#rawData[trait.traitId.resourceId].value = {};
|
|
750
750
|
}
|
|
751
751
|
this.#rawData[trait.traitId.resourceId]['value'][trait.traitId.traitLabel] =
|
|
@@ -849,9 +849,9 @@ export default class NestAccfactory {
|
|
|
849
849
|
|
|
850
850
|
// Optional things for each device type
|
|
851
851
|
if (
|
|
852
|
-
deviceClass.TYPE ===
|
|
853
|
-
deviceClass.TYPE ===
|
|
854
|
-
deviceClass.TYPE ===
|
|
852
|
+
deviceClass.TYPE === DEVICE_TYPE.CAMERA ||
|
|
853
|
+
deviceClass.TYPE === DEVICE_TYPE.DOORBELL ||
|
|
854
|
+
deviceClass.TYPE === DEVICE_TYPE.FLOODLIGHT
|
|
855
855
|
) {
|
|
856
856
|
// Setup polling loop for camera/doorbell zone data
|
|
857
857
|
// This is only required for Nest API data sources as these details are present in Protobuf API
|
|
@@ -860,7 +860,7 @@ export default class NestAccfactory {
|
|
|
860
860
|
let nest_google_uuid = this.#trackedDevices?.[deviceData?.serialNumber]?.rawDataUuid;
|
|
861
861
|
if (
|
|
862
862
|
this.#rawData?.[nest_google_uuid]?.value !== undefined &&
|
|
863
|
-
this.#trackedDevices?.[deviceData?.serialNumber]?.source === DATASOURCE.
|
|
863
|
+
this.#trackedDevices?.[deviceData?.serialNumber]?.source === DATASOURCE.NEST_API
|
|
864
864
|
) {
|
|
865
865
|
try {
|
|
866
866
|
let response = await fetchWrapper(
|
|
@@ -871,12 +871,12 @@ export default class NestAccfactory {
|
|
|
871
871
|
{
|
|
872
872
|
headers: {
|
|
873
873
|
referer: 'https://' + this.#connections[this.#rawData[nest_google_uuid].connection].referer,
|
|
874
|
-
'User-Agent':
|
|
874
|
+
'User-Agent': USER_AGENT,
|
|
875
875
|
[this.#connections[this.#rawData[nest_google_uuid].connection].cameraAPI.key]:
|
|
876
876
|
this.#connections[this.#rawData[nest_google_uuid].connection].cameraAPI.value +
|
|
877
877
|
this.#connections[this.#rawData[nest_google_uuid].connection].cameraAPI.token,
|
|
878
878
|
},
|
|
879
|
-
timeout:
|
|
879
|
+
timeout: CAMERA_ZONE_POLLING,
|
|
880
880
|
},
|
|
881
881
|
);
|
|
882
882
|
let data = await response.json();
|
|
@@ -888,7 +888,7 @@ export default class NestAccfactory {
|
|
|
888
888
|
.filter((zone) => zone.type.toUpperCase() === 'ACTIVITY' || zone.type.toUpperCase() === 'REGION')
|
|
889
889
|
.map((zone) => ({
|
|
890
890
|
id: zone.id === 0 ? 1 : zone.id,
|
|
891
|
-
name: makeHomeKitName(zone.label),
|
|
891
|
+
name: HomeKitDevice.makeHomeKitName(zone.label),
|
|
892
892
|
hidden: zone.hidden === true,
|
|
893
893
|
uri: zone.nexusapi_image_uri,
|
|
894
894
|
}))
|
|
@@ -916,7 +916,7 @@ export default class NestAccfactory {
|
|
|
916
916
|
}
|
|
917
917
|
}
|
|
918
918
|
}
|
|
919
|
-
},
|
|
919
|
+
}, CAMERA_ZONE_POLLING);
|
|
920
920
|
|
|
921
921
|
// Setup polling loop for camera/doorbell alert data, clearing any existing polling loop
|
|
922
922
|
clearInterval(this.#trackedDevices?.[deviceData.serialNumber]?.timers?.alerts);
|
|
@@ -925,7 +925,7 @@ export default class NestAccfactory {
|
|
|
925
925
|
let nest_google_uuid = this.#trackedDevices?.[deviceData?.serialNumber]?.rawDataUuid;
|
|
926
926
|
if (
|
|
927
927
|
this.#rawData?.[nest_google_uuid]?.value !== undefined &&
|
|
928
|
-
this.#trackedDevices?.[deviceData?.serialNumber]?.source === DATASOURCE.
|
|
928
|
+
this.#trackedDevices?.[deviceData?.serialNumber]?.source === DATASOURCE.PROTOBUF_API
|
|
929
929
|
) {
|
|
930
930
|
let commandResponse = await this.#protobufCommand(
|
|
931
931
|
this.#rawData[nest_google_uuid].connection,
|
|
@@ -991,7 +991,7 @@ export default class NestAccfactory {
|
|
|
991
991
|
|
|
992
992
|
if (
|
|
993
993
|
this.#rawData?.[nest_google_uuid]?.value !== undefined &&
|
|
994
|
-
this.#trackedDevices?.[deviceData?.serialNumber]?.source === DATASOURCE.
|
|
994
|
+
this.#trackedDevices?.[deviceData?.serialNumber]?.source === DATASOURCE.NEST_API
|
|
995
995
|
) {
|
|
996
996
|
try {
|
|
997
997
|
let response = await fetchWrapper(
|
|
@@ -1004,12 +1004,12 @@ export default class NestAccfactory {
|
|
|
1004
1004
|
{
|
|
1005
1005
|
headers: {
|
|
1006
1006
|
referer: 'https://' + this.#connections[this.#rawData[nest_google_uuid].connection].referer,
|
|
1007
|
-
'User-Agent':
|
|
1007
|
+
'User-Agent': USER_AGENT,
|
|
1008
1008
|
[this.#connections[this.#rawData[nest_google_uuid].connection].cameraAPI.key]:
|
|
1009
1009
|
this.#connections[this.#rawData[nest_google_uuid].connection].cameraAPI.value +
|
|
1010
1010
|
this.#connections[this.#rawData[nest_google_uuid].connection].cameraAPI.token,
|
|
1011
1011
|
},
|
|
1012
|
-
timeout:
|
|
1012
|
+
timeout: CAMERA_ALERT_POLLING,
|
|
1013
1013
|
retry: 3,
|
|
1014
1014
|
},
|
|
1015
1015
|
);
|
|
@@ -1055,10 +1055,10 @@ export default class NestAccfactory {
|
|
|
1055
1055
|
this.#trackedDevices?.[deviceData?.serialNumber]?.uuid &&
|
|
1056
1056
|
this.#eventEmitter?.emit?.(this.#trackedDevices[deviceData.serialNumber].uuid, HomeKitDevice.UPDATE, { alerts: alerts });
|
|
1057
1057
|
}
|
|
1058
|
-
},
|
|
1058
|
+
}, CAMERA_ALERT_POLLING);
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
|
-
if (deviceClass.TYPE ===
|
|
1061
|
+
if (deviceClass.TYPE === DEVICE_TYPE.WEATHER) {
|
|
1062
1062
|
// Setup polling loop for weather data, clearing any existing polling loop
|
|
1063
1063
|
clearInterval(this.#trackedDevices?.[deviceData.serialNumber]?.timers?.weather);
|
|
1064
1064
|
this.#trackedDevices[deviceData.serialNumber].timers.weather = setInterval(async () => {
|
|
@@ -1077,7 +1077,7 @@ export default class NestAccfactory {
|
|
|
1077
1077
|
this.#processData(this.#trackedDevices[deviceData.serialNumber].rawDataUuid)?.[deviceData.serialNumber],
|
|
1078
1078
|
);
|
|
1079
1079
|
}
|
|
1080
|
-
},
|
|
1080
|
+
}, WEATHER_POLLING);
|
|
1081
1081
|
}
|
|
1082
1082
|
}
|
|
1083
1083
|
}
|
|
@@ -1199,7 +1199,7 @@ export default class NestAccfactory {
|
|
|
1199
1199
|
if (description === '' && location === '') {
|
|
1200
1200
|
description = 'unknown description';
|
|
1201
1201
|
}
|
|
1202
|
-
data.description = makeHomeKitName(location === '' ? description : description + ' - ' + location);
|
|
1202
|
+
data.description = HomeKitDevice.makeHomeKitName(location === '' ? description : description + ' - ' + location);
|
|
1203
1203
|
delete data.location;
|
|
1204
1204
|
|
|
1205
1205
|
// Insert HomeKit pairing code for when using HAP-NodeJS library rather than Homebridge
|
|
@@ -1240,7 +1240,7 @@ export default class NestAccfactory {
|
|
|
1240
1240
|
let processed = {};
|
|
1241
1241
|
try {
|
|
1242
1242
|
// Fix up data we need to
|
|
1243
|
-
data.device_type =
|
|
1243
|
+
data.device_type = DEVICE_TYPE.THERMOSTAT; // Nest Thermostat
|
|
1244
1244
|
|
|
1245
1245
|
// If we have hot water control, it should be a 'UK/EU' model, so add that after the 'gen' tag in the model name
|
|
1246
1246
|
data.model = data.has_hot_water_control === true ? data.model.replace(/\bgen\)/, 'gen, EU)') : data.model;
|
|
@@ -1283,7 +1283,7 @@ export default class NestAccfactory {
|
|
|
1283
1283
|
let tempDevice = {};
|
|
1284
1284
|
try {
|
|
1285
1285
|
if (
|
|
1286
|
-
value?.source === DATASOURCE.
|
|
1286
|
+
value?.source === DATASOURCE.PROTOBUF_API &&
|
|
1287
1287
|
this.config.options?.useGoogleAPI === true &&
|
|
1288
1288
|
value.value?.configuration_done?.deviceReady === true
|
|
1289
1289
|
) {
|
|
@@ -1515,7 +1515,7 @@ export default class NestAccfactory {
|
|
|
1515
1515
|
tempDevice = process_thermostat_data(object_key, RESTTypeData);
|
|
1516
1516
|
}
|
|
1517
1517
|
|
|
1518
|
-
if (value?.source === DATASOURCE.
|
|
1518
|
+
if (value?.source === DATASOURCE.NEST_API && this.config.options?.useNestAPI === true && value.value?.where_id !== undefined) {
|
|
1519
1519
|
let RESTTypeData = {};
|
|
1520
1520
|
RESTTypeData.serialNumber = value.value.serial_number;
|
|
1521
1521
|
RESTTypeData.softwareVersion = value.value.current_version;
|
|
@@ -1566,7 +1566,7 @@ export default class NestAccfactory {
|
|
|
1566
1566
|
|
|
1567
1567
|
RESTTypeData.description =
|
|
1568
1568
|
this.#rawData?.['shared.' + value.value.serial_number]?.value?.name !== undefined
|
|
1569
|
-
? makeHomeKitName(this.#rawData['shared.' + value.value.serial_number].value.name)
|
|
1569
|
+
? HomeKitDevice.makeHomeKitName(this.#rawData['shared.' + value.value.serial_number].value.name)
|
|
1570
1570
|
: '';
|
|
1571
1571
|
RESTTypeData.location = get_location_name(
|
|
1572
1572
|
this.#rawData?.['link.' + value.value.serial_number].value.structure.split('.')[1],
|
|
@@ -1763,7 +1763,7 @@ export default class NestAccfactory {
|
|
|
1763
1763
|
let processed = {};
|
|
1764
1764
|
try {
|
|
1765
1765
|
// Fix up data we need to
|
|
1766
|
-
data.device_type =
|
|
1766
|
+
data.device_type = DEVICE_TYPE.TEMPSENSOR; // Nest Temperature sensor
|
|
1767
1767
|
data.model = 'Temperature Sensor';
|
|
1768
1768
|
data.softwareVersion = '1.0.0';
|
|
1769
1769
|
data = process_common_data(object_key, data);
|
|
@@ -1787,7 +1787,7 @@ export default class NestAccfactory {
|
|
|
1787
1787
|
let tempDevice = {};
|
|
1788
1788
|
try {
|
|
1789
1789
|
if (
|
|
1790
|
-
value?.source === DATASOURCE.
|
|
1790
|
+
value?.source === DATASOURCE.PROTOBUF_API &&
|
|
1791
1791
|
this.config.options?.useGoogleAPI === true &&
|
|
1792
1792
|
value.value?.configuration_done?.deviceReady === true &&
|
|
1793
1793
|
typeof value?.value?.associated_thermostat === 'string' &&
|
|
@@ -1813,7 +1813,7 @@ export default class NestAccfactory {
|
|
|
1813
1813
|
tempDevice = process_kryptonite_data(object_key, RESTTypeData);
|
|
1814
1814
|
}
|
|
1815
1815
|
if (
|
|
1816
|
-
value?.source === DATASOURCE.
|
|
1816
|
+
value?.source === DATASOURCE.NEST_API &&
|
|
1817
1817
|
this.config.options?.useNestAPI === true &&
|
|
1818
1818
|
value.value?.where_id !== undefined &&
|
|
1819
1819
|
value.value?.structure_id !== undefined &&
|
|
@@ -1852,7 +1852,7 @@ export default class NestAccfactory {
|
|
|
1852
1852
|
let processed = {};
|
|
1853
1853
|
try {
|
|
1854
1854
|
// Fix up data we need to
|
|
1855
|
-
data.device_type =
|
|
1855
|
+
data.device_type = DEVICE_TYPE.HEATLINK;
|
|
1856
1856
|
data = process_common_data(object_key, data);
|
|
1857
1857
|
data.current_temperature = adjustTemperature(data.current_temperature, 'C', 'C', true);
|
|
1858
1858
|
processed = data;
|
|
@@ -1874,7 +1874,7 @@ export default class NestAccfactory {
|
|
|
1874
1874
|
let tempDevice = {};
|
|
1875
1875
|
try {
|
|
1876
1876
|
if (
|
|
1877
|
-
value?.source === DATASOURCE.
|
|
1877
|
+
value?.source === DATASOURCE.PROTOBUF_API &&
|
|
1878
1878
|
this.config.options?.useGoogleAPI === true &&
|
|
1879
1879
|
value.value?.configuration_done?.deviceReady === true
|
|
1880
1880
|
) {
|
|
@@ -1915,7 +1915,7 @@ export default class NestAccfactory {
|
|
|
1915
1915
|
let processed = {};
|
|
1916
1916
|
try {
|
|
1917
1917
|
// Fix up data we need to
|
|
1918
|
-
data.device_type =
|
|
1918
|
+
data.device_type = DEVICE_TYPE.SMOKESENSOR; // Nest Protect
|
|
1919
1919
|
data = process_common_data(object_key, data);
|
|
1920
1920
|
processed = data;
|
|
1921
1921
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -1943,7 +1943,7 @@ export default class NestAccfactory {
|
|
|
1943
1943
|
let tempDevice = {};
|
|
1944
1944
|
try {
|
|
1945
1945
|
if (
|
|
1946
|
-
value?.source === DATASOURCE.
|
|
1946
|
+
value?.source === DATASOURCE.PROTOBUF_API &&
|
|
1947
1947
|
this.config.options?.useGoogleAPI === true &&
|
|
1948
1948
|
value.value?.configuration_done?.deviceReady === true
|
|
1949
1949
|
) {
|
|
@@ -2012,7 +2012,7 @@ export default class NestAccfactory {
|
|
|
2012
2012
|
tempDevice = process_protect_data(object_key, RESTTypeData);
|
|
2013
2013
|
}
|
|
2014
2014
|
if (
|
|
2015
|
-
value?.source === DATASOURCE.
|
|
2015
|
+
value?.source === DATASOURCE.NEST_API &&
|
|
2016
2016
|
this.config.options?.useNestAPI === true &&
|
|
2017
2017
|
value.value?.where_id !== undefined &&
|
|
2018
2018
|
value.value?.structure_id !== undefined
|
|
@@ -2081,12 +2081,12 @@ export default class NestAccfactory {
|
|
|
2081
2081
|
let processed = {};
|
|
2082
2082
|
try {
|
|
2083
2083
|
// Fix up data we need to
|
|
2084
|
-
data.device_type =
|
|
2084
|
+
data.device_type = DEVICE_TYPE.CAMERA;
|
|
2085
2085
|
if (data.model.toUpperCase().includes('DOORBELL') === true) {
|
|
2086
|
-
data.device_type =
|
|
2086
|
+
data.device_type = DEVICE_TYPE.DOORBELL;
|
|
2087
2087
|
}
|
|
2088
2088
|
if (data.model.toUpperCase().includes('FLOODLIGHT') === true) {
|
|
2089
|
-
data.device_type =
|
|
2089
|
+
data.device_type = DEVICE_TYPE.FLOODLIGHT;
|
|
2090
2090
|
}
|
|
2091
2091
|
data = process_common_data(object_key, data);
|
|
2092
2092
|
processed = data;
|
|
@@ -2120,7 +2120,7 @@ export default class NestAccfactory {
|
|
|
2120
2120
|
let tempDevice = {};
|
|
2121
2121
|
try {
|
|
2122
2122
|
if (
|
|
2123
|
-
value?.source === DATASOURCE.
|
|
2123
|
+
value?.source === DATASOURCE.PROTOBUF_API &&
|
|
2124
2124
|
this.config.options?.useGoogleAPI === true &&
|
|
2125
2125
|
Array.isArray(value.value?.streaming_protocol?.supportedProtocols) === true &&
|
|
2126
2126
|
value.value.streaming_protocol.supportedProtocols.includes('PROTOCOL_WEBRTC') === true &&
|
|
@@ -2192,7 +2192,7 @@ export default class NestAccfactory {
|
|
|
2192
2192
|
value.value.activity_zone_settings.activityZones.forEach((zone) => {
|
|
2193
2193
|
RESTTypeData.activity_zones.push({
|
|
2194
2194
|
id: zone.zoneProperties?.zoneId !== undefined ? zone.zoneProperties.zoneId : zone.zoneProperties.internalIndex,
|
|
2195
|
-
name: makeHomeKitName(zone.zoneProperties?.name !== undefined ? zone.zoneProperties.name : ''),
|
|
2195
|
+
name: HomeKitDevice.makeHomeKitName(zone.zoneProperties?.name !== undefined ? zone.zoneProperties.name : ''),
|
|
2196
2196
|
hidden: false,
|
|
2197
2197
|
uri: '',
|
|
2198
2198
|
});
|
|
@@ -2230,7 +2230,7 @@ export default class NestAccfactory {
|
|
|
2230
2230
|
}
|
|
2231
2231
|
|
|
2232
2232
|
if (
|
|
2233
|
-
value?.source === DATASOURCE.
|
|
2233
|
+
value?.source === DATASOURCE.NEST_API &&
|
|
2234
2234
|
this.config.options?.useNestAPI === true &&
|
|
2235
2235
|
value.value?.where_id !== undefined &&
|
|
2236
2236
|
value.value?.structure_id !== undefined &&
|
|
@@ -2314,7 +2314,7 @@ export default class NestAccfactory {
|
|
|
2314
2314
|
let processed = {};
|
|
2315
2315
|
try {
|
|
2316
2316
|
// Fix up data we need to
|
|
2317
|
-
data.device_type =
|
|
2317
|
+
data.device_type = DEVICE_TYPE.WEATHER;
|
|
2318
2318
|
data.model = 'Weather';
|
|
2319
2319
|
data.softwareVersion = '1.0.0';
|
|
2320
2320
|
data = process_common_data(object_key, data);
|
|
@@ -2346,7 +2346,7 @@ export default class NestAccfactory {
|
|
|
2346
2346
|
let tempDevice = {};
|
|
2347
2347
|
try {
|
|
2348
2348
|
if (
|
|
2349
|
-
value?.source === DATASOURCE.
|
|
2349
|
+
value?.source === DATASOURCE.PROTOBUF_API &&
|
|
2350
2350
|
this.config.options?.useGoogleAPI === true &&
|
|
2351
2351
|
value.value?.structure_location?.geoCoordinate?.latitude !== undefined &&
|
|
2352
2352
|
value.value?.structure_location?.geoCoordinate?.longitude !== undefined
|
|
@@ -2371,7 +2371,7 @@ export default class NestAccfactory {
|
|
|
2371
2371
|
}
|
|
2372
2372
|
|
|
2373
2373
|
if (
|
|
2374
|
-
value?.source === DATASOURCE.
|
|
2374
|
+
value?.source === DATASOURCE.NEST_API &&
|
|
2375
2375
|
this.config.options?.useNestAPI === true &&
|
|
2376
2376
|
value.value?.latitude !== undefined &&
|
|
2377
2377
|
value.value?.longitude !== undefined
|
|
@@ -2424,7 +2424,7 @@ export default class NestAccfactory {
|
|
|
2424
2424
|
let nest_google_uuid = values.uuid; // Nest/Google structure uuid for this get request
|
|
2425
2425
|
let uuid = this.#rawData[values.uuid].connection; // Connection uuid for this device
|
|
2426
2426
|
|
|
2427
|
-
if (this.#protobufRoot !== null && this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.
|
|
2427
|
+
if (this.#protobufRoot !== null && this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.PROTOBUF_API) {
|
|
2428
2428
|
let updatedTraits = [];
|
|
2429
2429
|
let protobufElement = {
|
|
2430
2430
|
traitRequest: {
|
|
@@ -2736,7 +2736,7 @@ export default class NestAccfactory {
|
|
|
2736
2736
|
}
|
|
2737
2737
|
}
|
|
2738
2738
|
|
|
2739
|
-
if (this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.
|
|
2739
|
+
if (this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.NEST_API && nest_google_uuid.startsWith('quartz.') === true) {
|
|
2740
2740
|
// Set value on Nest Camera/Doorbell
|
|
2741
2741
|
await Promise.all(
|
|
2742
2742
|
Object.entries(values)
|
|
@@ -2758,12 +2758,12 @@ export default class NestAccfactory {
|
|
|
2758
2758
|
{
|
|
2759
2759
|
headers: {
|
|
2760
2760
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
2761
|
-
'User-Agent':
|
|
2761
|
+
'User-Agent': USER_AGENT,
|
|
2762
2762
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
2763
2763
|
[this.#connections[uuid].cameraAPI.key]:
|
|
2764
2764
|
this.#connections[uuid].cameraAPI.value + this.#connections[uuid].cameraAPI.token,
|
|
2765
2765
|
},
|
|
2766
|
-
timeout:
|
|
2766
|
+
timeout: NEST_API_TIMEOUT,
|
|
2767
2767
|
},
|
|
2768
2768
|
mappedKey + '=' + value + '&uuid=' + nest_google_uuid.split('.')[1],
|
|
2769
2769
|
);
|
|
@@ -2781,7 +2781,7 @@ export default class NestAccfactory {
|
|
|
2781
2781
|
);
|
|
2782
2782
|
}
|
|
2783
2783
|
|
|
2784
|
-
if (this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.
|
|
2784
|
+
if (this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.NEST_API && nest_google_uuid.startsWith('quartz.') === false) {
|
|
2785
2785
|
// set values on other Nest devices besides cameras/doorbells
|
|
2786
2786
|
await Promise.all(
|
|
2787
2787
|
Object.entries(values)
|
|
@@ -2858,7 +2858,7 @@ export default class NestAccfactory {
|
|
|
2858
2858
|
{
|
|
2859
2859
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
2860
2860
|
headers: {
|
|
2861
|
-
'User-Agent':
|
|
2861
|
+
'User-Agent': USER_AGENT,
|
|
2862
2862
|
Authorization: 'Basic ' + this.#connections[uuid].token,
|
|
2863
2863
|
},
|
|
2864
2864
|
},
|
|
@@ -2897,7 +2897,7 @@ export default class NestAccfactory {
|
|
|
2897
2897
|
values[key] = undefined;
|
|
2898
2898
|
|
|
2899
2899
|
if (
|
|
2900
|
-
this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.
|
|
2900
|
+
this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.NEST_API &&
|
|
2901
2901
|
key === 'camera_snapshot' &&
|
|
2902
2902
|
nest_google_uuid.startsWith('quartz.') === true &&
|
|
2903
2903
|
typeof this.#rawData?.[nest_google_uuid]?.value?.nexus_api_http_server_url === 'string' &&
|
|
@@ -2911,7 +2911,7 @@ export default class NestAccfactory {
|
|
|
2911
2911
|
{
|
|
2912
2912
|
headers: {
|
|
2913
2913
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
2914
|
-
'User-Agent':
|
|
2914
|
+
'User-Agent': USER_AGENT,
|
|
2915
2915
|
[this.#connections[uuid].cameraAPI.key]:
|
|
2916
2916
|
this.#connections[uuid].cameraAPI.value + this.#connections[uuid].cameraAPI.token,
|
|
2917
2917
|
},
|
|
@@ -2931,7 +2931,7 @@ export default class NestAccfactory {
|
|
|
2931
2931
|
}
|
|
2932
2932
|
|
|
2933
2933
|
if (
|
|
2934
|
-
this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.
|
|
2934
|
+
this.#rawData?.[nest_google_uuid]?.source === DATASOURCE.PROTOBUF_API &&
|
|
2935
2935
|
this.#protobufRoot !== null &&
|
|
2936
2936
|
this.#rawData[nest_google_uuid]?.value?.device_identity?.vendorProductId !== undefined &&
|
|
2937
2937
|
key === 'camera_snapshot'
|
|
@@ -2964,7 +2964,7 @@ export default class NestAccfactory {
|
|
|
2964
2964
|
let response = await fetchWrapper('get', this.#rawData[nest_google_uuid].value.upload_live_image.liveImageUrl, {
|
|
2965
2965
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
2966
2966
|
headers: {
|
|
2967
|
-
'User-Agent':
|
|
2967
|
+
'User-Agent': USER_AGENT,
|
|
2968
2968
|
Authorization: 'Basic ' + this.#connections[uuid].token,
|
|
2969
2969
|
},
|
|
2970
2970
|
timeout: 3000,
|
|
@@ -2997,9 +2997,9 @@ export default class NestAccfactory {
|
|
|
2997
2997
|
let response = await fetchWrapper('get', this.#connections[uuid].weather_url + location, {
|
|
2998
2998
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
2999
2999
|
headers: {
|
|
3000
|
-
'User-Agent':
|
|
3000
|
+
'User-Agent': USER_AGENT,
|
|
3001
3001
|
},
|
|
3002
|
-
timeout:
|
|
3002
|
+
timeout: NEST_API_TIMEOUT,
|
|
3003
3003
|
});
|
|
3004
3004
|
|
|
3005
3005
|
let data = await response.json();
|
|
@@ -3087,7 +3087,7 @@ export default class NestAccfactory {
|
|
|
3087
3087
|
{
|
|
3088
3088
|
headers: {
|
|
3089
3089
|
referer: 'https://' + this.#connections[uuid].referer,
|
|
3090
|
-
'User-Agent':
|
|
3090
|
+
'User-Agent': USER_AGENT,
|
|
3091
3091
|
Authorization: 'Basic ' + this.#connections[uuid].token,
|
|
3092
3092
|
'Content-Type': 'application/x-protobuf',
|
|
3093
3093
|
'X-Accept-Content-Transfer-Encoding': 'binary',
|
|
@@ -3129,20 +3129,6 @@ function adjustTemperature(temperature, currentTemperatureUnit, targetTemperatur
|
|
|
3129
3129
|
return temperature;
|
|
3130
3130
|
}
|
|
3131
3131
|
|
|
3132
|
-
function makeHomeKitName(nameToMakeValid) {
|
|
3133
|
-
// Strip invalid characters to meet HomeKit naming requirements
|
|
3134
|
-
// Ensure only letters or numbers are at the beginning AND/OR end of string
|
|
3135
|
-
// Matches against uni-code characters
|
|
3136
|
-
let validHomeKitName = nameToMakeValid;
|
|
3137
|
-
if (typeof nameToMakeValid === 'string') {
|
|
3138
|
-
validHomeKitName = nameToMakeValid
|
|
3139
|
-
.replace(/[^\p{L}\p{N}\p{Z}\u2019 '.,-]/gu, '') // Remove disallowed characters
|
|
3140
|
-
.replace(/^[^\p{L}\p{N}]*/gu, '') // Trim invalid prefix
|
|
3141
|
-
.replace(/[^\p{L}\p{N}]+$/gu, ''); // Trim invalid suffix
|
|
3142
|
-
}
|
|
3143
|
-
return validHomeKitName;
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
3132
|
function crc24(valueToHash) {
|
|
3147
3133
|
const crc24HashTable = [
|
|
3148
3134
|
0x000000, 0x864cfb, 0x8ad50d, 0x0c99f6, 0x93e6e1, 0x15aa1a, 0x1933ec, 0x9f7f17, 0xa18139, 0x27cdc2, 0x2b5434, 0xad18cf, 0x3267d8,
|