homebridge-nest-accfactory 0.3.0 → 0.3.2

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/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 2025.06.10
8
+ // Code version 2025.07.30
9
9
  // Mark Hulskamp
10
10
  'use strict';
11
11
 
@@ -19,17 +19,16 @@ import fs from 'node:fs';
19
19
  import path from 'node:path';
20
20
  import tls from 'tls';
21
21
  import crypto from 'crypto';
22
- import { fileURLToPath } from 'node:url';
23
22
 
24
23
  // Define our modules
25
24
  import Streamer from './streamer.js';
26
25
 
27
26
  // Define constants
28
- const PINGINTERVAL = 15000; // Ping interval to nexus server while stream active
29
- const USERAGENT = 'Nest/5.78.0 (iOScom.nestlabs.jasper.release) os=18.0'; // User Agent string
30
- const __dirname = path.dirname(fileURLToPath(import.meta.url)); // Make a defined for JS __dirname
27
+ import { USER_AGENT, __dirname } from './consts.js';
31
28
 
32
- const PacketType = {
29
+ const PING_INTERVAL = 15000; // Ping interval to nexus server while stream active
30
+
31
+ const PACKET_TYPE = {
33
32
  PING: 1,
34
33
  HELLO: 100,
35
34
  PING_CAMERA: 101,
@@ -56,19 +55,17 @@ const PacketType = {
56
55
  };
57
56
 
58
57
  // Blank audio in AAC format, mono channel @48000
59
- const AACMONO48000BLANK = Buffer.from([
58
+ const AAC_MONO_48000_BLANK = Buffer.from([
60
59
  0xff, 0xf1, 0x4c, 0x40, 0x03, 0x9f, 0xfc, 0xde, 0x02, 0x00, 0x4c, 0x61, 0x76, 0x63, 0x35, 0x39, 0x2e, 0x31, 0x38, 0x2e, 0x31, 0x30, 0x30,
61
60
  0x00, 0x02, 0x30, 0x40, 0x0e,
62
61
  ]);
63
62
 
64
63
  // nexusTalk object
65
64
  export default class NexusTalk extends Streamer {
65
+ streaming_host = undefined; // Main nexustalk streaming host
66
66
  token = undefined;
67
- tokenType = undefined;
68
- pingTimer = undefined; // Timer object for ping interval
69
- stalledTimer = undefined; // Timer object for no received data
70
- host = ''; // Host to connect to or connected too
71
- blankAudio = AACMONO48000BLANK;
67
+ useGoogleAuth = false; // Nest vs google auth
68
+ blankAudio = AAC_MONO_48000_BLANK;
72
69
  video = {}; // Video stream details once connected
73
70
  audio = {}; // Audio stream details once connected
74
71
 
@@ -79,11 +76,23 @@ export default class NexusTalk extends Streamer {
79
76
  #messages = []; // Incoming messages
80
77
  #authorised = false; // Have we been authorised
81
78
  #id = undefined; // Session ID
79
+ #host = undefined; // Current host connected to
80
+ #pingTimer = undefined; // Timer object for ping interval
81
+ #stalledTimer = undefined; // Timer object for no received data
82
+
83
+ // Codecs being used for video, audio and talking
84
+ get codecs() {
85
+ return {
86
+ video: Streamer.CODEC_TYPE.H264, // Video codec
87
+ audio: Streamer.CODEC_TYPE.AAC, // Audio codec
88
+ talkback: Streamer.CODEC_TYPE.SPEEX, // Talkback codec
89
+ };
90
+ }
82
91
 
83
- constructor(deviceData, options) {
84
- super(deviceData, options);
92
+ constructor(uuid, deviceData, options) {
93
+ super(uuid, deviceData, options);
85
94
 
86
- if (fs.existsSync(path.resolve(__dirname + '/protobuf/nest/nexustalk.proto')) === true) {
95
+ if (fs.existsSync(path.join(__dirname, 'protobuf/nest/nexustalk.proto')) === true) {
87
96
  protobuf.util.Long = null;
88
97
  protobuf.configure();
89
98
  this.#protobufNexusTalk = protobuf.loadSync(path.resolve(__dirname + '/protobuf/nest/nexustalk.proto'));
@@ -91,85 +100,86 @@ export default class NexusTalk extends Streamer {
91
100
 
92
101
  // Store data we need from the device data passed it
93
102
  this.token = deviceData?.apiAccess?.token;
94
- this.tokenType = deviceData?.apiAccess?.oauth2 !== undefined ? 'google' : 'nest';
95
- this.host = deviceData?.streaming_host; // Host we'll connect to
96
-
97
- // Set our streamer codec types
98
- this.codecs = {
99
- video: 'h264',
100
- audio: 'aac',
101
- talk: 'speex',
102
- };
103
-
104
- // If specified option to start buffering, kick off
105
- if (options?.buffer === true) {
106
- this.startBuffering();
107
- }
103
+ this.streaming_host = deviceData?.streaming_host; // Host we'll connect to
104
+ this.useGoogleAuth = typeof deviceData?.apiAccess?.oauth2 === 'string' && deviceData?.apiAccess?.oauth2 !== '';
108
105
  }
109
106
 
110
107
  // Class functions
111
- connect(host) {
108
+ async connect(host) {
112
109
  // Clear any timers we have running
113
- clearInterval(this.pingTimer);
114
- clearTimeout(this.stalledTimer);
115
- this.pingTimer = undefined;
116
- this.stalledTimer = undefined;
110
+ clearInterval(this.#pingTimer);
111
+ clearTimeout(this.#stalledTimer);
112
+ this.#pingTimer = undefined;
113
+ this.#stalledTimer = undefined;
117
114
  this.#id = undefined; // No session ID yet
118
115
 
119
116
  if (this.online === true && this.videoEnabled === true) {
120
117
  if (typeof host === 'undefined' || host === null) {
121
118
  // No host parameter passed in, so we'll set this to our internally stored host
122
- host = this.host;
119
+ host = this.streaming_host;
123
120
  }
124
121
 
125
122
  this.connected = false; // Starting connection
126
123
  this?.log?.debug?.('Connection started to "%s"', host);
124
+ this.#host = host; // Update internal host name since we’re about to connect
125
+
126
+ // Wrap tls.connect() in a Promise so we can await the TLS handshake
127
+ try {
128
+ await new Promise((resolve, reject) => {
129
+ this.#socket = tls.connect({ host: host, port: 1443 }, () => {
130
+ // Opened connection to Nexus server, so now need to authenticate ourselves
131
+ this?.log?.debug?.('Connection established to "%s"', host);
132
+
133
+ this.#socket.setKeepAlive(true); // Keep socket connection alive
134
+ this.connected = true;
135
+ this.#Authenticate(false); // Send authentication request
136
+ resolve(); // Allow await connect() to continue
137
+ });
127
138
 
128
- this.#socket = tls.connect({ host: host, port: 1443 }, () => {
129
- // Opened connection to Nexus server, so now need to authenticate ourselves
130
- this?.log?.debug?.('Connection established to "%s"', host);
131
-
132
- this.#socket.setKeepAlive(true); // Keep socket connection alive
133
- this.host = host; // update internal host name since we've connected
134
- this.connected = true;
135
- this.#Authenticate(false);
136
- });
137
-
138
- this.#socket.on('error', () => {});
139
+ this.#socket.on('error', (err) => {
140
+ // TLS error (could be refused, timeout, etc.)
141
+ this?.log?.warn?.('TLS error on connect to "%s": %s', host, err?.message || err);
142
+ this.connected = undefined;
143
+ reject(err);
144
+ });
139
145
 
140
- this.#socket.on('end', () => {});
146
+ this.#socket.on('end', () => {});
141
147
 
142
- this.#socket.on('data', (data) => {
143
- this.#handleNexusData(data);
144
- });
148
+ this.#socket.on('data', (data) => {
149
+ this.#handleNexusData(data);
150
+ });
145
151
 
146
- this.#socket.on('close', (hadError) => {
147
- this?.log?.debug?.('Connection closed to "%s"', host);
148
-
149
- clearInterval(this.pingTimer);
150
- clearTimeout(this.stalledTimer);
151
- this.pingTimer = undefined;
152
- this.stalledTimer = undefined;
153
- this.#authorised = false; // Since connection close, we can't be authorised anymore
154
- this.#socket = undefined; // Clear socket object
155
- this.connected = undefined;
156
- this.#id = undefined; // Not an active session anymore
157
-
158
- if (hadError === true && this.haveOutputs() === true) {
159
- // We still have either active buffering occuring or output streams running
160
- // so attempt to restart connection to existing host
161
- this.connect(host);
162
- }
163
- });
152
+ this.#socket.on('close', (hadError) => {
153
+ this?.log?.debug?.('Connection closed to "%s"', host);
154
+
155
+ clearInterval(this.#pingTimer);
156
+ clearTimeout(this.#stalledTimer);
157
+ this.#pingTimer = undefined;
158
+ this.#stalledTimer = undefined;
159
+ this.#authorised = false; // Since connection closed, we can't be authorised anymore
160
+ this.#socket = undefined; // Clear socket object
161
+ this.connected = undefined;
162
+ this.#id = undefined; // Not an active session anymore
163
+
164
+ if (hadError === true && (this.isStreaming() === true || this.isBuffering() === true)) {
165
+ // We still have either active buffering occurring or output streams running
166
+ // so attempt to restart connection to existing host
167
+ this.connect(host);
168
+ }
169
+ });
170
+ });
171
+ } catch (error) {
172
+ this?.log?.error?.('Failed to connect to "%s": %s', host, String(error));
173
+ }
164
174
  }
165
175
  }
166
176
 
167
- close(stopStreamFirst) {
177
+ async close(stopStreamFirst) {
168
178
  // Close an authenicated socket stream gracefully
169
179
  if (this.#socket !== undefined) {
170
180
  if (stopStreamFirst === true) {
171
181
  // Send a notifcation to nexus we're finished playback
172
- this.#stopNexusData();
182
+ await this.#stopNexusData();
173
183
  }
174
184
  this.#socket.destroy();
175
185
  }
@@ -181,15 +191,17 @@ export default class NexusTalk extends Streamer {
181
191
  this.#messages = [];
182
192
  this.video = {};
183
193
  this.audio = {};
194
+ this.#host = undefined; // No longer connected to this host
184
195
  }
185
196
 
186
- update(deviceData) {
197
+ async onUpdate(deviceData) {
187
198
  if (typeof deviceData !== 'object') {
188
199
  return;
189
200
  }
190
201
 
191
- if (deviceData.apiAccess.token !== this.token) {
202
+ if (deviceData?.apiAccess?.token !== undefined && deviceData.apiAccess.token !== this.token) {
192
203
  // access token has changed so re-authorise
204
+ this?.log?.debug?.('Access token has changed for uuid "%s". Updating token', this.nest_google_uuid);
193
205
  this.token = deviceData.apiAccess.token;
194
206
 
195
207
  if (this.#socket !== undefined) {
@@ -197,30 +209,38 @@ export default class NexusTalk extends Streamer {
197
209
  }
198
210
  }
199
211
 
200
- if (this.host !== deviceData.streaming_host) {
201
- this.host = deviceData.streaming_host;
202
- this?.log?.debug?.('New host has been requested for connection. Host requested is "%s"', this.host);
203
- }
212
+ if (deviceData?.streaming_host !== undefined && this.streaming_host !== deviceData.streaming_host) {
213
+ this.streaming_host = deviceData.streaming_host;
204
214
 
205
- // Let our parent handle the remaining updates
206
- super.update(deviceData);
215
+ if (this.isStreaming() === true || this.isBuffering() === true) {
216
+ this?.log?.debug?.('New host has been requested for connection. Host requested is "%s"', this.streaming_host);
217
+
218
+ // Setup listener for socket close event. Once socket is closed, we'll perform the redirect
219
+ this.#socket?.on?.('close', () => {
220
+ this.connect(this.streaming_host); // Connect to new host
221
+ });
222
+ this.close(true); // Close existing socket
223
+ }
224
+ }
207
225
  }
208
226
 
209
- talkingAudio(talkingData) {
227
+ sendTalkback(talkingBuffer) {
228
+ if (Buffer.isBuffer(talkingBuffer) === false || this.#protobufNexusTalk === undefined || this.#id === undefined) {
229
+ return;
230
+ }
231
+
210
232
  // Encode audio packet for sending to camera
211
- if (typeof talkingData === 'object' && this.#protobufNexusTalk !== undefined) {
212
- let TraitMap = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.AudioPayload');
213
- if (TraitMap !== null) {
214
- let encodedData = TraitMap.encode(
215
- TraitMap.fromObject({
216
- payload: talkingData,
217
- sessionId: this.#id,
218
- codec: this.codecs.talk.toUpperCase(),
219
- sampleRate: 16000,
220
- }),
221
- ).finish();
222
- this.#sendMessage(PacketType.AUDIO_PAYLOAD, encodedData);
223
- }
233
+ let TraitMap = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.AudioPayload');
234
+ if (TraitMap !== null) {
235
+ let encodedData = TraitMap.encode(
236
+ TraitMap.fromObject({
237
+ payload: talkingBuffer,
238
+ sessionId: this.#id,
239
+ codec: Streamer.CODEC_TYPE.SPEEX,
240
+ sampleRate: 16000,
241
+ }),
242
+ ).finish();
243
+ this.#sendMessage(PACKET_TYPE.AUDIO_PAYLOAD, encodedData);
224
244
  }
225
245
  }
226
246
 
@@ -248,7 +268,7 @@ export default class NexusTalk extends Streamer {
248
268
  profileNotFoundAction: 'REDIRECT',
249
269
  }),
250
270
  ).finish();
251
- this.#sendMessage(PacketType.START_PLAYBACK, encodedData);
271
+ this.#sendMessage(PACKET_TYPE.START_PLAYBACK, encodedData);
252
272
  }
253
273
  }
254
274
 
@@ -261,13 +281,13 @@ export default class NexusTalk extends Streamer {
261
281
  sessionId: this.#id,
262
282
  }),
263
283
  ).finish();
