@homebridge-plugins/homebridge-eufy-security 5.0.0-beta.3 → 5.0.0-beta.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.
@@ -39,12 +39,11 @@ const diagnosticsClose = document.querySelector('[data-diagnostics-close]');
39
39
  const diagnosticsWizardPanel = document.querySelector('[data-diagnostics-wizard]');
40
40
  const diagnosticsQuestion = document.querySelector('[data-diagnostics-question]');
41
41
  const diagnosticsQuestionText = document.querySelector('[data-diagnostics-question-text]');
42
- const diagnosticsYes = document.querySelector('[data-diagnostics-answer="yes"]');
43
- const diagnosticsNo = document.querySelector('[data-diagnostics-answer="no"]');
44
- const diagnosticsDirect = document.querySelector('[data-diagnostics-direct]');
45
- const diagnosticsDirectPanel = document.querySelector('[data-diagnostics-direct-panel]');
46
- const diagnosticsDirectChoose = document.querySelector('[data-diagnostics-direct-choose]');
47
- const diagnosticsDirectBack = document.querySelector('[data-diagnostics-direct-back]');
42
+ const diagnosticsTiles = [...document.querySelectorAll('[data-diagnostics-tile]')];
43
+ const diagnosticsDevices = document.querySelector('[data-diagnostics-devices]');
44
+ const diagnosticsDeviceList = document.querySelector('[data-diagnostics-device-list]');
45
+ const diagnosticsDevicesEvery = document.querySelector('[data-diagnostics-devices-every]');
46
+ const diagnosticsDevicesChosen = document.querySelector('[data-diagnostics-devices-chosen]');
48
47
  const diagnosticsFrequency = document.querySelector('[data-diagnostics-frequency]');
49
48
  const diagnosticsFrequencyHeading = document.querySelector('#diagnostics-frequency-heading');
50
49
  const diagnosticsFrequencyNow = document.querySelector('[data-diagnostics-frequency-answer="now"]');
