homebridge-nest-accfactory 0.0.6 → 0.2.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 +53 -4
- package/README.md +37 -19
- package/dist/HomeKitDevice.js +132 -109
- package/dist/camera.js +344 -263
- package/dist/doorbell.js +5 -3
- package/dist/floodlight.js +3 -3
- package/dist/nexustalk.js +62 -36
- package/dist/protect.js +2 -2
- package/dist/protobuf/googlehome/foyer.proto +216 -160
- package/dist/res/Nest_camera_connecting.h264 +0 -0
- package/dist/res/Nest_camera_off.h264 +0 -0
- package/dist/res/Nest_camera_offline.h264 +0 -0
- package/dist/res/Nest_camera_transfer.h264 +0 -0
- package/dist/streamer.js +78 -37
- package/dist/system.js +1321 -1263
- package/dist/thermostat.js +73 -27
- package/dist/webrtc.js +582 -0
- package/package.json +31 -29
package/dist/doorbell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Nest Doorbell(s)
|
|
2
2
|
// Part of homebridge-nest-accfactory
|
|
3
3
|
//
|
|
4
|
-
// Code version
|
|
4
|
+
// Code version 27/9/2024
|
|
5
5
|
// Mark Hulskamp
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -41,7 +41,7 @@ export default class NestDoorbell extends NestCamera {
|
|
|
41
41
|
this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet((value) => {
|
|
42
42
|
if (value !== this.deviceData.indoor_chime_enabled) {
|
|
43
43
|
// only change indoor chime status value if different than on-device
|
|
44
|
-
this.set({ indoor_chime_enabled: value });
|
|
44
|
+
this.set({ uuid: this.deviceData.nest_google_uuid, indoor_chime_enabled: value });
|
|
45
45
|
|
|
46
46
|
this?.log?.info && this.log.info('Indoor chime on "%s" was turned', this.deviceData.description, value === true ? 'on' : 'off');
|
|
47
47
|
}
|
|
@@ -66,7 +66,9 @@ export default class NestDoorbell extends NestCamera {
|
|
|
66
66
|
removeServices() {
|
|
67
67
|
super.removeServices();
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
clearTimeout(this.doorbellTimer);
|
|
70
|
+
this.doorbellTimer = undefined;
|
|
71
|
+
|
|
70
72
|
if (this.switchService !== undefined) {
|
|
71
73
|
this.accessory.removeService(this.switchService);
|
|
72
74
|
}
|
package/dist/floodlight.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Nest Cam with Floodlight
|
|
2
2
|
// Part of homebridge-nest-accfactory
|
|
3
3
|
//
|
|
4
|
-
// Code version
|
|
4
|
+
// Code version 27/9/2024
|
|
5
5
|
// Mark Hulskamp
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -38,7 +38,7 @@ export default class NestFloodlight extends NestCamera {
|
|
|
38
38
|
// Setup set callback for this light service
|
|
39
39
|
this.lightService.getCharacteristic(this.hap.Characteristic.On).onSet((value) => {
|
|
40
40
|
if (value !== this.deviceData.light_enabled) {
|
|
41
|
-
this.set({ light_enabled: value });
|
|
41
|
+
this.set({ uuid: this.deviceData.nest_google_uuid, light_enabled: value });
|
|
42
42
|
|
|
43
43
|
this?.log?.info && this.log.info('Floodlight on "%s" was turned', this.deviceData.description, value === true ? 'on' : 'off');
|
|
44
44
|
}
|
|
@@ -46,7 +46,7 @@ export default class NestFloodlight extends NestCamera {
|
|
|
46
46
|
|
|
47
47
|
this.lightService.getCharacteristic(this.hap.Characteristic.Brightness).onSet((value) => {
|
|
48
48
|
if (value !== this.deviceData.light_brightness) {
|
|
49
|
-
this.set({ light_brightness: value });
|
|
49
|
+
this.set({ uuid: this.deviceData.nest_google_uuid, light_brightness: value });
|
|
50
50
|
|
|
51
51
|
this?.log?.info && this.log.info('Floodlight brightness on "%s" was set to "%s %"', this.deviceData.description, value);
|
|
52
52
|
}
|
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
|
|
8
|
+
// Code version 29/9/2024
|
|
9
9
|
// Mark Hulskamp
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
@@ -55,15 +55,22 @@ const PacketType = {
|
|
|
55
55
|
AUTHORIZE_REQUEST: 212,
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
// Blank audio in AAC format, mono channel @48000
|
|
59
|
+
const AACMONO48000BLANK = Buffer.from([
|
|
60
|
+
0xff, 0xf1, 0x4c, 0x40, 0x03, 0x9f, 0xfc, 0xde, 0x02, 0x00, 0x4c, 0x61, 0x76, 0x63, 0x35, 0x39, 0x2e, 0x31, 0x38, 0x2e, 0x31, 0x30, 0x30,
|
|
61
|
+
0x00, 0x02, 0x30, 0x40, 0x0e,
|
|
62
|
+
]);
|
|
63
|
+
|
|
58
64
|
// nexusTalk object
|
|
59
65
|
export default class NexusTalk extends Streamer {
|
|
60
66
|
token = undefined;
|
|
61
67
|
tokenType = undefined;
|
|
62
68
|
pingTimer = undefined; // Timer object for ping interval
|
|
63
69
|
stalledTimer = undefined; // Timer object for no received data
|
|
64
|
-
video = {}; // Video stream details
|
|
65
|
-
audio = {}; // Audio stream details
|
|
66
70
|
host = ''; // Host to connect to or connected too
|
|
71
|
+
blankAudio = AACMONO48000BLANK;
|
|
72
|
+
video = {}; // Video stream details once connected
|
|
73
|
+
audio = {}; // Audio stream details once connected
|
|
67
74
|
|
|
68
75
|
// Internal data only for this class
|
|
69
76
|
#protobufNexusTalk = undefined; // Protobuf for NexusTalk
|
|
@@ -87,8 +94,15 @@ export default class NexusTalk extends Streamer {
|
|
|
87
94
|
this.tokenType = deviceData?.apiAccess?.oauth2 !== undefined ? 'google' : 'nest';
|
|
88
95
|
this.host = deviceData?.streaming_host; // Host we'll connect to
|
|
89
96
|
|
|
97
|
+
// Set our streamer codec types
|
|
98
|
+
this.codecs = {
|
|
99
|
+
video: 'h264',
|
|
100
|
+
audio: 'aac',
|
|
101
|
+
talk: 'speex',
|
|
102
|
+
};
|
|
103
|
+
|
|
90
104
|
// If specified option to start buffering, kick off
|
|
91
|
-
if (
|
|
105
|
+
if (options?.buffer === true) {
|
|
92
106
|
this.startBuffering();
|
|
93
107
|
}
|
|
94
108
|
}
|
|
@@ -96,9 +110,10 @@ export default class NexusTalk extends Streamer {
|
|
|
96
110
|
// Class functions
|
|
97
111
|
connect(host) {
|
|
98
112
|
// Clear any timers we have running
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
clearInterval(this.pingTimer);
|
|
114
|
+
clearTimeout(this.stalledTimer);
|
|
115
|
+
this.pingTimer = undefined;
|
|
116
|
+
this.stalledTimer = undefined;
|
|
102
117
|
this.#id = undefined; // No session ID yet
|
|
103
118
|
|
|
104
119
|
if (this.online === true && this.videoEnabled === true) {
|
|
@@ -107,7 +122,8 @@ export default class NexusTalk extends Streamer {
|
|
|
107
122
|
host = this.host;
|
|
108
123
|
}
|
|
109
124
|
|
|
110
|
-
this
|
|
125
|
+
this.connected = false; // Starting connection
|
|
126
|
+
this?.log?.debug && this.log.debug('Connection started to "%s"', host);
|
|
111
127
|
|
|
112
128
|
this.#socket = tls.connect({ host: host, port: 1443 }, () => {
|
|
113
129
|
// Opened connection to Nexus server, so now need to authenticate ourselves
|
|
@@ -130,11 +146,13 @@ export default class NexusTalk extends Streamer {
|
|
|
130
146
|
this.#socket.on('close', (hadError) => {
|
|
131
147
|
this?.log?.debug && this.log.debug('Connection closed to "%s"', host);
|
|
132
148
|
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
clearInterval(this.pingTimer);
|
|
150
|
+
clearTimeout(this.stalledTimer);
|
|
151
|
+
this.pingTimer = undefined;
|
|
152
|
+
this.stalledTimer = undefined;
|
|
135
153
|
this.#authorised = false; // Since connection close, we can't be authorised anymore
|
|
136
154
|
this.#socket = undefined; // Clear socket object
|
|
137
|
-
this.connected =
|
|
155
|
+
this.connected = undefined;
|
|
138
156
|
this.#id = undefined; // Not an active session anymore
|
|
139
157
|
|
|
140
158
|
if (hadError === true && this.haveOutputs() === true) {
|
|
@@ -156,11 +174,13 @@ export default class NexusTalk extends Streamer {
|
|
|
156
174
|
this.#socket.destroy();
|
|
157
175
|
}
|
|
158
176
|
|
|
159
|
-
this.connected =
|
|
177
|
+
this.connected = undefined;
|
|
160
178
|
this.#socket = undefined;
|
|
161
179
|
this.#id = undefined; // Not an active session anymore
|
|
162
180
|
this.#packets = [];
|
|
163
181
|
this.#messages = [];
|
|
182
|
+
this.video = {};
|
|
183
|
+
this.audio = {};
|
|
164
184
|
}
|
|
165
185
|
|
|
166
186
|
update(deviceData) {
|
|
@@ -195,7 +215,7 @@ export default class NexusTalk extends Streamer {
|
|
|
195
215
|
TraitMap.fromObject({
|
|
196
216
|
payload: talkingData,
|
|
197
217
|
sessionId: this.#id,
|
|
198
|
-
codec:
|
|
218
|
+
codec: this.codecs.talk.toUpperCase(),
|
|
199
219
|
sampleRate: 16000,
|
|
200
220
|
}),
|
|
201
221
|
).finish();
|
|
@@ -345,20 +365,21 @@ export default class NexusTalk extends Streamer {
|
|
|
345
365
|
let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.PlaybackBegin').decode(payload).toJSON();
|
|
346
366
|
decodedMessage.channels.forEach((stream) => {
|
|
347
367
|
// Find which channels match our video and audio streams
|
|
348
|
-
if (stream.codecType ===
|
|
368
|
+
if (stream.codecType === this.codecs.video.toUpperCase()) {
|
|
349
369
|
this.video = {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
370
|
+
id: stream.channelId,
|
|
371
|
+
startTime: Date.now() + stream.startTime,
|
|
372
|
+
sampleRate: stream.sampleRate,
|
|
373
|
+
timeStamp: 0,
|
|
354
374
|
};
|
|
355
375
|
}
|
|
356
|
-
if (stream.codecType ===
|
|
376
|
+
if (stream.codecType === this.codecs.audio.toUpperCase()) {
|
|
357
377
|
this.audio = {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
378
|
+
id: stream.channelId,
|
|
379
|
+
startTime: Date.now() + stream.startTime,
|
|
380
|
+
sampleRate: stream.sampleRate,
|
|
381
|
+
timeStamp: 0,
|
|
382
|
+
talking: false,
|
|
362
383
|
};
|
|
363
384
|
}
|
|
364
385
|
});
|
|
@@ -380,9 +401,14 @@ export default class NexusTalk extends Streamer {
|
|
|
380
401
|
// Setup up a timeout to monitor for no packets recieved in a certain period
|
|
381
402
|
// If its trigger, we'll attempt to restart the stream and/or connection
|
|
382
403
|
// <-- testing to see how often this occurs first
|
|
383
|
-
|
|
404
|
+
clearTimeout(this.stalledTimer);
|
|
384
405
|
this.stalledTimer = setTimeout(() => {
|
|
385
|
-
this?.log?.debug &&
|
|
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
|
+
);
|
|
386
412
|
|
|
387
413
|
// Setup listener for socket close event. Once socket is closed, we'll perform the re-connection
|
|
388
414
|
this.#socket &&
|
|
@@ -393,15 +419,13 @@ export default class NexusTalk extends Streamer {
|
|
|
393
419
|
}, 8000);
|
|
394
420
|
|
|
395
421
|
// Handle video packet
|
|
396
|
-
if (decodedMessage.channelId === this.video
|
|
397
|
-
this.video.
|
|
398
|
-
this.addToOutput('video', this.video.start_time + this.video.timestamp_delta, Buffer.from(decodedMessage.payload, 'base64'));
|
|
422
|
+
if (decodedMessage?.channelId !== undefined && decodedMessage.channelId === this.video?.id) {
|
|
423
|
+
this.addToOutput('video', Buffer.from(decodedMessage.payload, 'base64'));
|
|
399
424
|
}
|
|
400
425
|
|
|
401
426
|
// Handle audio packet
|
|
402
|
-
if (decodedMessage.channelId === this.audio
|
|
403
|
-
this.audio.
|
|
404
|
-
this.addToOutput('audio', this.audio.start_time + this.audio.timestamp_delta, Buffer.from(decodedMessage.payload, 'base64'));
|
|
427
|
+
if (decodedMessage?.channelId !== undefined && decodedMessage.channelId === this.audio?.id) {
|
|
428
|
+
this.addToOutput('audio', Buffer.from(decodedMessage.payload, 'base64'));
|
|
405
429
|
}
|
|
406
430
|
}
|
|
407
431
|
}
|
|
@@ -448,16 +472,18 @@ export default class NexusTalk extends Streamer {
|
|
|
448
472
|
#handleTalkbackBegin(payload) {
|
|
449
473
|
// Decode talk begin packet
|
|
450
474
|
if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
|
|
451
|
-
let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackBegin').decode(payload).toJSON();
|
|
452
|
-
this
|
|
475
|
+
//let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackBegin').decode(payload).toJSON();
|
|
476
|
+
this.audio.talking = true;
|
|
477
|
+
this?.log?.debug && this.log.debug(Streamer.TALKINGSTART, this.uuid);
|
|
453
478
|
}
|
|
454
479
|
}
|
|
455
480
|
|
|
456
481
|
#handleTalkbackEnd(payload) {
|
|
457
482
|
// Decode talk end packet
|
|
458
483
|
if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
|
|
459
|
-
let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackEnd').decode(payload).toJSON();
|
|
460
|
-
this
|
|
484
|
+
//let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackEnd').decode(payload).toJSON();
|
|
485
|
+
this.audio.talking = false;
|
|
486
|
+
this?.log?.debug && this.log.debug('Talking ended on uuid "%s"', this.uuid);
|
|
461
487
|
}
|
|
462
488
|
}
|
|
463
489
|
|
|
@@ -497,7 +523,7 @@ export default class NexusTalk extends Streamer {
|
|
|
497
523
|
}
|
|
498
524
|
|
|
499
525
|
// Periodically send PING message to keep stream alive
|
|
500
|
-
|
|
526
|
+
clearInterval(this.pingTimer);
|
|
501
527
|
this.pingTimer = setInterval(() => {
|
|
502
528
|
this.#sendMessage(PacketType.PING, Buffer.alloc(0));
|
|
503
529
|
}, PINGINTERVAL);
|
package/dist/protect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Nest Protect
|
|
2
2
|
// Part of homebridge-nest-accfactory
|
|
3
3
|
//
|
|
4
|
-
// Code version
|
|
4
|
+
// Code version 27/9/2024
|
|
5
5
|
// Mark Hulskamp
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -233,7 +233,7 @@ export default class NestProtect extends HomeKitDevice {
|
|
|
233
233
|
//this.log.info('Eve Smoke Alarm test', (EveHomeSetData.alarmtest === true ? 'start' : 'stop'));
|
|
234
234
|
}
|
|
235
235
|
if (typeof EveHomeSetData?.statusled === 'boolean') {
|
|
236
|
-
this.set({ ntp_green_led_enable: EveHomeSetData.statusled });
|
|
236
|
+
this.set({ uuid: this.deviceData.nest_google_uuid, ntp_green_led_enable: EveHomeSetData.statusled });
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
}
|