@homebridge-plugins/homebridge-eufy-security 4.4.4-beta.2 → 4.4.4-beta.3

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/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const LIB_VERSION = "4.4.4-beta.2";
1
+ export const LIB_VERSION = "4.4.4-beta.3";
2
2
  //# sourceMappingURL=version.js.map
@@ -65,7 +65,7 @@ class UiServer extends HomebridgePluginUiServer {
65
65
  this.storagePath = this.homebridgeStoragePath + '/eufysecurity';
66
66
  this.storedAccessories_file = this.storagePath + '/accessories.json';
67
67
  this.unsupported_file = this.storagePath + '/unsupported.json';
68
- this.diagnosticsZipFilePath = null; // generated dynamically with timestamp
68
+ this.diagnosticsZipFilePath = null;
69
69
  this.config.persistentDir = this.storagePath;
70
70
 
71
71
  this.initLogger();
@@ -74,17 +74,7 @@ class UiServer extends HomebridgePluginUiServer {
74
74
  this.ready();
75
75
  }
76
76
 
77
- /**
78
- * Compute a unified power descriptor from a properties object.
79
- * Works for both devices and stations.
80
- * @param {object} props - the properties object (from device.getProperties() or station.getProperties())
81
- * @returns {{ source: string, icon: string, label: string, battery?: number, batteryLow?: boolean }}
82
- * source: 'battery' | 'solar' | 'plugged' | null
83
- * icon: icon filename for the UI
84
- * label: display text for the UI
85
- * battery: percentage (0-100) if available
86
- * batteryLow: true/false for simple sensors without percentage
87
- */
77
+ /** Build a unified power descriptor (source, icon, label, battery) from a properties object. */
88
78
  _computePower(props) {
89
79
  const power = { source: null, icon: null, label: null };
90
80
 
@@ -140,35 +130,35 @@ class UiServer extends HomebridgePluginUiServer {
140
130
 
141
131
  initLogger() {
142
132
  const logOptions = {
143
- name: `[UI-${LIB_VERSION}]`, // Name prefix for log messages
144
- prettyLogTemplate: '[{{mm}}/{{dd}}/{{yyyy}}, {{hh}}:{{MM}}:{{ss}}]\t{{name}}\t{{logLevelName}}\t', // Template for pretty log output
145
- prettyErrorTemplate: '\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}', // Template for pretty error output
146
- prettyErrorStackTemplate: ' • {{fileName}}\t{{method}}\n\t{{fileNameWithLine}}', // Template for error stack trace
147
- prettyErrorParentNamesSeparator: '', // Separator for parent names in error messages
148
- prettyErrorLoggerNameDelimiter: '\t', // Delimiter for logger name in error messages
149
- stylePrettyLogs: true, // Enable styling for logs
150
- minLevel: 2, // Minimum log level to display (3 corresponds to INFO)
151
- prettyLogTimeZone: 'local', // Time zone for log timestamps
152
- prettyLogStyles: { // Styles for different log elements
153
- logLevelName: { // Styles for log level names
154
- '*': ['bold', 'black', 'bgWhiteBright', 'dim'], // Default style
155
- SILLY: ['bold', 'white'], // Style for SILLY level
156
- TRACE: ['bold', 'whiteBright'], // Style for TRACE level
157
- DEBUG: ['bold', 'green'], // Style for DEBUG level
158
- INFO: ['bold', 'blue'], // Style for INFO level
159
- WARN: ['bold', 'yellow'], // Style for WARN level
160
- ERROR: ['bold', 'red'], // Style for ERROR level
161
- FATAL: ['bold', 'redBright'], // Style for FATAL level
133
+ name: `[UI-${LIB_VERSION}]`,
134
+ prettyLogTemplate: '[{{mm}}/{{dd}}/{{yyyy}}, {{hh}}:{{MM}}:{{ss}}]\t{{name}}\t{{logLevelName}}\t',
135
+ prettyErrorTemplate: '\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}',
136
+ prettyErrorStackTemplate: ' • {{fileName}}\t{{method}}\n\t{{fileNameWithLine}}',
137
+ prettyErrorParentNamesSeparator: '',
138
+ prettyErrorLoggerNameDelimiter: '\t',
139
+ stylePrettyLogs: true,
140
+ minLevel: 2,
141
+ prettyLogTimeZone: 'local',
142
+ prettyLogStyles: {
143
+ logLevelName: {
144
+ '*': ['bold', 'black', 'bgWhiteBright', 'dim'],
145
+ SILLY: ['bold', 'white'],
146
+ TRACE: ['bold', 'whiteBright'],
147
+ DEBUG: ['bold', 'green'],
148
+ INFO: ['bold', 'blue'],
149
+ WARN: ['bold', 'yellow'],
150
+ ERROR: ['bold', 'red'],
151
+ FATAL: ['bold', 'redBright'],
162
152
  },
163
- dateIsoStr: 'gray', // Style for ISO date strings
164
- filePathWithLine: 'white', // Style for file paths with line numbers
165
- name: 'green', // Style for logger names
166
- nameWithDelimiterPrefix: ['white', 'bold'], // Style for logger names with delimiter prefix
167
- nameWithDelimiterSuffix: ['white', 'bold'], // Style for logger names with delimiter suffix
168
- errorName: ['bold', 'bgRedBright', 'whiteBright'], // Style for error names
169
- fileName: ['yellow'], // Style for file names
153
+ dateIsoStr: 'gray',
154
+ filePathWithLine: 'white',
155
+ name: 'green',
156
+ nameWithDelimiterPrefix: ['white', 'bold'],
157
+ nameWithDelimiterSuffix: ['white', 'bold'],
158
+ errorName: ['bold', 'bgRedBright', 'whiteBright'],
159
+ fileName: ['yellow'],
170
160
  },
171
- maskValuesOfKeys: [ // Keys whose values should be masked in logs
161
+ maskValuesOfKeys: [
172
162
  'username',
173
163
  'password',
174
164
  'token',
@@ -248,11 +238,7 @@ class UiServer extends HomebridgePluginUiServer {
248
238
  return { ok: true };
249
239
  }
250
240
 
251
- /**
252
- * Load valid country codes from the shared countries.js file.
253
- * Parsed lazily and cached for subsequent calls.
254
- * @returns {Set<string>}
255
- */
241
+ /** Lazily load and cache valid ISO 3166-1 alpha-2 country codes from countries.js. */
256
242
  _getValidCountryCodes() {
257
243
  if (!this._validCountryCodes) {
258
244
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -310,9 +296,7 @@ class UiServer extends HomebridgePluginUiServer {
310
296
  }
311
297
 
312
298
  async login(options) {
313
- // --- Plugin heartbeat safeguard ---
314
- // If the plugin is running (accessories.json updated within the last 90s),
315
- // block login to prevent a competing eufy-security-client instance.
299
+ // Block login if the plugin is already running (accessories updated within 90s)
316
300
  if (!this.eufyClient) {
317
301
  try {
318
302
  if (fs.existsSync(this.storedAccessories_file)) {
@@ -346,22 +330,14 @@ class UiServer extends HomebridgePluginUiServer {
346
330
  }
347
331
 
348
332
  if (!this.eufyClient && options && options.username && options.password && options.country) {
349
- // Clear any pending timeouts from a previous login attempt
350
- if (this.processingTimeout) {
351
- clearTimeout(this.processingTimeout);
352
- this.processingTimeout = null;
353
- }
354
- if (this._closeTimeout) {
355
- clearTimeout(this._closeTimeout);
356
- this._closeTimeout = null;
357
- }
333
+ this._clearAllTimers();
358
334
  this.stations = [];
359
335
  this.pendingStations = [];
360
336
  this.pendingDevices = [];
361
337
  this._discoveryPhase = 'authenticating';
362
338
  this.log.debug('init eufyClient');
363
339
 
364
- // Validate country code against known list
340
+ // Validate country code
365
341
  const country = typeof options.country === 'string' ? options.country.trim().toUpperCase() : '';
366
342
  if (!this._getValidCountryCodes().has(country)) {
367
343
  const raw = typeof options.country === 'object' ? JSON.stringify(options.country) : String(options.country);
@@ -377,8 +353,8 @@ class UiServer extends HomebridgePluginUiServer {
377
353
  this.config.trustedDeviceName = options.deviceName;
378
354
  try {
379
355
  this.eufyClient = await EufySecurity.initialize(this.config, this.tsLog);
380
- this.eufyClient?.on('station added', this.addStation.bind(this));
381
- this.eufyClient?.on('device added', this.addDevice.bind(this));
356
+ this.eufyClient?.on('station added', this._onStationDiscovered.bind(this));
357
+ this.eufyClient?.on('device added', this._onDeviceDiscovered.bind(this));
382
358
  this.eufyClient?.on('push connect', () => this.log.debug('Push Connected!'));
383
359
  this.eufyClient?.on('push close', () => this.log.debug('Push Closed!'));
384
360
  this.eufyClient?.on('connect', () => this.log.debug('Connected!'));
@@ -399,7 +375,7 @@ class UiServer extends HomebridgePluginUiServer {
399
375
  if (options && options.username && options.password && options.country) {
400
376
  this.log.debug('login with credentials');
401
377
  try {
402
- this._registerAuthHandlers();
378
+ this._registerOneTimeAuthHandlers();
403
379
  this.eufyClient?.connect()
404
380
  .then(() => this.log.debug('connected?: ' + this.eufyClient?.isConnected()))
405
381
  .catch((error) => this.log.error(error));
@@ -416,7 +392,7 @@ class UiServer extends HomebridgePluginUiServer {
416
392
  message: 'Verifying TFA code...',
417
393
  });
418
394
  try {
419
- this._registerAuthHandlers();
395
+ this._registerOneTimeAuthHandlers();
420
396
  this.eufyClient?.connect({ verifyCode: options.verifyCode, force: false })
421
397
  .then(() => this.log.debug('TFA connect resolved, connected?: ' + this.eufyClient?.isConnected()))
422
398
  .catch((error) => {
@@ -436,7 +412,7 @@ class UiServer extends HomebridgePluginUiServer {
436
412
  message: 'Verifying captcha...',
437
413
  });
438
414
  try {
439
- this._registerAuthHandlers();
415
+ this._registerOneTimeAuthHandlers();
440
416
  this.eufyClient?.connect({ captcha: { captchaCode: options.captcha.captchaCode, captchaId: options.captcha.captchaId }, force: false })
441
417
  .then(() => this.log.debug('Captcha connect resolved, connected?: ' + this.eufyClient?.isConnected()))
442
418
  .catch((error) => {
@@ -457,11 +433,8 @@ class UiServer extends HomebridgePluginUiServer {
457
433
  return { pending: true };
458
434
  }
459
435
 
460
- /**
461
- * Register one-time auth outcome handlers on the eufy client.
462
- * All outcomes are delivered to the UI via push events.
463
- */
464
- _registerAuthHandlers() {
436
+ /** Register once-only auth event handlers (TFA, captcha, connect) on the eufy client. */
437
+ _registerOneTimeAuthHandlers() {
465
438
  this.eufyClient?.once('tfa request', () => {
466
439
  clearTimeout(this._loginTimeout);
467
440
  this.pushEvent('tfaRequest', {});
@@ -472,32 +445,29 @@ class UiServer extends HomebridgePluginUiServer {
472
445
  });
473
446
  this.eufyClient?.once('connect', () => {
474
447
  clearTimeout(this._loginTimeout);
448
+ if (this.adminAccountUsed) {
449
+ return;
450
+ }
475
451
  this.pushEvent('authSuccess', {});
476
452
  this.pushEvent('discoveryProgress', {
477
453
  phase: 'authenticating',
478
454
  progress: 15,
479
455
  message: 'Authenticated — waiting for devices...',
480
456
  });
481
- this._startDiscoveryInactivityTimeout();
457
+ this._startDiscoveryInactivityTimer();
482
458
  });
483
459
  }
484
460
 
485
- /**
486
- * Start the discovery inactivity timeout.
487
- * If no station or device is discovered within DISCOVERY_INACTIVITY_SEC seconds
488
- * after authentication, save the account and send an empty result to the UI.
489
- */
490
- _startDiscoveryInactivityTimeout() {
491
- // If stations or devices were already discovered before connect fired, skip
461
+ /** Start a timer that gives up on device discovery after DISCOVERY_INACTIVITY_SEC seconds. */
462
+ _startDiscoveryInactivityTimer() {
492
463
  if (this.pendingStations.length > 0 || this.pendingDevices.length > 0) {
493
464
  this.log.debug('Devices already discovered before connect event — skipping inactivity timeout');
494
465
  return;
495
466
  }
496
- this._cancelDiscoveryInactivityTimeout();
467
+ this._clearDiscoveryInactivityTimer();
497
468
  const totalSec = UiServer.DISCOVERY_INACTIVITY_SEC;
498
469
  const start = Date.now();
499
470
 
500
- // Tick every second: progress 15 → 95 during the wait, with countdown
501
471
  this._discoveryInactivityTickInterval = setInterval(() => {
502
472
  const elapsed = Math.floor((Date.now() - start) / 1000);
503
473
  const remaining = Math.max(0, totalSec - elapsed);
@@ -534,10 +504,52 @@ class UiServer extends HomebridgePluginUiServer {
534
504
  }, totalSec * 1000);
535
505
  }
536
506
 
537
- /**
538
- * Cancel the discovery inactivity timeout (called when a station or device is discovered).
539
- */
540
- _cancelDiscoveryInactivityTimeout() {
507
+ /** Return true if the station's account is the main admin (not a guest admin). */
508
+ _isMainAdminAccount(station) {
509
+ const rawStation = station.getRawStation();
510
+ return rawStation.member.member_type !== UserType.ADMIN;
511
+ }
512
+
513
+ /** Abort login: tear down client, cancel timers, notify UI, and reset plugin storage. */
514
+ _abortNonGuestAdminLogin() {
515
+ this.adminAccountUsed = true;
516
+ this._clearAllTimers();
517
+ this.eufyClient?.removeAllListeners();
518
+ this.eufyClient?.close();
519
+ this.pushEvent('AdminAccountUsed', true);
520
+ this.resetPlugin();
521
+ this.log.error(`
522
+ #########################
523
+ ######### ERROR #########
524
+ #########################
525
+ You're not using a guest admin account with this plugin! You must use a guest admin account!
526
+ Please look here for more details:
527
+ https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
528
+ #########################
529
+ `);
530
+ }
531
+
532
+ /** Clear all pending timers (login, processing, close, debounce tick, discovery inactivity). */
533
+ _clearAllTimers() {
534
+ clearTimeout(this._loginTimeout);
535
+ this._loginTimeout = null;
536
+ if (this.processingTimeout) {
537
+ clearTimeout(this.processingTimeout);
538
+ this.processingTimeout = null;
539
+ }
540
+ if (this._closeTimeout) {
541
+ clearTimeout(this._closeTimeout);
542
+ this._closeTimeout = null;
543
+ }
544
+ if (this._debounceTickInterval) {
545
+ clearInterval(this._debounceTickInterval);
546
+ this._debounceTickInterval = null;
547
+ }
548
+ this._clearDiscoveryInactivityTimer();
549
+ }
550
+
551
+ /** Clear the post-auth discovery inactivity timer. */
552
+ _clearDiscoveryInactivityTimer() {
541
553
  if (this._discoveryInactivityTickInterval) {
542
554
  clearInterval(this._discoveryInactivityTickInterval);
543
555
  this._discoveryInactivityTickInterval = null;
@@ -548,11 +560,7 @@ class UiServer extends HomebridgePluginUiServer {
548
560
  }
549
561
  }
550
562
 
551
- /**
552
- * Parse a semver string into [major, minor, patch].
553
- * @param {string} ver - e.g. '4.4.2-beta.18'
554
- * @returns {number[]}
555
- */
563
+ /** Parse a semver string (e.g. '4.4.2-beta.18') into [major, minor, patch]. */
556
564
  _parseSemver(ver) {
557
565
  return (ver || '0.0.0').replace(/-.*$/, '').split('.').map(Number);
558
566
  }
@@ -613,27 +621,17 @@ class UiServer extends HomebridgePluginUiServer {
613
621
  return new Promise(resolve => setTimeout(resolve, ms));
614
622
  }
615
623
 
616
- async addStation(station) {
617
- // Check if creds are guest admin
618
- const rawStation = station.getRawStation();
619
- if (rawStation.member.member_type !== UserType.ADMIN) {
620
- this.adminAccountUsed = true;
621
- this.eufyClient?.close();
622
- this.pushEvent('AdminAccountUsed', true);
623
- this.resetPlugin();
624
- this.log.error(`
625
- #########################
626
- ######### ERROR #########
627
- #########################
628
- You're not using a guest admin account with this plugin! You must use a guest admin account!
629
- Please look here for more details:
630
- https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
631
- #########################
632
- `);
624
+ async _onStationDiscovered(station) {
625
+ if (this.adminAccountUsed) {
633
626
  return;
634
627
  }
635
628
 
636
- this._cancelDiscoveryInactivityTimeout();
629
+ if (this._isMainAdminAccount(station)) {
630
+ this._abortNonGuestAdminLogin();
631
+ return;
632
+ }
633
+
634
+ this._clearDiscoveryInactivityTimer();
637
635
  this.pendingStations.push(station);
638
636
  this.log.debug(`${station.getName()}: Station queued for processing`);
639
637
  this._discoveryPhase = 'queuing';
@@ -644,10 +642,10 @@ class UiServer extends HomebridgePluginUiServer {
644
642
  devices: this.pendingDevices.length,
645
643
  message: `Discovered ${this.pendingStations.length} station(s), ${this.pendingDevices.length} device(s)...`,
646
644
  });
647
- this.resetDiscoveryDebounce();
645
+ this._restartDiscoveryDebounce();
648
646
  }
649
647
 
650
- async addDevice(device) {
648
+ async _onDeviceDiscovered(device) {
651
649
  if (this.adminAccountUsed) {
652
650
  this.pushEvent('AdminAccountUsed', true);
653
651
  return;
@@ -659,7 +657,7 @@ class UiServer extends HomebridgePluginUiServer {
659
657
  return;
660
658
  }
661
659
 
662
- this._cancelDiscoveryInactivityTimeout();
660
+ this._clearDiscoveryInactivityTimer();
663
661
  this.pendingDevices.push(device);
664
662
  this.log.debug(`${device.getName()}: Device queued for processing`);
665
663
  this._discoveryPhase = 'queuing';
@@ -670,24 +668,12 @@ class UiServer extends HomebridgePluginUiServer {
670
668
  devices: this.pendingDevices.length,
671
669
  message: `Discovered ${this.pendingStations.length} station(s), ${this.pendingDevices.length} device(s)...`,
672
670
  });
673
- this.resetDiscoveryDebounce();
671
+ this._restartDiscoveryDebounce();
674
672
  }
675
673
 
676
- /**
677
- * Resets the discovery debounce timer.
678
- * Each time a station or device is emitted, the timer restarts.
679
- * Processing begins once no new events arrive for DISCOVERY_DEBOUNCE_SEC seconds.
680
- */
681
- resetDiscoveryDebounce() {
682
- if (this.processingTimeout) {
683
- clearTimeout(this.processingTimeout);
684
- }
685
- if (this._closeTimeout) {
686
- clearTimeout(this._closeTimeout);
687
- }
688
- if (this._debounceTickInterval) {
689
- clearInterval(this._debounceTickInterval);
690
- }
674
+ /** Restart the debounce timer — processing fires after DISCOVERY_DEBOUNCE_SEC of silence. */
675
+ _restartDiscoveryDebounce() {
676
+ this._clearAllTimers();
691
677
  const delaySec = UiServer.DISCOVERY_DEBOUNCE_SEC;
692
678
  this.log.debug(
693
679
  `Discovery debounce reset — will process in ${delaySec}s if no more devices arrive ` +
@@ -712,7 +698,7 @@ class UiServer extends HomebridgePluginUiServer {
712
698
  this.processingTimeout = setTimeout(() => {
713
699
  clearInterval(this._debounceTickInterval);
714
700
  this._debounceTickInterval = null;
715
- this.processPendingAccessories().catch(error => this.log.error('Error processing pending accessories:', error));
701
+ this._processPendingAccessories().catch(error => this.log.error('Error processing pending accessories:', error));
716
702
  }, delaySec * 1000);
717
703
  // Close connection after processing + potential 2-min unsupported intel wait
718
704
  const closeAfterSec = delaySec + (UNSUPPORTED_INTEL_WAIT_MS / 1000) + 15;
@@ -722,7 +708,8 @@ class UiServer extends HomebridgePluginUiServer {
722
708
  }, closeAfterSec * 1000);
723
709
  }
724
710
 
725
- async processPendingAccessories() {
711
+ /** Process all queued stations and devices after the debounce window closes. */
712
+ async _processPendingAccessories() {
726
713
  this.log.debug(`Processing ${this.pendingStations.length} stations and ${this.pendingDevices.length} devices`);
727
714
 
728
715
  this._discoveryPhase = 'processing';
@@ -741,9 +728,7 @@ class UiServer extends HomebridgePluginUiServer {
741
728
  );
742
729
  }
743
730
 
744
- // --- Collect unsupported items (stations + devices) upfront ---
745
- // Hub/base stations (type 0, HB3, etc.) are not in DeviceProperties so
746
- // Device.isSupported() returns false for them — exclude known station types.
731
+ // Collect unsupported items (exclude hub/base station types that aren't in DeviceProperties)
747
732
  const unsupportedItems = [];
748
733
 
749
734
  for (const station of this.pendingStations) {
@@ -758,7 +743,7 @@ class UiServer extends HomebridgePluginUiServer {
758
743
  } catch (e) { /* ignore */ }
759
744
  }
760
745
 
761
- // If unsupported items exist, notify UI and wait (user can skip via /skipIntelWait)
746
+ // Wait for raw data on unsupported items (user can skip via /skipIntelWait)
762
747
  if (unsupportedItems.length > 0) {
763
748
  const names = unsupportedItems.map(i => `${i.getName()} (type ${i.getDeviceType()})`).join(', ');
764
749
  this._skipIntelWait = false;
@@ -772,7 +757,7 @@ class UiServer extends HomebridgePluginUiServer {
772
757
 
773
758
  this.log.info(`Unsupported intel: waiting up to ${UNSUPPORTED_INTEL_WAIT_MS / 1000}s for raw data (user can skip)`);
774
759
 
775
- // Cancellable wait — check _skipIntelWait every second, ticking progress 50 → 95
760
+ // Poll every second, ticking progress 50 → 95
776
761
  const pollMs = 1000;
777
762
  let waited = 0;
778
763
  while (waited < UNSUPPORTED_INTEL_WAIT_MS && !this._skipIntelWait) {
@@ -962,6 +947,7 @@ class UiServer extends HomebridgePluginUiServer {
962
947
  this.pushEvent('addAccessory', { stations: this.stations, extendedDiscovery: unsupportedItems.length > 0 });
963
948
  }
964
949
 
950
+ /** Persist discovered stations/devices to accessories.json. */
965
951
  storeAccessories() {
966
952
  if (!fs.existsSync(this.storagePath)) {
967
953
  fs.mkdirSync(this.storagePath, { recursive: true });
@@ -971,11 +957,8 @@ class UiServer extends HomebridgePluginUiServer {
971
957
  }
972
958
 
973
959
  // ── Sensitive-field redaction ──────────────────────────────────────────────
974
- // Keys whose string values must be partially masked before persisting to
975
- // unsupported.json. Values are [keepStart, keepEnd] — the number of
976
- // characters to leave visible at the beginning and end of the string.
977
- // An empty / falsy string is left as-is so we can tell the field is blank.
978
960
 
961
+ /** Keys whose string values are partially masked before persisting to unsupported.json. */
979
962
  static SENSITIVE_KEYS = new Map([
980
963
  // Serial numbers — keep model prefix (e.g. T8170)
981
964
  ['station_sn', [5, 0]],
@@ -1065,10 +1048,7 @@ class UiServer extends HomebridgePluginUiServer {
1065
1048
 
1066
1049
  // ── Unsupported device storage ──────────────────────────────────────────
1067
1050
 
1068
- /**
1069
- * Collect raw intel for all unsupported devices/stations and write to unsupported.json.
1070
- * This data is only used by the Plugin UI for triage and diagnostics.
1071
- */
1051
+ /** Collect raw intel for unsupported devices/stations and write to unsupported.json. */
1072
1052
  storeUnsupportedDevices(pendingStations, pendingDevices) {
1073
1053
  const unsupportedEntries = [];
1074
1054
 
@@ -1096,9 +1076,7 @@ class UiServer extends HomebridgePluginUiServer {
1096
1076
  this.log.debug(`Persisted ${unsupportedEntries.length} unsupported device(s) to unsupported.json`);
1097
1077
  }
1098
1078
 
1099
- /**
1100
- * Build a triage-ready intel object for an unsupported device.
1101
- */
1079
+ /** Build a triage-ready intel object for an unsupported device. */
1102
1080
  _buildUnsupportedDeviceEntry(device) {
1103
1081
  const rawDevice = device.getRawDevice ? device.getRawDevice() : {};
1104
1082
  const rawProps = device.getRawProperties ? device.getRawProperties() : {};
@@ -1122,9 +1100,7 @@ class UiServer extends HomebridgePluginUiServer {
1122
1100
  };
1123
1101
  }
1124
1102
 
1125
- /**
1126
- * Build a triage-ready intel object for an unsupported standalone station.
1127
- */
1103
+ /** Build a triage-ready intel object for an unsupported standalone station. */
1128
1104
  _buildUnsupportedStationEntry(station) {
1129
1105
  const rawStation = station.getRawStation ? station.getRawStation() : {};
1130
1106
  const rawProps = station.getRawProperties ? station.getRawProperties() : {};
@@ -1148,9 +1124,7 @@ class UiServer extends HomebridgePluginUiServer {
1148
1124
  };
1149
1125
  }
1150
1126
 
1151
- /**
1152
- * Load unsupported device intel from disk.
1153
- */
1127
+ /** Load unsupported device intel from disk. */
1154
1128
  async loadUnsupportedDevices() {
1155
1129
  try {
1156
1130
  if (!fs.existsSync(this.unsupported_file)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Homebridge Eufy Security",
3
3
  "name": "@homebridge-plugins/homebridge-eufy-security",
4
- "version": "4.4.4-beta.2",
4
+ "version": "4.4.4-beta.3",
5
5
  "description": "Control Eufy Security from homebridge.",
6
6
  "type": "module",
7
7
  "license": "Apache-2.0",