264
- this.#sendMessage(PacketType.STOP_PLAYBACK, encodedData);
284
+ this.#sendMessage(PACKET_TYPE.STOP_PLAYBACK, encodedData);
265
285
  }
266
286
  }
267
287
  }
268
288
 
269
289
  #sendMessage(type, data) {
270
- if (this.#socket?.readyState !== 'open' || (type !== PacketType.HELLO && this.#authorised === false)) {
290
+ if (this.#socket?.readyState !== 'open' || (type !== PACKET_TYPE.HELLO && this.#authorised === false)) {
271
291
  // We're not connect and/or authorised yet, so 'cache' message for processing once this occurs
272
292
  this.#messages.push({ type: type, data: data });
273
293
  return;
@@ -275,11 +295,11 @@ export default class NexusTalk extends Streamer {
275
295
 
276
296
  // Create nexusTalk message header
277
297
  let header = Buffer.alloc(3);
278
- if (type !== PacketType.LONG_PLAYBACK_PACKET) {
298
+ if (type !== PACKET_TYPE.LONG_PLAYBACK_PACKET) {
279
299
  header.writeUInt8(type, 0);
280
300
  header.writeUInt16BE(data.length, 1);
281
301
  }
282
- if (type === PacketType.LONG_PLAYBACK_PACKET) {
302
+ if (type === PACKET_TYPE.LONG_PLAYBACK_PACKET) {
283
303
  header = Buffer.alloc(5);
284
304
  header.writeUInt8(type, 0);
285
305
  header.writeUInt32BE(data.length, 1);
@@ -300,36 +320,34 @@ export default class NexusTalk extends Streamer {
300
320
  let TraitMap = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.AuthoriseRequest');
301
321
  if (TraitMap !== null) {
302
322
  authoriseRequest = TraitMap.encode(
303
- TraitMap.fromObject(
304
- this.tokenType === 'nest' ? { sessionToken: this.token } : this.tokenType === 'google' ? { oliveToken: this.token } : {},
305
- ),
323
+ TraitMap.fromObject(this.useGoogleAuth === true ? { oliveToken: this.token } : { sessionToken: this.token }),
306
324
  ).finish();
307
325
  }
308
326
 
309
327
  if (reauthorise === true && authoriseRequest !== null) {
310
328
  // Request to re-authorise only
311
- this?.log?.debug?.('Re-authentication requested to "%s"', this.host);
312
- this.#sendMessage(PacketType.AUTHORIZE_REQUEST, authoriseRequest);
329
+ this?.log?.debug?.('Re-authentication requested to "%s"', this.#host);
330
+ this.#sendMessage(PACKET_TYPE.AUTHORIZE_REQUEST, authoriseRequest);
313
331
  }
314
332
 
315
333
  if (reauthorise === false && authoriseRequest !== null) {
316
334
  // This isn't a re-authorise request, so perform 'Hello' packet
317
335
  let TraitMap = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.Hello');
318
336
  if (TraitMap !== null) {
319
- this?.log?.debug?.('Performing authentication to "%s"', this.host);
337
+ this?.log?.debug?.('Performing authentication to "%s"', this.#host);
320
338
 
321
339
  let encodedData = TraitMap.encode(
322
340
  TraitMap.fromObject({
323
341
  protocolVersion: 'VERSION_3',
324
- uuid: this.uuid.split(/[._]+/)[1],
342
+ uuid: this.nest_google_uuid.split(/[._]+/)[1],
325
343
  requireConnectedCamera: false,
326
- userAgent: USERAGENT,
344
+ USER_AGENT: USER_AGENT,
327
345
  deviceId: crypto.randomUUID(),
328
346
  clientType: 'IOS',
329
347
  authoriseRequest: authoriseRequest,
330
348
  }),
331
349
  ).finish();
332
- this.#sendMessage(PacketType.HELLO, encodedData);
350
+ this.#sendMessage(PACKET_TYPE.HELLO, encodedData);
333
351
  }
334
352
  }
335
353
  }
@@ -350,13 +368,12 @@ export default class NexusTalk extends Streamer {
350
368
  return;
351
369
  }
352
370
 
353
- this?.log?.debug?.('Redirect requested from "%s" to "%s"', this.host, redirectToHost);
371
+ this?.log?.debug?.('Redirect requested from "%s" to "%s"', this.#host, redirectToHost);
354
372
 
355
373
  // Setup listener for socket close event. Once socket is closed, we'll perform the redirect
356
- this.#socket &&
357
- this.#socket.on('close', () => {
358
- this.connect(redirectToHost); // Connect to new host
359
- });
374
+ this.#socket?.on?.('close', () => {
375
+ this.connect(redirectToHost); // Connect to new host
376
+ });
360
377
  this.close(true); // Close existing socket
361
378
  }
362
379
 
@@ -368,18 +385,17 @@ export default class NexusTalk extends Streamer {
368
385
  if (stream.codecType === this.codecs.video.toUpperCase()) {
369
386
  this.video = {
370
387
  id: stream.channelId,
371
- startTime: Date.now() + stream.startTime,
388
+ baseTimestamp: stream.startTimestamp,
389
+ baseTime: Date.now(),
372
390
  sampleRate: stream.sampleRate,
373
- timeStamp: 0,
374
391
  };
375
392
  }
376
393
  if (stream.codecType === this.codecs.audio.toUpperCase()) {
377
394
  this.audio = {
378
395
  id: stream.channelId,
379
- startTime: Date.now() + stream.startTime,
396
+ baseTimestamp: stream.startTimestamp,
397
+ baseTime: Date.now(),
380
398
  sampleRate: stream.sampleRate,
381
- timeStamp: 0,
382
- talking: false,
383
399
  };
384
400
  }
385
401
  });
@@ -389,11 +405,26 @@ export default class NexusTalk extends Streamer {
389
405
  this.#packets = [];
390
406
  this.#messages = [];
391
407
 
392
- this?.log?.debug?.('Playback started from "%s" with session ID "%s"', this.host, this.#id);
408
+ this?.log?.debug?.('Playback started from "%s" with session ID "%s"', this.#host, this.#id);
393
409
  }
394
410
  }
395
411
 
396
412
  #handlePlaybackPacket(payload) {
413
+ const calculateTimestamp = (delta, stream) => {
414
+ if (
415
+ typeof delta !== 'number' ||
416
+ typeof stream?.sampleRate !== 'number' ||
417
+ stream?.baseTime === undefined ||
418
+ stream?.baseTimestamp === undefined
419
+ ) {
420
+ return Date.now();
421
+ }
422
+
423
+ let deltaTicks = stream.baseTimestamp + delta - stream.baseTimestamp;
424
+ let deltaMs = (deltaTicks / stream.sampleRate) * 1000;
425
+ return stream.baseTime + deltaMs;
426
+ };
427
+
397
428
  // Decode playback packet
398
429
  if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
399
430
  let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.PlaybackPacket').decode(payload).toJSON();
@@ -401,30 +432,33 @@ export default class NexusTalk extends Streamer {
401
432
  // Setup up a timeout to monitor for no packets recieved in a certain period
402
433
  // If its trigger, we'll attempt to restart the stream and/or connection
403
434
  // <-- testing to see how often this occurs first
404
- clearTimeout(this.stalledTimer);
405
- this.stalledTimer = setTimeout(() => {
435
+ clearTimeout(this.#stalledTimer);
436
+ this.#stalledTimer = setTimeout(() => {
406
437
  this?.log?.debug?.(
407
438
  'We have not received any data from nexus in the past "%s" seconds for uuid "%s". Attempting restart',
408
439
  10,
409
- this.uuid,
440
+ this.nest_google_uuid,
410
441
  );
411
442
 
412
443
  // Setup listener for socket close event. Once socket is closed, we'll perform the re-connection
413
- this.#socket &&
414
- this.#socket.on('close', () => {
415
- this.connect(); // try reconnection
416
- });
444
+ this.#socket?.on?.('close', () => {
445
+ this.connect(); // try reconnection
446
+ });
417
447
  this.close(false); // Close existing socket
418
448
  }, 10000);
419
449
 
450
+ // Timestamps are rolling — incremented from startTime using timestampDelta per packet
451
+
420
452
  // Handle video packet
421
453
  if (decodedMessage?.channelId !== undefined && decodedMessage.channelId === this.video?.id) {
422
- this.addToOutput('video', Buffer.from(decodedMessage.payload, 'base64'));
454
+ let ts = calculateTimestamp(decodedMessage.timestampDelta, this.video);
455
+ this.add(Streamer.PACKET_TYPE.VIDEO, Buffer.from(decodedMessage.payload, 'base64'), ts);
423
456
  }
424
457
 
425
458
  // Handle audio packet
426
459
  if (decodedMessage?.channelId !== undefined && decodedMessage.channelId === this.audio?.id) {
427
- this.addToOutput('audio', Buffer.from(decodedMessage.payload, 'base64'));
460
+ let ts = calculateTimestamp(decodedMessage.timestampDelta, this.audio);
461
+ this.add(Streamer.PACKET_TYPE.AUDIO, Buffer.from(decodedMessage.payload, 'base64'), ts);
428
462
  }
429
463
  }
430
464
  }
