homebridge-nest-accfactory 0.2.9 → 0.3.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.md +14 -7
  3. package/config.schema.json +126 -1
  4. package/dist/HomeKitDevice.js +194 -77
  5. package/dist/HomeKitHistory.js +1 -1
  6. package/dist/config.js +207 -0
  7. package/dist/devices.js +113 -0
  8. package/dist/index.js +2 -1
  9. package/dist/nexustalk.js +19 -21
  10. package/dist/{camera.js → plugins/camera.js} +212 -239
  11. package/dist/{doorbell.js → plugins/doorbell.js} +32 -30
  12. package/dist/plugins/floodlight.js +91 -0
  13. package/dist/plugins/heatlink.js +17 -0
  14. package/dist/{protect.js → plugins/protect.js} +24 -41
  15. package/dist/{tempsensor.js → plugins/tempsensor.js} +13 -17
  16. package/dist/{thermostat.js → plugins/thermostat.js} +424 -381
  17. package/dist/{weather.js → plugins/weather.js} +26 -60
  18. package/dist/protobuf/google/trait/product/camera.proto +1 -1
  19. package/dist/protobuf/googlehome/foyer.proto +0 -46
  20. package/dist/protobuf/nest/services/apigateway.proto +31 -1
  21. package/dist/protobuf/nest/trait/firmware.proto +207 -89
  22. package/dist/protobuf/nest/trait/hvac.proto +1052 -312
  23. package/dist/protobuf/nest/trait/located.proto +51 -8
  24. package/dist/protobuf/nest/trait/network.proto +366 -36
  25. package/dist/protobuf/nest/trait/occupancy.proto +145 -17
  26. package/dist/protobuf/nest/trait/product/protect.proto +57 -43
  27. package/dist/protobuf/nest/trait/resourcedirectory.proto +8 -0
  28. package/dist/protobuf/nest/trait/sensor.proto +7 -1
  29. package/dist/protobuf/nest/trait/service.proto +3 -1
  30. package/dist/protobuf/nest/trait/structure.proto +60 -14
  31. package/dist/protobuf/nest/trait/ui.proto +41 -1
  32. package/dist/protobuf/nest/trait/user.proto +6 -1
  33. package/dist/protobuf/nest/trait/voiceassistant.proto +2 -1
  34. package/dist/protobuf/nestlabs/eventingapi/v1.proto +20 -1
  35. package/dist/protobuf/root.proto +1 -0
  36. package/dist/protobuf/wdl.proto +18 -2
  37. package/dist/protobuf/weave/common.proto +2 -1
  38. package/dist/protobuf/weave/trait/heartbeat.proto +41 -1
  39. package/dist/protobuf/weave/trait/power.proto +1 -0
  40. package/dist/protobuf/weave/trait/security.proto +10 -1
  41. package/dist/streamer.js +80 -80
  42. package/dist/system.js +1208 -1245
  43. package/dist/webrtc.js +28 -23
  44. package/package.json +12 -12
  45. package/dist/floodlight.js +0 -97
