@homebridge-plugins/homebridge-eufy-security 5.0.0-beta.16 → 5.0.0-beta.17

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.
@@ -263,16 +263,53 @@
263
263
  */
264
264
  const DIAGNOSABLE = new Set(['incomplete', 'missing', 'stale']);
265
265
 
266
+ /** The diagnostics area a condition's capture belongs to, by its code. A code no entry matches is `other`. */
267
+ const CONDITION_AREAS = [
268
+ [/^camera-recording/, 'hksv-recording'],
269
+ [/^camera-(?:live|media|streaming|snapshot|talkback)/, 'live-media'],
270
+ [/lock|light|arming|siren|contact|camera-control/, 'control-state'],
271
+ [/transport-degraded/, 'startup-authentication'],
272
+ ];
273
+
274
+ /**
275
+ * The problem the diagnostics action points at, or nothing: a state a run explains, then the first active condition
276
+ * this page has words for, then the first device nothing can reach. Each names the diagnostics area a run on it is
277
+ * about. A condition is worded under its log keys; the ones left unworded are those the page answers on its own,
278
+ * such as a sign-in or another owner, and the devices no action makes supported.
279
+ */
280
+ function attention(result, state, messages) {
281
+ if (DIAGNOSABLE.has(result.state)) return { ...state, profile: 'startup-authentication' };
282
+ const condition = (result.conditions ?? []).find(
283
+ ({ summaryKey, actionKey }) => messages[summaryKey] && messages[actionKey],
284
+ );
285
+ if (condition) {
286
+ return {
287
+ title: messages[condition.summaryKey],
288
+ summary: messages[condition.actionKey],
289
+ profile: CONDITION_AREAS.find(([code]) => code.test(condition.code))?.[1] ?? 'other',
290
+ };
291
+ }
292
+ const device = result.devices.find((candidate) => candidate.availability === 'unavailable');
293
+ return (
294
+ device && {
295
+ title: (messages.attentionUnreachableTitle ?? '').replace('{device}', device.name),
296
+ summary: messages.attentionUnreachableSummary ?? '',
297
+ profile: 'device-representation',
298
+ }
299
+ );
300
+ }
301
+
302
+ /** Draws the dashboard, and answers the problem it shows the diagnostics action, if any. */
266
303
  function render(result, config, messages, elements) {
267
304
  const suffix = result.state
268
305
  .split('-')
269
306
  .map((part) => part[0].toUpperCase() + part.slice(1))
270
307
  .join('');
271
- elements.title.textContent = messages[`dashboard${suffix}Title`] ?? messages.dashboardIncompleteTitle;
272
- elements.summary.textContent = messages[`dashboard${suffix}Summary`] ?? messages.dashboardIncompleteSummary;
308
+ const title = messages[`dashboard${suffix}Title`] ?? messages.dashboardIncompleteTitle;
309
+ const summary = messages[`dashboard${suffix}Summary`] ?? messages.dashboardIncompleteSummary;
310
+ elements.title.textContent = title;
311
+ elements.summary.textContent = summary;
273
312
  elements.dashboard.dataset.state = result.state;
274
- // Offered rather than announced: a run is worth suggesting only where the state is one a run explains.
275
- if (elements.diagnose) elements.diagnose.hidden = !DIAGNOSABLE.has(result.state);
276
313
  elements.dashboard.hidden = false;
277
314
  elements.masthead.hidden = true;
278
315
  renderUpdatePending(result, messages, elements);
@@ -284,6 +321,7 @@
284
321
  } else if (result.state === 'authentication-required') {
285
322
  elements.setup.hidden = false;
286
323
  }
324
+ return attention(result, { title, summary }, messages);
287
325
  }
288
326
 
289
327
  function bindPreferences(elements, getConfig, saveConfig, getMessages, openDevice) {
@@ -9,55 +9,14 @@
9
9
  'other',
10
10
  ];
11
11
 