@@ -436,18 +470,17 @@ export default class NexusTalk extends Streamer {
436
470
 
437
471
  if (this.#id !== undefined && decodedMessage.reason === 'USER_ENDED_SESSION') {
438
472
  // Normal playback ended ie: when we stopped playback
439
- this?.log?.debug?.('Playback ended on "%s"', this.host);
473
+ this?.log?.debug?.('Playback ended on "%s"', this.#host);
440
474
  }
441
475
 
442
476
  if (decodedMessage.reason !== 'USER_ENDED_SESSION') {
443
477
  // Error during playback, so we'll attempt to restart by reconnection to host
444
- this?.log?.debug?.('Playback ended on "%s" with error "%s". Attempting reconnection', this.host, decodedMessage.reason);
478
+ this?.log?.debug?.('Playback ended on "%s" with error "%s". Attempting reconnection', this.#host, decodedMessage.reason);
445
479
 
446
480
  // Setup listener for socket close event. Once socket is closed, we'll perform the re-connection
447
- this.#socket &&
448
- this.#socket.on('close', () => {
449
- this.connect(); // try reconnection to existing host
450
- });
481
+ this.#socket?.on?.('close', () => {
482
+ this.connect(); // try reconnection to existing host
483
+ });
451
484
  this.close(false); // Close existing socket