@@ -0,0 +1,113 @@
1
+ // Device support loader
2
+ // Part of homebridge-nest-accfactory
3
+ //
4
+ // Code version 2025.06.12
5
+ // Mark Hulskamp
6
+ 'use strict';
7
+
8
+ // Define nodejs module requirements
9
+ import path from 'node:path';
10
+ import fs from 'node:fs/promises';
11
+ import url from 'node:url';
12
+
13
+ // Import our modules
14
+ import HomeKitDevice from './HomeKitDevice.js';
15
+
16
+ // Define constants
17
+ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
18
+ const DeviceType = Object.freeze({
19
+ THERMOSTAT: 'Thermostat',
20
+ TEMPSENSOR: 'TemperatureSensor',
21
+ SMOKESENSOR: 'Protect',
22
+ CAMERA: 'Camera',
23
+ DOORBELL: 'Doorbell',
24
+ FLOODLIGHT: 'FloodlightCamera',
25
+ WEATHER: 'Weather',
26
+ HEATLINK: 'Heatlink',
27
+ LOCK: 'Lock',
28
+ ALARM: 'Alarm',
29
+ });
30
+
31
+ async function loadDeviceModules(log, pluginDir = '') {
32
+ let baseDir = path.join(__dirname, pluginDir);
33
+ let deviceMap = new Map();
34
+ let files = (await fs.readdir(baseDir)).sort();
35
+
36
+ for (const file of files) {
37
+ if (file.endsWith('.js') === false) {
38
+ continue;
39
+ }
40
+
41
+ try {
42
+ let module = await import(url.pathToFileURL(path.join(baseDir, file)).href);
43
+ let exportsToCheck = Object.values(module);
44
+
45
+ for (const exported of exportsToCheck) {
46
+ if (typeof exported !== 'function') {
47
+ continue;
48
+ }
49
+
50
+ let proto = Object.getPrototypeOf(exported);
51
+ while (proto !== undefined && proto.name !== '') {
52
+ if (proto === HomeKitDevice) {
53
+ if (
54
+ typeof exported.TYPE !== 'string' ||
55
+ exported.TYPE === '' ||
56
+ typeof exported.VERSION !== 'string' ||
57
+ exported.VERSION === ''
58
+ ) {
59
+ log?.warn?.('Skipping device module %s (missing TYPE or VERSION)', file);
60
+ break;
61
+ }
62
+
63
+ if (deviceMap.has(exported.TYPE) === false) {
64
+ deviceMap.set(exported.TYPE, exported);
65
+ log?.info?.('Loaded device module "%s" (v%s)', exported.TYPE, exported.VERSION);
66
+ }
67
+
68
+ break;
69
+ }
70
+
71
+ proto = Object.getPrototypeOf(proto);
72
+ }
73
+ }
74
+ } catch (error) {
75
+ log?.warn?.('Failed to load device support file "%s": %s', file, error.message);
76
+ }
77
+ }
78
+
79
+ return deviceMap;
80
+ }
81
+
82
+ function getDeviceHKCategory(type) {
83
+ let category = 1; // Categories.OTHER
84
+
85
+ if (type === DeviceType.LOCK) {
86
+ category = 6; // Categories.DOOR_LOCK
87
+ }
88
+
89
+ if (type === DeviceType.THERMOSTAT) {
90
+ category = 9; // Categories.THERMOSTAT
91
+ }
92
+
93
+ if (type === DeviceType.TEMPSENSOR || type === DeviceType.HEATLINK || type === DeviceType.SMOKESENSOR || type === DeviceType.WEATHER) {
94
+ category = 10; // Categories.SENSOR
95
+ }
96
+
97
+ if (type === DeviceType.ALARM) {
98
+ category = 11; // Categories.SECURITY_SYSTEM
99
+ }
100
+
101
+ if (type === DeviceType.CAMERA || type === DeviceType.FLOODLIGHT) {
102
+ category = 17; // Categories.IP_CAMERA
103
+ }
104
+
105
+ if (type === DeviceType.DOORBELL) {
106
+ category = 18; // Categories.VIDEO_DOORBELL
107
+ }
108
+
109
+ return category;
110
+ }
111
+
112
+ // Define exports
113
+ export { DeviceType, loadDeviceModules, getDeviceHKCategory };
package/dist/index.js CHANGED
@@ -10,13 +10,14 @@
10
10
  // Nest Temp Sensors (1st gen)
11
11
  // Nest Cameras (Cam Indoor, IQ Indoor, Outdoor, IQ Outdoor, Cam with Floodlight)
12
12
  // Nest Doorbells (wired 1st gen)
13
+ // Nest HeatLink
13
14
  //
14
15
  // The accessory supports authentication to Nest/Google using either a Nest account OR Google (migrated Nest account) account.
15
16
  // "preliminary" support for using FieldTest account types also.
16
17
  //
17
18
  // Supports both Nest REST and Protobuf APIs for communication
18
19
  //
19
- // Code version 7/10/2024
20
+ // Code version 2025.06.05
20
21
  // Mark Hulskamp
21
22
  'use strict';
22
23
 
package/dist/nexustalk.js CHANGED
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // Credit to https://github.com/Brandawg93/homebridge-nest-cam for the work on the Nest Camera comms code on which this is based
7
7
  //
8
- // Code version 20/11/2024
8
+ // Code version 2025.06.10
9
9
  // Mark Hulskamp
10
10
  'use strict';
11
11
 
@@ -123,11 +123,11 @@ export default class NexusTalk extends Streamer {
123
123
  }
124
124
 
125
125
  this.connected = false; // Starting connection
126
- this?.log?.debug && this.log.debug('Connection started to "%s"', host);
126
+ this?.log?.debug?.('Connection started to "%s"', host);
127
127
 
128
128
  this.#socket = tls.connect({ host: host, port: 1443 }, () => {
129
129
  // Opened connection to Nexus server, so now need to authenticate ourselves
130
- this?.log?.debug && this.log.debug('Connection established to "%s"', host);
130
+ this?.log?.debug?.('Connection established to "%s"', host);
131
131
 
132
132
  this.#socket.setKeepAlive(true); // Keep socket connection alive
133
133
  this.host = host; // update internal host name since we've connected
@@ -144,7 +144,7 @@ export default class NexusTalk extends Streamer {
144
144
  });
145
145
 
146
146
  this.#socket.on('close', (hadError) => {
147
- this?.log?.debug && this.log.debug('Connection closed to "%s"', host);
147
+ this?.log?.debug?.('Connection closed to "%s"', host);
148
148
 
149
149
  clearInterval(this.pingTimer);
150
150
  clearTimeout(this.stalledTimer);