12
- const deviceProfiles = ['device-representation', 'control-state', 'live-media', 'hksv-recording'];
13
-
14
12
  /**
15
- * Whether a fault of a given area could be about a given device, per area that can tell.
16
- *
17
- * A device the fault cannot happen on is not an answer to which ones are affected: offering a HomeBase for a
18
- * live view fault invites a report about a stream that device never had. Missing from the list means every
19
- * device qualifies, which is what a device missing from HomeKit needs — the ones with no representation at
20
- * all are exactly the subject there.
13
+ * Which screen a session puts on the panel: its file while one can be downloaded, its capture while one runs, and
14
+ * otherwise the question, since picking an area replaces the session with a capture of that area.
21
15
  */
22
- const affected = {
23
- 'control-state': (device) => device.controllable,
24
- 'live-media': (device) => device.representation.includes('camera.streaming'),
25
- 'hksv-recording': (device) => device.representation.includes('camera.streaming'),
26
- };
27
-
28
- function affectable(profile, devices) {
29
- return devices.filter(affected[profile] ?? (() => true));
30
- }
31
-
32
- function start() {
33
- return { mode: 'tiles', profile: undefined };
34
- }
35
-
36
- function select(state, profile) {
37
- return { ...state, mode: deviceProfiles.includes(profile) ? 'devices' : 'frequency', profile };
38
- }
39
-
40
- function chooseDevices(state, devices) {
41
- return { ...state, mode: 'frequency', devices };
42
- }
43
-
44
- function reject() {
45
- return start();
46
- }
47
-
48
- function chooseReproductionMode(state, reproductionMode) {
49
- return { ...state, mode: 'match', reproductionMode };
50
- }
51
-
52
- function backFromFrequency(state) {
53
- return deviceProfiles.includes(state.profile) ? { ...state, mode: 'devices' } : start();
54
- }
55
-
56
- function screen(session, startingAnother) {
57
- if (session.partialExportAvailable && !startingAnother) return 'review';
58
- if (startingAnother || session.status === 'inactive' || session.status === 'expired') return 'choose';
59
- if (session.status === 'authorized' || session.status === 'reproducing') return 'reproduce';
60
- return 'status';
16
+ function screen(session) {
17
+ if (session.partialExportAvailable) return 'review';
18
+ if (session.status === 'reproducing') return 'reproduce';
19
+ return 'choose';
61
20
  }
62
21
 
63
22
  function backgroundActive(session) {
@@ -65,16 +24,8 @@
65
24
  }
66
25
 
67
26
  global.HomebridgeEufyDiagnosticsWizard = {
68
- affectable,
69
- backFromFrequency,
70
27
  backgroundActive,
71
- chooseDevices,
72
- chooseReproductionMode,
73
- deviceProfiles,
74
28
  profiles,
75
- reject,
76
29
  screen,
77
- select,
78
- start,
79
30
  };
80
31
  })(window);
@@ -87,7 +87,7 @@
87
87
  "log.notice.stateSubscriberFailed": "A runtime state consumer failed; review Homebridge Eufy health in the dashboard.",
88
88
  "log.notice.statusPublicationFailed": "Runtime status could not be published; dashboard status may be stale.",
89
89
  "log.notice.channelServeFailed": "Live runtime status could not be served to the plugin settings page; it will show the last published status instead.",
90
- "log.notice.diagnosticsAuthorizationArmed": "Diagnostic evidence collection is now active in the plugin runtime.",
90
+ "log.notice.diagnosticsAuthorizationArmed": "A diagnostics capture started; finish it from the plugin settings before {finishBy}.",
91
91
  "log.notice.standDownNotRearmed": "Homebridge Eufy stood down for an authentication and could not take the account session back; restart Homebridge to bring it back.",
92
92
  "log.notice.standDownAccountReplaced": "Homebridge Eufy stood down for an authentication that replaced the account; restart Homebridge to use the new account.",
93
93
  "log.runtime.authenticationRequired": "Eufy authentication is required",
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.16",
4
+ "version": "5.0.0-beta.17",
5
5
  "description": "Control Eufy Security from homebridge.",
6
6
  "type": "module",
7
7
  "license": "Apache-2.0",