homebridge-nest-accfactory 0.2.2 → 0.2.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 CHANGED
@@ -2,6 +2,14 @@
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.2.4a (2024/12/10)
6
+
7
+ - Fix for camera video stream when audio disabled
8
+
9
+ ## v0.2.3 (2024/12/06)
10
+
11
+ - General code cleanup and bug fixes
12
+ - Fix for HomeKit Secure Video recording for migrated camera/doorbell to Google Home
5
13
 
6
14
  ## v0.2.2 (2024/10/05)
7
15
 
package/README.md CHANGED
@@ -3,16 +3,17 @@
3
3
  </p>
4
4
  <span align="center">
5
5
 
6
- # Homebridge Nest Accfactory
6
+ # Nest Accfactory
7
7
 
8
8
  [![npm](https://badgen.net/npm/v/homebridge-nest-accfactory/latest)](https://www.npmjs.com/package/homebridge-nest-accfactory)
9
9
  [![npm](https://badgen.net/npm/dt/homebridge-nest-accfactory?label=downloads)](https://www.npmjs.com/package/homebridge-nest-accfactory)
10
- [![Donate](https://badgen.net/badge/donate/paypal/yellow)](https://paypal.me/n0rt0nthec4t)
11
10
 
12
11
  </span>
13
12
 
14
13
  Formally known as [Nest_accfactory](https://github.com/n0rt0nthec4t/Nest_accfactory), this is a Homebridge plugin I have developed to allow Nest devices to be used with HomeKit including having support for HomeKit Secure Video on doorbells and camera devices
15
14
 
15
+ Building and maintaining this project takes time, effort, and resources. If you find it valuable, please consider sponsoring to support its development and future improvements. Your support makes a difference—thank you!
16
+
16
17
  ## Supported Devices
17
18
 
18
19
  The following Nest devices are known to be supported
@@ -98,6 +99,7 @@ The following options are available in the config.json options object. These app
98
99
  |-------------------|-----------------------------------------------------------------------------------------------|------------|
99
100
  | elevation | Height above sea level for the weather station | 0 |
100
101
  | eveHistory | Provide history in EveHome application where applicable | true |
102
+ | ffmegDebug | Turns on specific debugging output for when ffmpeg is envoked | false |
101
103
  | ffmegPath | Path to an ffmpeg binary for us to use. Will look in current directory by default | |
102
104
  | hksv | Enable HomeKit Secure Video for supported camera(s) and doorbell(s) | false |
103
105
  | maxStreams | Maximum number of concurrent video streams in HomeKit for supported camera(s) and doorbell(s) | 2 |
@@ -114,6 +116,7 @@ The following options are available on a per-device level in the config.json dev
114
116
  | elevation | Height above sea level for the specific weather station | 0 |
115
117
  | eveHistory | Provide history in EveHome application where applicable for the specific device | true |
116
118
  | exclude | Exclude the device | false |
119
+ | ffmegDebug | Turns on specific debugging output for when ffmpeg is envoked | false |
117
120
  | hksv | Enable HomeKit Secure Video for supported camera(s) and doorbell(s) | false |
118
121
  | humiditySensor | Create a seperate humidity sensor for supported thermostat(s) | false |
119
122
  | localAccess | Use direct access to supported camera(s) and doorbell(s) for video streaming and recording | false |
@@ -132,8 +135,10 @@ To support streaming and recording from cameras, an ffmpeg binary needs to be pr
132
135
  - libspeex
133
136
  - libopus
134
137
 
135
- By default, we look in the current directory where the plug-in excutes for an ffmpeg binary, however, you can specify a specific ffmpeg binary to use va the configuration option 'ffmpegPath'
138
+ By default, we look in the current directory where the plug-in excutes for an ffmpeg binary, however, you can specify a specific ffmpeg binary to use via the configuration option 'ffmpegPath'
139
+
140
+ ## Disclaimer
136
141
 
137
- ## Caveats
142
+ 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.
138
143
 
139
- homebridge-nest-accfactory and Nest_accfactory are both hobby projects of mine, provided as-is, with no warranty whatsoever. I've been running it successfully at my home, but your mileage might vary. If you do find an issue, please reachout and see what we can do
144
+ 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. If you do encounter any problems with the source code, feel free to reach out, and we can discuss possible solutions
@@ -19,6 +19,7 @@
19
19
  // description
20
20
  // manufacturer
21
21
  // model
22
+ // hkUsername
22
23
  // hkPairingCode
23
24
  //
24
25
  // Following constants should be overridden in the module loading this class file
@@ -34,14 +35,13 @@
34
35
  // HomeKitDevice.updateServices(deviceData)
35
36
  // HomeKitDevice.messageServices(type, message)
36
37
  //
37
- // Code version 28/9/2024
38
+ // Code version 8/10/2024
38
39
  // Mark Hulskamp
39
40
  'use strict';
40
41
 
41
42
  // Define nodejs module requirements
42
43
  import crypto from 'crypto';
43
44
  import EventEmitter from 'node:events';
44
- import { Buffer } from 'node:buffer';
45
45
 
46
46
  // Define our HomeKit device class
47
47
  export default class HomeKitDevice {
@@ -154,9 +154,11 @@ export default class HomeKitDevice {
154
154
  typeof this.deviceData?.manufacturer !== 'string' ||
155
155
  this.deviceData.manufacturer === '' ||
156
156
  (this.#platform === undefined &&
157
- typeof this.deviceData?.hkPairingCode !== 'string' &&
158
- (new RegExp(/^([0-9]{3}-[0-9]{2}-[0-9]{3})$/).test(this.deviceData.hkPairingCode) === true ||
159
- new RegExp(/^([0-9]{4}-[0-9]{4})$/).test(this.deviceData.hkPairingCode) === true))
157
+ (typeof this.deviceData?.hkPairingCode !== 'string' ||
158
+ (new RegExp(/^([0-9]{3}-[0-9]{2}-[0-9]{3})$/).test(this.deviceData.hkPairingCode) === false &&
159
+ new RegExp(/^([0-9]{4}-[0-9]{4})$/).test(this.deviceData.hkPairingCode) === false) ||
160
+ typeof this.deviceData?.hkUsername !== 'string' ||
161
+ new RegExp(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/).test(this.deviceData.hkUsername) === false))
160
162
  ) {
161
163
  return;
162
164
  }
@@ -172,13 +174,7 @@ export default class HomeKitDevice {
172
174
  // Create HAP-NodeJS libray accessory
173
175
  this.accessory = new this.hap.Accessory(accessoryName, this.uuid);
174
176
 
175
- // Create a HomeKit username for the device in format of xx:xx:xx:xx:xx:xx
176
- // Use a Nest Labs prefix for first 6 digits, followed by a CRC24 based off serial number for last 6 digits.
177
- this.accessory.username = ('18B430' + crc24(this.deviceData.serialNumber.toUpperCase()))
178
- .toString('hex')
179
- .split(/(..)/)
180
- .filter((s) => s)
181
- .join(':');
177
+ this.accessory.username = this.deviceData.hkUsername;
182
178
  this.accessory.pincode = this.deviceData.hkPairingCode;
183
179
  this.accessory.category = accessoryCategory;
184
180
  }
@@ -201,18 +197,15 @@ export default class HomeKitDevice {
201
197
  if (typeof this.addServices === 'function') {
202
198
  try {
203
199
  let postSetupDetails = await this.addServices();
204
- if (this?.log?.info) {
200
+ this?.log?.info &&
205
201
  this.log.info('Setup %s %s as "%s"', this.deviceData.manufacturer, this.deviceData.model, this.deviceData.description);
206
- if (this.historyService?.EveHome !== undefined) {
207
- this.log.info(' += EveHome support as "%s"', this.historyService.EveHome.evetype);
208
- }
209
- if (typeof postSetupDetails === 'object') {
210
- postSetupDetails.forEach((output) => {
211
- if (this?.log?.info) {
212
- this.log.info(' += %s', output);
213
- }
214
- });
215
- }
202
+ if (this.historyService?.EveHome !== undefined) {
203
+ this?.log?.info && this.log.info(' += EveHome support as "%s"', this.historyService.EveHome.evetype);
204
+ }
205
+ if (typeof postSetupDetails === 'object') {
206
+ postSetupDetails.forEach((output) => {
207
+ this?.log?.info && this.log.info(' += %s', output);
208
+ });
216
209
  }
217
210
  } catch (error) {
218
211
  this?.log?.error && this.log.error('addServices call for device "%s" failed. Error was', this.deviceData.description, error);
@@ -229,10 +222,9 @@ export default class HomeKitDevice {
229
222
  pincode: this.accessory.pincode,
230
223
  category: this.accessory.category,
231
224
  });
232
- if (this?.log?.info) {
233
- this.log.info(' += Advertising as "%s"', this.accessory.displayName);
234
- this.log.info(' += Pairing code is "%s"', this.accessory.pincode);
235
- }
225
+
226
+ this?.log?.info && this.log.info(' += Advertising as "%s"', this.accessory.displayName);
227
+ this?.log?.info && this.log.info(' += Pairing code is "%s"', this.accessory.pincode);
236
228
  }
237
229
 
238
230
  return this.accessory; // Return our HomeKit accessory
@@ -440,36 +432,3 @@ export default class HomeKitDevice {
440
432
  }
441
433
  }
442
434
  }
443
-
444
- // General helper functions which don't need to be part of an object class
445
- function crc24(valueToHash) {
446
- const crc24HashTable = [
447
- 0x000000, 0x864cfb, 0x8ad50d, 0x0c99f6, 0x93e6e1, 0x15aa1a, 0x1933ec, 0x9f7f17, 0xa18139, 0x27cdc2, 0x2b5434, 0xad18cf, 0x3267d8,
448
- 0xb42b23, 0xb8b2d5, 0x3efe2e, 0xc54e89, 0x430272, 0x4f9b84, 0xc9d77f, 0x56a868, 0xd0e493, 0xdc7d65, 0x5a319e, 0x64cfb0, 0xe2834b,
449
- 0xee1abd, 0x685646, 0xf72951, 0x7165aa, 0x7dfc5c, 0xfbb0a7, 0x0cd1e9, 0x8a9d12, 0x8604e4, 0x00481f, 0x9f3708, 0x197bf3, 0x15e205,
450
- 0x93aefe, 0xad50d0, 0x2b1c2b, 0x2785dd, 0xa1c926, 0x3eb631, 0xb8faca, 0xb4633c, 0x322fc7, 0xc99f60, 0x4fd39b, 0x434a6d, 0xc50696,
451
- 0x5a7981, 0xdc357a, 0xd0ac8c, 0x56e077, 0x681e59, 0xee52a2, 0xe2cb54, 0x6487af, 0xfbf8b8, 0x7db443, 0x712db5, 0xf7614e, 0x19a3d2,
452
- 0x9fef29, 0x9376df, 0x153a24, 0x8a4533, 0x0c09c8, 0x00903e, 0x86dcc5, 0xb822eb, 0x3e6e10, 0x32f7e6, 0xb4bb1d, 0x2bc40a, 0xad88f1,
453
- 0xa11107, 0x275dfc, 0xdced5b, 0x5aa1a0, 0x563856, 0xd074ad, 0x4f0bba, 0xc94741, 0xc5deb7, 0x43924c, 0x7d6c62, 0xfb2099, 0xf7b96f,
454
- 0x71f594, 0xee8a83, 0x68c678, 0x645f8e, 0xe21375, 0x15723b, 0x933ec0, 0x9fa736, 0x19ebcd, 0x8694da, 0x00d821, 0x0c41d7, 0x8a0d2c,
455
- 0xb4f302, 0x32bff9, 0x3e260f, 0xb86af4, 0x2715e3, 0xa15918, 0xadc0ee, 0x2b8c15, 0xd03cb2, 0x567049, 0x5ae9bf, 0xdca544, 0x43da53,
456
- 0xc596a8, 0xc90f5e, 0x4f43a5, 0x71bd8b, 0xf7f170, 0xfb6886, 0x7d247d, 0xe25b6a, 0x641791, 0x688e67, 0xeec29c, 0x3347a4, 0xb50b5f,
457
- 0xb992a9, 0x3fde52, 0xa0a145, 0x26edbe, 0x2a7448, 0xac38b3, 0x92c69d, 0x148a66, 0x181390, 0x9e5f6b, 0x01207c, 0x876c87, 0x8bf571,
458
- 0x0db98a, 0xf6092d, 0x7045d6, 0x7cdc20, 0xfa90db, 0x65efcc, 0xe3a337, 0xef3ac1, 0x69763a, 0x578814, 0xd1c4ef, 0xdd5d19, 0x5b11e2,
459
- 0xc46ef5, 0x42220e, 0x4ebbf8, 0xc8f703, 0x3f964d, 0xb9dab6, 0xb54340, 0x330fbb, 0xac70ac, 0x2a3c57, 0x26a5a1, 0xa0e95a, 0x9e1774,
460
- 0x185b8f, 0x14c279, 0x928e82, 0x0df195, 0x8bbd6e, 0x872498, 0x016863, 0xfad8c4, 0x7c943f, 0x700dc9, 0xf64132, 0x693e25, 0xef72de,
461
- 0xe3eb28, 0x65a7d3, 0x5b59fd, 0xdd1506, 0xd18cf0, 0x57c00b, 0xc8bf1c, 0x4ef3e7, 0x426a11, 0xc426ea, 0x2ae476, 0xaca88d, 0xa0317b,
462
- 0x267d80, 0xb90297, 0x3f4e6c, 0x33d79a, 0xb59b61, 0x8b654f, 0x0d29b4, 0x01b042, 0x87fcb9, 0x1883ae, 0x9ecf55, 0x9256a3, 0x141a58,
463
- 0xefaaff, 0x69e604, 0x657ff2, 0xe33309, 0x7c4c1e, 0xfa00e5, 0xf69913, 0x70d5e8, 0x4e2bc6, 0xc8673d, 0xc4fecb, 0x42b230, 0xddcd27,
464
- 0x5b81dc, 0x57182a, 0xd154d1, 0x26359f, 0xa07964, 0xace092, 0x2aac69, 0xb5d37e, 0x339f85, 0x3f0673, 0xb94a88, 0x87b4a6, 0x01f85d,
465
- 0x0d61ab, 0x8b2d50, 0x145247, 0x921ebc, 0x9e874a, 0x18cbb1, 0xe37b16, 0x6537ed, 0x69ae1b, 0xefe2e0, 0x709df7, 0xf6d10c, 0xfa48fa,
466
- 0x7c0401, 0x42fa2f, 0xc4b6d4, 0xc82f22, 0x4e63d9, 0xd11cce, 0x575035, 0x5bc9c3, 0xdd8538,
467
- ];
468
-
469
- let crc24 = 0xb704ce; // init crc24 hash;
470
- valueToHash = Buffer.from(valueToHash); // convert value into buffer for processing
471
- for (let index = 0; index < valueToHash.length; index++) {
472
- crc24 = (crc24HashTable[((crc24 >> 16) ^ valueToHash[index]) & 0xff] ^ (crc24 << 8)) & 0xffffff;
473
- }
474
- return crc24.toString(16); // return crc24 as hex string
475
- }
@@ -10,7 +10,7 @@
10
10
  //
11
11
  // Credit to https://github.com/simont77/fakegato-history for the work on starting the EveHome comms protocol decoding
12
12
  //
13
- // Version 29/8/2024
13
+ // Version 15/10/2024
14
14
  // Mark Hulskamp
15
15
 
16
16
  // Define nodejs module requirements
@@ -361,7 +361,11 @@ export default class HomeKitHistory {
361
361
  // Update types we have in history. This will just be the main type and its latest location in history
362
362
  let typeIndex = this.historyData.types.findIndex((type) => type.type === historyEntry.type && type.sub === historyEntry.sub);
363
363
  if (typeIndex === -1) {
364
- this.historyData.types.push({ type: historyEntry.type, sub: historyEntry.sub, lastEntry: this.historyData.next - 1 });
364
+ this.historyData.types.push({
365
+ type: historyEntry.type,
366
+ sub: historyEntry.sub,
367
+ lastEntry: this.historyData.next - 1,
368
+ });
365
369
  } else {
366
370
  this.historyData.types[typeIndex].lastEntry = this.historyData.next - 1;
367
371
  }
@@ -382,44 +386,36 @@ export default class HomeKitHistory {
382
386
  // returns a JSON object of all history for this service and subtype
383
387
  // handles if we've rolled over history also
384
388
  let tempHistory = [];
385
- let findUUID = null;
386
- let findSub = null;
387
- if (typeof subtype !== 'undefined' && subtype !== null) {
388
- findSub = subtype;
389
+ if (specifickey === undefined) {
390
+ specifickey = {};
389
391
  }
390
- if (typeof service !== 'object') {
392
+
393
+ if (service !== undefined && service !== '') {
391
394
  // passed in UUID byself, rather than service object
392
- findUUID = service;
395
+ specifickey.type = service;
393
396
  }
394
- if (typeof service?.UUID === 'string') {
395
- findUUID = service.UUID;
397
+
398
+ if (service?.UUID !== undefined && service.UUID !== '') {
399
+ specifickey.type = service.UUID;
396
400
  }
397
- if (typeof service.subtype === 'undefined' && typeof subtype === 'undefined') {
398
- findSub = 0;
401
+
402
+ if (service?.subtype === undefined && typeof subtype === undefined) {
403
+ specifickey.sub = subtype;
399
404
  }
400
- tempHistory = tempHistory.concat(
401
- this.historyData.data.slice(this.historyData.next, this.historyData.data.length),
402
- this.historyData.data.slice(0, this.historyData.next),
403
- );
404
- tempHistory = tempHistory.filter((historyEntry) => {
405
- if (typeof specifickey === 'object' && Object.keys(specifickey).length === 1) {
406
- // limit entry to a specifc key type value if specified
407
- if (
408
- (findSub === null && historyEntry.type === findUUID && historyEntry[Object.keys(specifickey)] === Object.values(specifickey)) ||
409
- (findSub !== null &&
410
- historyEntry.type === findUUID &&
411
- historyEntry.sub === findSub &&
412
- historyEntry[Object.keys(specifickey)] === Object.values(specifickey))
413
- ) {
414
- return historyEntry;
415
- }
416
- } else if (
417
- (findSub === null && historyEntry.type === findUUID) ||
418
- (findSub !== null && historyEntry.type === findUUID && historyEntry.sub === findSub)
419
- ) {
420
- return historyEntry;
421
- }
422
- });
405
+
406
+ if (subtype !== undefined && subtype !== null) {
407
+ specifickey.sub = subtype;
408
+ }
409
+
410
+ tempHistory = tempHistory
411
+ .concat(
412
+ this.historyData.data.slice(this.historyData.next, this.historyData.data.length),
413
+ this.historyData.data.slice(0, this.historyData.next),
414
+ )
415
+ .filter((historyEntry) => {
416
+ return Object.entries(specifickey).every(([key, value]) => historyEntry[key] === value);
417
+ });
418
+
423
419
  return tempHistory;
424
420
  }
425
421
 
@@ -428,7 +424,10 @@ export default class HomeKitHistory {
428
424
  // we get all the data for the service, ignoring the specific subtypes
429
425
  let tempHistory = this.getHistory(service, null); // all history
430
426
  if (tempHistory.length !== 0) {
431
- let writer = fs.createWriteStream(csvfile, { flags: 'w', autoClose: 'true' });
427
+ let writer = fs.createWriteStream(csvfile, {
428
+ flags: 'w',
429
+ autoClose: 'true',
430
+ });
432
431
  if (writer !== null) {
433
432
  // write header, we'll use the first record keys for the header keys
434
433
  let header = 'time,subtype';
@@ -457,27 +456,8 @@ export default class HomeKitHistory {
457
456
 
458
457
  lastHistory(service, subtype) {
459
458
  // returns the last history event for this service type and subtype
460
- let findUUID = null;
461
- let findSub = null;
462
- if (typeof subtype !== 'undefined') {
463
- findSub = subtype;
464
- }
465
- if (typeof service !== 'object') {
466
- // passed in UUID byself, rather than service object
467
- findUUID = service;
468
- }
469
- if (typeof service?.UUID === 'string') {
470
- findUUID = service.UUID;
471
- }
472
- if (typeof service.subtype === 'undefined' && typeof subtype === 'undefined') {
473
- findSub = 0;
474
- }
475
-
476
- // If subtype is 'null' find newest event based on time
477
- let typeIndex = this.historyData.types.findIndex(
478
- (type) => (type.type === findUUID && type.sub === findSub && subtype !== null) || (type.type === findUUID && subtype === null),
479
- );
480
- return typeIndex !== -1 ? this.historyData.data[this.historyData.types[typeIndex].lastEntry] : null;
459
+ let lastHistory = this.getHistory(service, subtype);
460
+ return lastHistory.length > 0 ? lastHistory?.[lastHistory.length - 1] : undefined;
481
461
  }
482
462
 
483
463
  entryCount(service, subtype, specifickey) {
@@ -501,7 +481,11 @@ export default class HomeKitHistory {
501
481
  (typeof type.sub === 'undefined' && type.type === this.historyData.data[index].type),
502
482
  ) === -1
503
483
  ) {
504
- this.historyData.types.push({ type: this.historyData.data[index].type, sub: this.historyData.data[index].sub, lastEntry: index });
484
+ this.historyData.types.push({
485
+ type: this.historyData.data[index].type,
486
+ sub: this.historyData.data[index].sub,
487
+ lastEntry: index,
488
+ });
505
489
  }
506
490
  }
507
491
  }
@@ -534,7 +518,7 @@ export default class HomeKitHistory {
534
518
  // Callbacks setup below after this is created
535
519
  let historyService = this.#createHistoryService(service, [
536
520
  this.hap.Characteristic.EveLastActivation,
537
- this.hap.Characteristic.EveOpenDuration,
521
+ this.hap.Characteristic.EveOpenedDuration,
538
522
  this.hap.Characteristic.EveTimesOpened,
539
523
  ]);
540
524
 
@@ -561,12 +545,13 @@ export default class HomeKitHistory {
561
545
  service.updateCharacteristic(
562
546
  this.hap.Characteristic.EveTimesOpened,
563
547
  this.entryCount(this.EveHome.type, this.EveHome.sub, { status: 1 }),
564
- ); // Count of entries based upon status = 1, opened
548
+ );
565
549
  service.updateCharacteristic(this.hap.Characteristic.EveLastActivation, this.#EveLastEventTime());
566
550
 
567
551
  // Setup callbacks for characteristics
568
552
  service.getCharacteristic(this.hap.Characteristic.EveTimesOpened).onGet(() => {
569
- return this.entryCount(this.EveHome.type, this.EveHome.sub, { status: 1 }); // Count of entries based upon status = 1, opened
553
+ // Count of entries based upon status = 1, opened
554
+ return this.entryCount(this.EveHome.type, this.EveHome.sub, { status: 1 });
570
555
  });
571
556
 
572
557
  service.getCharacteristic(this.hap.Characteristic.EveLastActivation).onGet(() => {
@@ -834,7 +819,10 @@ export default class HomeKitHistory {
834
819
  //let nowTemp = valHex.substr(index, 2) === '80' ? null : parseInt(valHex.substr(index, 2), 16) * 0.5;
835
820
  let ecoTemp = valHex.substr(index + 2, 2) === '80' ? null : parseInt(valHex.substr(index + 2, 2), 16) * 0.5;
836
821
  let comfortTemp = valHex.substr(index + 4, 2) === '80' ? null : parseInt(valHex.substr(index + 4, 2), 16) * 0.5;
837
- processedData.scheduleTemps = { eco: ecoTemp, comfort: comfortTemp };
822
+ processedData.scheduleTemps = {
823
+ eco: ecoTemp,
824
+ comfort: comfortTemp,
825
+ };
838
826
  index += 6;
839
827
  break;
840
828
  }
@@ -883,7 +871,11 @@ export default class HomeKitHistory {
883
871
  }
884
872
  index += 4;
885
873
  }
886
- programs.push({ id: programs.length + 1, days: DAYSOFWEEK[index2], schedule: times });
874
+ programs.push({
875
+ id: programs.length + 1,
876
+ days: DAYSOFWEEK[index2],
877
+ schedule: times,
878
+ });
887
879
  }
888
880
 
889
881
  this.EveThermoPersist.programs = programs;
@@ -1442,7 +1434,11 @@ export default class HomeKitHistory {
1442
1434
  offset: start_offset,
1443
1435
  });
1444
1436
  }
1445
- programs.push({ id: programs.length + 1, days: [], schedule: times });
1437
+ programs.push({
1438
+ id: programs.length + 1,
1439
+ days: [],
1440
+ schedule: times,
1441
+ });
1446
1442
  }
1447
1443
  index2 = index2 + 4 + scheduleSize; // Move to next program
1448
1444
  }
@@ -1756,7 +1752,7 @@ export default class HomeKitHistory {
1756
1752
  // calculate time in seconds since first event to last event. If no history we'll use the current time as the last event time
1757
1753
  let historyEntry = this.lastHistory(this.EveHome.type, this.EveHome.sub);
1758
1754
  let lastTime = Math.floor(Date.now() / 1000) - (this.EveHome.reftime + EPOCH_OFFSET);
1759
- if (historyEntry && Object.keys(historyEntry).length !== 0) {
1755
+ if (isNaN(historyEntry?.time) === false) {
1760
1756
  lastTime -= Math.floor(Date.now() / 1000) - historyEntry.time;
1761
1757
  }
1762
1758
  return lastTime;
@@ -2259,8 +2255,8 @@ export default class HomeKitHistory {
2259
2255
  '%s %s %s',
2260
2256
  numberToEveHexString(parseInt('11', 2), 2), // Field include/exclude mask
2261
2257
  numberToEveHexString(historyEntry.watts * 10, 4), // Power in watts
2262
- numberToEveHexString(historyEntry.status, 2),
2263
- ); // Power status, 1 = on, 0 = off
2258
+ numberToEveHexString(historyEntry.status, 2), // Power status, 1 = on, 0 = off
2259
+ );
2264
2260
  break;
2265
2261
  }
2266
2262