@@ -52,11 +51,11 @@ const diagnosticsFrequencyIntermittent = document.querySelector('[data-diagnosti
52
51
  const diagnosticsFrequencyBack = document.querySelector('[data-diagnostics-frequency-back]');
53
52
  const diagnosticsMatch = document.querySelector('[data-diagnostics-match]');
54
53
  const diagnosticsReject = document.querySelector('[data-diagnostics-reject]');
55
- const diagnosticsProfile = document.querySelector('[data-diagnostics-profile]');
56
54
  const diagnosticsAuthorize = document.querySelector('[data-diagnostics-authorize]');
57
55
  const diagnosticsReproduction = document.querySelector('[data-diagnostics-reproduction]');
58
56
  const diagnosticsStatus = document.querySelector('[data-diagnostics-status]');
59
57
  const diagnosticsIssue = document.querySelector('[data-diagnostics-issue]');
58
+ const diagnosticsIssueHint = document.querySelector('[data-diagnostics-issue-hint]');
60
59
  const diagnosticsResult = document.querySelector('[data-diagnostics-result]');
61
60
  const diagnosticsActions = document.querySelector('[data-diagnostics-actions]');
62
61
  const diagnosticsGuidanceTitle = document.querySelector('[data-diagnostics-guidance-title]');
@@ -66,7 +65,6 @@ const diagnosticsPhaseTitle = document.querySelector('[data-diagnostics-phase-ti
66
65
  const diagnosticsGuidanceBeforeSection = document.querySelector('[data-diagnostics-guidance-before-section]');
67
66
  const diagnosticsGuidanceBefore = document.querySelector('[data-diagnostics-guidance-before]');
68
67
  const diagnosticsGuidanceAction = document.querySelector('[data-diagnostics-guidance-action]');
69
- const diagnosticsReview = document.querySelector('[data-diagnostics-review]');
70
68
  const diagnosticsManifest = document.querySelector('[data-diagnostics-manifest]');
71
69
  const diagnosticsReviewConfirm = document.querySelector('[data-diagnostics-review-confirm]');
72
70
  const diagnosticsReviewConfirmLabel = document.querySelector('[data-diagnostics-review-confirm-label]');
@@ -100,6 +98,12 @@ let challenge = '';
100
98
  let legacyNames = [];
101
99
  let legacyAcknowledged = false;
102
100
  let diagnosticsState = { status: 'inactive', missingEvidence: [] };
101
+ /** Whether the reporter already downloaded this session's archive, which is what the report offer follows. */
102
+ let diagnosticsArchiveDownloaded = false;
103
+ /** The session whose manifest is on screen, so it is fetched once rather than on every render. */
104
+ let diagnosticsReviewedCaseId = '';
105
+ /** Guards the one fetch, so a burst of renders does not assemble the manifest several times over. */
106
+ let diagnosticsReviewPending = false;
103
107
  let diagnosticsReviewId = '';
104
108
  let diagnosticsStartingAnother = false;
105
109
  let panelReturn;
@@ -258,17 +262,65 @@ function renderDiagnosticsGuidance(profile) {
258
262
  diagnosticsGuidanceAction.textContent = messages[guidance.action] ?? '';
259
263
  }
260
264
 
265
+ /**
266
+ * The devices the reporter may name, one tile each.
267
+ *
268
+ * A tile carries the device's own name above its retained image where it has one, and the icon the devices
269
+ * view uses for a device where it does not. The image is capped at half the tile: it is there to be recognised
270
+ * at a glance, and a picture that fills the tile leaves the name it belongs to competing with it.
271
+ *
272
+ * The list is asked for when it is empty rather than relying on the devices view having been opened first, and
273
+ * a request that goes unanswered leaves the step able to say every device, which is a complete answer.
274
+ */
275
+ async function renderDiagnosticsDeviceList() {
276
+ if (dashboardDevices.length === 0) {
277
+ try {
278
+ const snapshot = await requestWithinDeadline('/dashboard', { representationPreferences: {} }, 12000);
279
+ dashboardDevices = snapshot.devices ?? [];
280
+ } catch {
281
+ dashboardDevices = [];
282
+ }
283
+ }
284
+ diagnosticsDeviceList.replaceChildren();
285
+ for (const device of dashboardDevices) {
286
+ const tile = document.createElement('button');
287
+ tile.type = 'button';
288
+ tile.dataset.serial = device.serial;
289
+ tile.setAttribute('aria-pressed', 'false');
290
+ const art = document.createElement('img');
291
+ art.alt = '';
292
+ if (device.artwork) {
293
+ art.className = 'device-photo';
294
+ art.loading = 'lazy';
295
+ art.src = device.artwork;
296
+ } else {
297
+ art.src = 'assets/icons/inventory.svg';
298
+ }
299
+ const name = document.createElement('span');
300
+ name.textContent = device.name;
301
+ tile.append(art, name);
302
+ tile.addEventListener('click', () => {
303
+ tile.setAttribute('aria-pressed', tile.getAttribute('aria-pressed') === 'true' ? 'false' : 'true');
304
+ diagnosticsDevicesChosen.disabled = chosenDiagnosticsDevices().length === 0;
305
+ });
306
+ diagnosticsDeviceList.append(tile);
307
+ }
308
+ diagnosticsDevicesChosen.disabled = true;
309
+ }
310
+
311
+ /** The serials pressed in the device step, in the order the grid drew them. */
312
+ function chosenDiagnosticsDevices() {
313
+ return [...diagnosticsDeviceList.querySelectorAll('[data-serial]')]
314
+ .filter((tile) => tile.getAttribute('aria-pressed') === 'true')
315
+ .map((tile) => tile.dataset.serial);
316
+ }
317
+
261
318
  function renderDiagnosticsWizard() {
262
- diagnosticsQuestion.hidden = diagnosticsWizardState.mode !== 'questions';
263
- diagnosticsDirectPanel.hidden = diagnosticsWizardState.mode !== 'direct';
319
+ diagnosticsQuestion.hidden = diagnosticsWizardState.mode !== 'tiles';
320
+ diagnosticsDevices.hidden = diagnosticsWizardState.mode !== 'devices';
264
321
  diagnosticsFrequency.hidden = diagnosticsWizardState.mode !== 'frequency';
265
322
  diagnosticsMatch.hidden = diagnosticsWizardState.mode !== 'match';
266
- if (diagnosticsWizardState.mode === 'questions') {
267
- const question = diagnosticsWizard.questions[diagnosticsWizardState.questionIndex];
268
- diagnosticsQuestionText.textContent = messages[question.message] ?? '';
269
- }
270
323
  if (diagnosticsWizardState.mode === 'match') {
271
- diagnosticsProfile.value = diagnosticsWizardState.profile;
272
324
  renderDiagnosticsGuidance(diagnosticsWizardState.profile);
273
325
  diagnosticsModeSummary.textContent =
274
326
  messages[
@@ -279,7 +331,21 @@ function renderDiagnosticsWizard() {
279
331
  }
280
332
  }
281
333
 
334
+ /**
335
+ * The report step, reachable or not.
336
+ *
337
+ * Without an archive there is nothing to attach, so the anchor carries no `href` — it is not a link — and
338
+ * `aria-disabled` states the same thing to a reader who cannot see it greyed. The hint says why, because a
339
+ * control that cannot be used and does not explain itself reads as broken rather than as not yet.
340
+ */
341
+ function setIssueStepReachable(url) {
342
+ diagnosticsIssue.href = url;
343
+ diagnosticsIssue.setAttribute('aria-disabled', url ? 'false' : 'true');
344
+ diagnosticsIssueHint.textContent = messages[url ? 'diagnosticsIssueOpensTab' : 'diagnosticsIssueNeedsArchive'] ?? '';
345
+ }
346
+
282
347
  function renderDiagnostics(state) {
348
+ if (state.supportCaseId !== diagnosticsState.supportCaseId) diagnosticsArchiveDownloaded = false;
283
349
  diagnosticsState = state;
284
350
  const screen = diagnosticsWizard.screen(state, diagnosticsStartingAnother);
285
351
  const choosing = screen === 'choose';
@@ -307,17 +373,20 @@ function renderDiagnostics(state) {
307
373
  : state.status === 'reproducing'
308
374
  ? messages.diagnosticsNowFinish
309
375
  : messages.diagnosticsStartRecording;
310
- diagnosticsIssue.hidden = !reviewing;
311
- diagnosticsIssue.href = state.issueUrl ?? '';
376
+ diagnosticsIssue.hidden = true;
377
+ setIssueStepReachable(diagnosticsArchiveDownloaded ? (state.issueUrl ?? '') : '');
312
378
  diagnosticsResult.hidden = !reviewing;
313
- diagnosticsReview.hidden = !reviewing;
314
- diagnosticsManifest.hidden = true;
315
- diagnosticsReviewConfirmLabel.hidden = true;
316
- diagnosticsReviewConfirm.checked = false;
317
- diagnosticsExport.hidden = true;
318
- diagnosticsExport.disabled = true;
319
- diagnosticsReviewId = '';
320
- if (state.profile) diagnosticsProfile.value = state.profile;
379
+ const reviewed = reviewing && diagnosticsReviewedCaseId === (state.supportCaseId ?? '');
380
+ diagnosticsManifest.hidden = !reviewed;
381
+ diagnosticsReviewConfirmLabel.hidden = !reviewed;
382
+ diagnosticsExport.hidden = !reviewed;
383
+ diagnosticsIssue.hidden = !reviewed;
384
+ if (!reviewed) {
385
+ diagnosticsReviewConfirm.checked = false;
386
+ diagnosticsExport.disabled = true;
387
+ diagnosticsReviewId = '';
388
+ if (reviewing) void ensureArchiveReview(state.supportCaseId ?? '');
389
+ }
321
390
  if (screen === 'reproduce') {
322
391
  renderDiagnosticsGuidance(state.profile);
323
392
  if (reproductionMode === 'intermittent') {
@@ -349,6 +418,21 @@ function renderDiagnostics(state) {
349
418
  ).replace('{evidence}', state.missingEvidence?.join(', ') ?? '');
350
419
  }
351
420
 
421
+ /**
422
+ * The catalogue entry that says, in a reader's words, what a never-collected class is.
423
+ *
424
+ * A class with no entry falls back to its own identifier, which is visible rather than absent, so a newly
425
+ * declared class shows up as something to translate instead of quietly disappearing from the list.
426
+ */
427
+ const NEVER_COLLECTED_LABELS = {
428
+ 'credentials-and-authentication': 'excludedCredentials',
429
+ 'tokens-cookies-and-authorization': 'excludedTokens',
430
+ 'session-and-push-stores': 'excludedSessions',
431
+ 'private-and-symmetric-keys': 'excludedKeys',
432
+ 'unconstrained-internal-objects': 'excludedInternalData',
433
+ 'camera-images-talkback-and-raw-media': 'excludedMedia',
434
+ };
435
+
352
436
  function renderArchiveManifest(manifest) {
353
437
  diagnosticsManifest.replaceChildren();
354
438
  const summary = document.createElement('p');
@@ -366,9 +450,16 @@ function renderArchiveManifest(manifest) {
366
450
  row.textContent = `${item.evidence} · ${item.privacyClass} · ${item.status}${missing}${size}${truncated}${fields ? ` · ${messages.diagnosticsArchiveFields ?? ''} ${fields}` : ''}`;
367
451
  evidence.append(row);
368
452
  }
453
+ const detail = document.createElement('details');
454
+ const detailLabel = document.createElement('summary');
455
+ detailLabel.textContent = messages.diagnosticsArchiveDetail ?? '';
456
+ detail.append(detailLabel, summary, evidence);
369
457
  const exclusions = document.createElement('p');
370
- exclusions.textContent = `${messages.diagnosticsArchiveExcluded ?? ''} ${(manifest.excludedClasses ?? []).join(', ')}`;
371
- diagnosticsManifest.append(summary, evidence, exclusions);
458
+ const neverCollected = (manifest.excludedClasses ?? []).map(
459
+ (excluded) => messages[NEVER_COLLECTED_LABELS[excluded]] ?? excluded,
460
+ );
461
+ exclusions.textContent = `${messages.diagnosticsArchiveExcluded ?? ''} ${neverCollected.join(', ')}.`;
462
+ diagnosticsManifest.append(detail, exclusions);
372
463
  const uncovered = (manifest.evidence ?? []).filter((item) => item.coversReproduction === false);
373
464
  if (uncovered.length > 0) {
374
465
  const gap = document.createElement('p');
@@ -385,25 +476,32 @@ function renderArchiveManifest(manifest) {
385
476
  diagnosticsManifest.hidden = false;
386
477
  }
387
478
 
388
- diagnosticsReview.addEventListener('click', async () => {
389
- diagnosticsReview.disabled = true;
479
+ /**
480
+ * The manifest for the session on screen, fetched once.
481
+ *
482
+ * A completed reproduction that the reporter is looking at is a file they came for, so the manifest is
483
+ * read without being asked for. It is read once per session rather than per render, because assembling it
484
+ * reads every collected log. Exporting spends it, so the session is forgotten again and a second download
485
+ * asks for a fresh one.
486
+ */
487
+ async function ensureArchiveReview(caseId) {
488
+ if (!caseId || diagnosticsReviewedCaseId === caseId || diagnosticsReviewPending) return;
489
+ diagnosticsReviewPending = true;
390
490
  try {
391
491
  const review = await requestWithinDeadline('/diagnostics/archive/review', undefined, 12000);
392
492
  diagnosticsReviewId = review.reviewId;
493
+ diagnosticsReviewedCaseId = caseId;
393
494
  renderArchiveManifest(review.manifest);
394
495
  diagnosticsReviewConfirmLabel.hidden = false;
395
496
  diagnosticsExport.hidden = false;
497
+ diagnosticsExport.disabled = !diagnosticsReviewConfirm.checked;
498
+ diagnosticsIssue.hidden = false;
396
499
  } catch {
397
- try {
398
- renderDiagnostics(await requestWithinDeadline('/diagnostics/status', undefined, 12000));
399
- diagnosticsStatus.textContent = messages.diagnosticsFailed ?? '';
400
- } catch {
401
- diagnosticsStatus.textContent = messages.diagnosticsFailed ?? '';
402
- }
500
+ diagnosticsStatus.textContent = messages.diagnosticsFailed ?? '';
403
501
  } finally {
404
- diagnosticsReview.disabled = false;
502
+ diagnosticsReviewPending = false;
405
503
  }
406
- });
504
+ }
407
505
 
408
506
  diagnosticsReviewConfirm.addEventListener('change', () => {
409
507
  diagnosticsExport.disabled = !diagnosticsReviewConfirm.checked || !diagnosticsReviewId;
@@ -425,7 +523,10 @@ diagnosticsExport.addEventListener('click', async () => {
425
523
  download.click();
426
524
  document.body.removeChild(download);
427
525
  diagnosticsReviewId = '';
526
+ diagnosticsReviewedCaseId = '';
428
527
  diagnosticsReviewConfirm.checked = false;
528
+ diagnosticsArchiveDownloaded = true;
529
+ setIssueStepReachable(diagnosticsState.issueUrl ?? '');
429
530
  } catch {
430
531
  diagnosticsStatus.textContent = messages.diagnosticsFailed ?? '';
431
532
  }
@@ -441,6 +542,7 @@ diagnosticsAuthorize.addEventListener('click', async () => {
441
542
  {
442
543
  profile: diagnosticsWizardState.profile,
443
544
  reproductionMode: diagnosticsWizardState.reproductionMode,
545
+ ...(diagnosticsWizardState.devices === undefined ? {} : { affectedDevices: diagnosticsWizardState.devices }),
444
546
  },
445
547
  12000,
446
548
  );
@@ -465,37 +567,32 @@ diagnosticsAuthorize.addEventListener('click', async () => {
465
567
  }
466
568
  });
467
569
 
468
- diagnosticsYes.addEventListener('click', () => {
469
- diagnosticsWizardState = diagnosticsWizard.answer(diagnosticsWizardState, true);
470
- renderDiagnosticsWizard();
471
- diagnosticsFrequencyHeading.focus?.();
472
- });
473
-
474
- diagnosticsNo.addEventListener('click', () => {
475
- diagnosticsWizardState = diagnosticsWizard.answer(diagnosticsWizardState, false);
476
- renderDiagnosticsWizard();
477
- if (diagnosticsWizardState.mode === 'frequency') diagnosticsFrequencyHeading.focus?.();
478
- else diagnosticsQuestionText.focus?.();
479
- });
570
+ for (const tile of diagnosticsTiles) {
571
+ tile.addEventListener('click', async () => {
572
+ diagnosticsWizardState = diagnosticsWizard.select(diagnosticsWizardState, tile.dataset.diagnosticsTile);
573
+ renderDiagnosticsWizard();
574
+ if (diagnosticsWizardState.mode === 'devices') await renderDiagnosticsDeviceList();
575
+ else diagnosticsFrequencyHeading.focus?.();
576
+ });
577
+ }
480
578
 
481
- diagnosticsDirect.addEventListener('click', () => {
482
- diagnosticsWizardState = diagnosticsWizard.direct();
483
- renderDiagnosticsWizard();
484
- diagnosticsProfile.focus?.();
579
+ diagnosticsDevicesEvery.addEventListener('click', () => {
580
+ const tiles = [...diagnosticsDeviceList.querySelectorAll('[data-serial]')];
581
+ for (const tile of tiles) tile.setAttribute('aria-pressed', 'true');
582
+ diagnosticsDevicesChosen.disabled = tiles.length === 0;
583
+ if (tiles.length === 0) {
584
+ diagnosticsWizardState = diagnosticsWizard.chooseDevices(diagnosticsWizardState, 'all');
585
+ renderDiagnosticsWizard();
586
+ diagnosticsFrequencyHeading.focus?.();
587
+ }
485
588
  });
486
589
 
487
- diagnosticsDirectChoose.addEventListener('click', () => {
488
- diagnosticsWizardState = diagnosticsWizard.selectDirect(diagnosticsWizardState, diagnosticsProfile.value);
590
+ diagnosticsDevicesChosen.addEventListener('click', () => {
591
+ diagnosticsWizardState = diagnosticsWizard.chooseDevices(diagnosticsWizardState, chosenDiagnosticsDevices());
489
592
  renderDiagnosticsWizard();
490
593
  diagnosticsFrequencyHeading.focus?.();
491
594
  });
492
595
 
493
- diagnosticsDirectBack.addEventListener('click', () => {
494
- diagnosticsWizardState = diagnosticsWizard.start();
495
- renderDiagnosticsWizard();
496
- diagnosticsQuestionText.focus?.();
497
- });
498
-
499
596
  function chooseDiagnosticsReproductionMode(reproductionMode) {
500
597
  diagnosticsWizardState = diagnosticsWizard.chooseReproductionMode(diagnosticsWizardState, reproductionMode);
501
598
  renderDiagnosticsWizard();
@@ -508,15 +605,13 @@ diagnosticsFrequencyIntermittent.addEventListener('click', () => chooseDiagnosti
508
605
  diagnosticsFrequencyBack.addEventListener('click', () => {
509
606
  diagnosticsWizardState = diagnosticsWizard.backFromFrequency(diagnosticsWizardState);
510
607
  renderDiagnosticsWizard();
511
- if (diagnosticsWizardState.mode === 'direct') diagnosticsProfile.focus?.();
512
- else diagnosticsQuestionText.focus?.();
608
+ diagnosticsQuestionText.focus?.();
513
609
  });
514
610
 
515
611
  diagnosticsReject.addEventListener('click', () => {
516
612
  diagnosticsWizardState = diagnosticsWizard.reject(diagnosticsWizardState);
517
613
  renderDiagnosticsWizard();
518
- if (diagnosticsWizardState.mode === 'direct') diagnosticsProfile.focus?.();
519
- else diagnosticsQuestionText.focus?.();
614
+ diagnosticsQuestionText.focus?.();
520
615
  });
521
616
 
522
617
  diagnosticsStartAnother.addEventListener('click', () => {
@@ -1,37 +1,30 @@
1
1
  (function attachDiagnosticsWizard(global) {
2
- const questions = [
3
- { profile: 'dashboard-ui', message: 'diagnosticsQuestionDashboard' },
4
- { profile: 'startup-authentication', message: 'diagnosticsQuestionStartup' },
5
- { profile: 'device-representation', message: 'diagnosticsQuestionDevices' },
6
- { profile: 'control-state', message: 'diagnosticsQuestionControl' },
7
- { profile: 'live-media', message: 'diagnosticsQuestionLiveMedia' },
8
- { profile: 'hksv-recording', message: 'diagnosticsQuestionRecording' },
2
+ const profiles = [
3
+ 'startup-authentication',
4
+ 'device-representation',
5
+ 'control-state',
6
+ 'live-media',
7
+ 'hksv-recording',
8
+ 'dashboard-ui',
9
+ 'other',
9
10
  ];
10
11
 
11
- function start() {
12
- return { mode: 'questions', questionIndex: 0, profile: undefined, source: 'questions' };
13
- }
12
+ const deviceProfiles = ['device-representation', 'control-state', 'live-media', 'hksv-recording'];
14
13
 
15
- function answer(state, matches) {
16
- if (matches) {
17
- return { ...state, mode: 'frequency', profile: questions[state.questionIndex].profile };
18
- }
19
- if (state.questionIndex === questions.length - 1) {
20
- return { ...state, mode: 'frequency', profile: 'other' };
21
- }
22
- return { ...state, questionIndex: state.questionIndex + 1 };
14
+ function start() {
15
+ return { mode: 'tiles', profile: undefined };
23
16
  }
24
17
 
25
- function direct() {
26
- return { mode: 'direct', questionIndex: 0, profile: undefined, source: 'direct' };
18
+ function select(state, profile) {
19
+ return { ...state, mode: deviceProfiles.includes(profile) ? 'devices' : 'frequency', profile };
27
20
  }
28
21
 
29
- function selectDirect(state, profile) {
30
- return { ...state, mode: 'frequency', profile, source: 'direct' };
22
+ function chooseDevices(state, devices) {
23
+ return { ...state, mode: 'frequency', devices };
31
24
  }
32
25
 
33
- function reject(state) {
34
- return state.source === 'direct' ? direct() : start();
26
+ function reject() {
27
+ return start();
35
28
  }
36
29
 
37
30
  function chooseReproductionMode(state, reproductionMode) {
@@ -39,7 +32,7 @@
39
32
  }
40
33
 
41
34
  function backFromFrequency(state) {
42
- return state.source === 'direct' ? direct() : start();
35
+ return deviceProfiles.includes(state.profile) ? { ...state, mode: 'devices' } : start();
43
36
  }
44
37
 
45
38
  function screen(session, startingAnother) {
@@ -54,15 +47,15 @@
54
47
  }
55
48
 
56
49
  global.HomebridgeEufyDiagnosticsWizard = {
57
- answer,
58
50
  backFromFrequency,
59
51
  backgroundActive,
52
+ chooseDevices,
60
53
  chooseReproductionMode,
61
- direct,
62
- questions,
54
+ deviceProfiles,
55
+ profiles,
63
56
  reject,
64
57
  screen,
65
- selectDirect,
58
+ select,
66
59
  start,
67
60
  };
68
61
  })(window);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Homebridge Eufy",
3
3
  "name": "@homebridge-plugins/homebridge-eufy-security",
4
- "version": "5.0.0-beta.3",
4
+ "version": "5.0.0-beta.5",
5
5
  "description": "Control Eufy Security from homebridge.",
6
6
  "type": "module",
7
7
  "license": "Apache-2.0",