452
485
  }
453
486
  }
@@ -472,7 +505,7 @@ export default class NexusTalk extends Streamer {
472
505
  if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
473
506
  //let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackBegin').decode(payload).toJSON();
474
507
  this.audio.talking = true;
475
- this?.log?.debug?.('Talking started on uuid "%s"', this.uuid);
508
+ this?.log?.debug?.('Talking started on uuid "%s"', this.nest_google_uuid);
476
509
  }
477
510
  }
478
511
 
@@ -481,7 +514,7 @@ export default class NexusTalk extends Streamer {
481
514
  if (typeof payload === 'object' && this.#protobufNexusTalk !== undefined) {
482
515
  //let decodedMessage = this.#protobufNexusTalk.lookup('nest.nexustalk.v1.TalkbackEnd').decode(payload).toJSON();
483
516
  this.audio.talking = false;
484
- this?.log?.debug?.('Talking ended on uuid "%s"', this.uuid);
517
+ this?.log?.debug?.('Talking ended on uuid "%s"', this.nest_google_uuid);
485
518
  }
486
519
  }
487
520
 
@@ -494,7 +527,7 @@ export default class NexusTalk extends Streamer {
494
527
  let packetType = this.#packets.readUInt8(0);
495
528
  let packetSize = this.#packets.readUInt16BE(1);
496
529
 
497
- if (packetType === PacketType.LONG_PLAYBACK_PACKET) {
530
+ if (packetType === PACKET_TYPE.LONG_PLAYBACK_PACKET) {
498
531
  headerSize = 5;
499
532
  packetSize = this.#packets.readUInt32BE(1);
500
533
  }
@@ -509,11 +542,11 @@ export default class NexusTalk extends Streamer {
509
542
  this.#packets = this.#packets.subarray(headerSize + packetSize);
510
543
 
511
544
  switch (packetType) {
512
- case PacketType.PING: {
545
+ case PACKET_TYPE.PING: {
513
546
  break;
514
547
  }
515
548
 
516
- case PacketType.OK: {
549
+ case PACKET_TYPE.OK: {
517
550
  // process any pending messages we have stored
518
551
  this.#authorised = true; // OK message, means we're connected and authorised to Nexus
519
552
  for (let message = this.#messages.shift(); message; message = this.#messages.shift()) {
@@ -521,48 +554,48 @@ export default class NexusTalk extends Streamer {
521
554
  }
522
555
 
523
556
  // Periodically send PING message to keep stream alive
524
- clearInterval(this.pingTimer);
525
- this.pingTimer = setInterval(() => {
526
- this.#sendMessage(PacketType.PING, Buffer.alloc(0));
527
- }, PINGINTERVAL);
557
+ clearInterval(this.#pingTimer);
558
+ this.#pingTimer = setInterval(() => {
559
+ this.#sendMessage(PACKET_TYPE.PING, Buffer.alloc(0));
560
+ }, PING_INTERVAL);
528
561
 
529
562
  // Start processing data
530
563
  this.#startNexusData();
531
564
  break;
532
565
  }
533
566
 
534
- case PacketType.ERROR: {
567
+ case PACKET_TYPE.ERROR: {
535
568
  this.#handleNexusError(protoBufPayload);
536
569
  break;
537
570
  }
538
571
 
539
- case PacketType.PLAYBACK_BEGIN: {
572
+ case PACKET_TYPE.PLAYBACK_BEGIN: {
540
573
  this.#handlePlaybackBegin(protoBufPayload);
541
574
  break;
542
575
  }
543
576
 
544
- case PacketType.PLAYBACK_END: {
577
+ case PACKET_TYPE.PLAYBACK_END: {
545
578
  this.#handlePlaybackEnd(protoBufPayload);
546
579
  break;
547
580
  }
548
581
 
549
- case PacketType.PLAYBACK_PACKET:
550
- case PacketType.LONG_PLAYBACK_PACKET: {
582
+ case PACKET_TYPE.PLAYBACK_PACKET:
583
+ case PACKET_TYPE.LONG_PLAYBACK_PACKET: {
551
584
  this.#handlePlaybackPacket(protoBufPayload);
552
585
  break;
553
586
  }
554
587
 
555
- case PacketType.REDIRECT: {
588
+ case PACKET_TYPE.REDIRECT: {
556
589
  this.#handleRedirect(protoBufPayload);
557
590
  break;
558
591
  }
559
592
 
560
- case PacketType.TALKBACK_BEGIN: {
593
+ case PACKET_TYPE.TALKBACK_BEGIN: {
561
594
  this.#handleTalkbackBegin(protoBufPayload);
562
595
  break;
563
596
  }
564
597
 
565
- case PacketType.TALKBACK_END: {
598
+ case PACKET_TYPE.TALKBACK_END: {
566
599
  this.#handleTalkbackEnd(protoBufPayload);
567
600
  break;
568
601
  }