@@ -199,7 +199,7 @@ export default class NexusTalk extends Streamer {
199
199
 
200
200
  if (this.host !== deviceData.streaming_host) {
201
201
  this.host = deviceData.streaming_host;
202
- this?.log?.debug && this.log.debug('New host has been requested for connection. Host requested is "%s"', this.host);
202
+ this?.log?.debug?.('New host has been requested for connection. Host requested is "%s"', this.host);
203
203
  }
204
204
 
205
205
  // Let our parent handle the remaining updates
@@ -308,7 +308,7 @@ export default class NexusTalk extends Streamer {
308
308
 
309
309
  if (reauthorise === true && authoriseRequest !== null) {
310
310
  // Request to re-authorise only
311
- this?.log?.debug && this.log.debug('Re-authentication requested to "%s"', this.host);
311
+ this?.log?.debug?.('Re-authentication requested to "%s"', this.host);
312
312
  this.#sendMessage(PacketType.AUTHORIZE_REQUEST, authoriseRequest);
313
313
  }
314
314
 
@@ -316,7 +316,7 @@ export default class NexusTalk extends Streamer {
316
316
  // This isn't a re-authorise request, so perform 'Hello' packet
317
317
  let TraitMap = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.Hello');
318
318
  if (TraitMap !== null) {
319
- this?.log?.debug && this.log.debug('Performing authentication to "%s"', this.host);
319
+ this?.log?.debug?.('Performing authentication to "%s"', this.host);
320
320
 
321
321
  let encodedData = TraitMap.encode(
322
322
  TraitMap.fromObject({
@@ -350,7 +350,7 @@ export default class NexusTalk extends Streamer {
350
350
  return;
351
351
  }
352
352
 
353
- this?.log?.debug && this.log.debug('Redirect requested from "%s" to "%s"', this.host, redirectToHost);
353
+ this?.log?.debug?.('Redirect requested from "%s" to "%s"', this.host, redirectToHost);
354
354
 
355
355
  // Setup listener for socket close event. Once socket is closed, we'll perform the redirect
356
356
  this.#socket &&
@@ -389,7 +389,7 @@ export default class NexusTalk extends Streamer {
389
389
  this.#packets = [];
390
390
  this.#messages = [];
391
391
 
392
- this?.log?.debug && this.log.debug('Playback started from "%s" with session ID "%s"', this.host, this.#id);
392
+ this?.log?.debug?.('Playback started from "%s" with session ID "%s"', this.host, this.#id);
393
393
  }
394
394
  }
395
395
 
@@ -403,12 +403,11 @@ export default class NexusTalk extends Streamer {
403
403
  // <-- testing to see how often this occurs first
404
404
  clearTimeout(this.stalledTimer);
405
405
  this.stalledTimer = setTimeout(() => {
406
- this?.log?.debug &&
407
- this.log.debug(
408
- 'We have not received any data from nexus in the past "%s" seconds for uuid "%s". Attempting restart',
409
- 10,
410
- this.uuid,
411
- );
406
+ this?.log?.debug?.(
407
+ 'We have not received any data from nexus in the past "%s" seconds for uuid "%s". Attempting restart',
408
+ 10,
409
+ this.uuid,
410
+ );
412
411
 
413
412
  // Setup listener for socket close event. Once socket is closed, we'll perform the re-connection
414
413
  this.#socket &&
@@ -437,13 +436,12 @@ export default class NexusTalk extends Streamer {
437
436
 
438
437
  if (this.#id !== undefined && decodedMessage.reason === 'USER_ENDED_SESSION') {
439
438
  // Normal playback ended ie: when we stopped playback
440
- this?.log?.debug && this.log.debug('Playback ended on "%s"', this.host);
439
+ this?.log?.debug?.('Playback ended on "%s"', this.host);
441
440
  }
442
441
 
443
442
  if (decodedMessage.reason !== 'USER_ENDED_SESSION') {
444
443
  // Error during playback, so we'll attempt to restart by reconnection to host
445
- this?.log?.debug &&
446
- this.log.debug('Playback ended on "%s" with error "%s". Attempting reconnection', this.host, decodedMessage.reason);
444
+ this?.log?.debug?.('Playback ended on "%s" with error "%s". Attempting reconnection', this.host, decodedMessage.reason);
447
445
 
448
446
  // Setup listener for socket close event. Once socket is closed, we'll perform the re-connection
449
447
  this.#socket &&
@@ -464,7 +462,7 @@ export default class NexusTalk extends Streamer {
464
462
  this.#Authenticate(true); // Update authorisation only
465
463
  } else {
466
464
  // NexusStreamer Error, packet.message contains the message
467
- this?.log?.debug && this.log.debug('Error', decodedMessage.message);
465
+ this?.log?.debug?.('Error', decodedMessage.message);
468
466
  }
469
467
  }
470
468
  }
@@ -474,7 +472,7 @@ export default class NexusTalk extends Streamer {
474
472
  if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
475
473
  //let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackBegin').decode(payload).toJSON();
476
474
  this.audio.talking = true;
477
- this?.log?.debug && this.log.debug('Talking started on uuid "%s"', this.uuid);
475
+ this?.log?.debug?.('Talking started on uuid "%s"', this.uuid);
478
476
  }
479
477
  }
480
478
 
@@ -483,7 +481,7 @@ export default class NexusTalk extends Streamer {
483
481
  if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
484
482
  //let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackEnd').decode(payload).toJSON();
485
483
  this.audio.talking = false;
486
- this?.log?.debug && this.log.debug('Talking ended on uuid "%s"', this.uuid);
484
+ this?.log?.debug?.('Talking ended on uuid "%s"', this.uuid);
487
485
  }
488
486
  }
489
487