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

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.
@@ -37,8 +37,10 @@
37
37
  *
38
38
  * A row is the whole control — the state names itself and the select beside it says what it means. An
39
39
  * unassigned state shows a dash, which is what keeps Night out of HomeKit until it is given a mode.
40
+ *
41
+ * It is withheld while the security system it maps is turned off, and the stored map is kept for when it is back.
40
42
  */
41
- function armingModeControl(device, assigned, messages) {
43
+ function armingModeControl(device, assigned, messages, securitySystem) {
42
44
  const summary = ARMING_SLOTS.map(
43
45
  ({ slot, label }) => `${messages[label]} → ${EUFY_MODES.find((mode) => mode.value === assigned[slot])?.label ?? '—'}`,
44
46
  ).join(' · ');
@@ -52,17 +54,18 @@
52
54
  return `<tr><th scope="row"><label for="${id}">${escapeHtml(messages[label])}</label></th><td><select id="${id}" data-preference="armingModes" data-slot="${slot}" data-serial="${escapeHtml(device.serial)}" data-original="${escapeHtml(value)}"><option value=""${value === '' ? ' selected' : ''}>—</option>${options}</select></td></tr>`;
53
55
  }).join('');
54
56
  const assignedItself = ARMING_SLOTS.some(({ slot }) => assigned[slot] !== DEFAULT_ARMING_MODES[slot]);
55
- return `<details class="arming-setting" data-setting${assignedItself ? ' open' : ''}${device.represented ? '' : ' hidden'} data-requires-representation><summary><span class="setting-label">${escapeHtml(messages.preferenceArmingModes)}</span><span class="arming-summary">${escapeHtml(summary)}</span></summary><table class="arming-table"><thead><tr><th scope="col">HomeKit</th><th scope="col">eufy</th></tr></thead><tbody>${rows}</tbody></table><p class="snapshot-help">${escapeHtml(messages.armingModesHelp)}</p></details>`;
57
+ return `<details class="arming-setting" data-setting${assignedItself ? ' open' : ''}${device.represented && securitySystem ? '' : ' hidden'} data-requires-representation data-requires-security-system><summary><span class="setting-label">${escapeHtml(messages.preferenceArmingModes)}</span><span class="arming-summary">${escapeHtml(summary)}</span></summary><table class="arming-table"><thead><tr><th scope="col">HomeKit</th><th scope="col">eufy</th></tr></thead><tbody>${rows}</tbody></table><p class="snapshot-help">${escapeHtml(messages.armingModesHelp)}</p></details>`;
56
58
  }
57
59
 
58
60
  function preferenceControl(device, key, preference, messages) {
59
61
  const labels = {
60
62
  represented: messages.preferenceRepresented,
61
63
  audio: messages.preferenceAudio,
64
+ securitySystem: messages.preferenceSecuritySystem,
62
65
  snapshotMode: messages.preferenceSnapshotMode,
63
66
  };
64
67
  if (key === 'armingModes') {
65
- return armingModeControl(device, preference.armingModes, messages);
68
+ return armingModeControl(device, preference.armingModes, messages, preference.securitySystem);
66
69
  }
67
70
  const represented = preference.represented;
68
71
  const dependent = key === 'represented' ? '' : ` data-requires-representation${represented ? '' : ' hidden'}`;
@@ -254,10 +257,11 @@
254
257
  }
255
258
 
256
259
  /*
257
- * The states a diagnostic run can shed light on. Sign-in, another process holding the session, and a pending
258
- * restart each have their own remedy stated on the page, so sending those to the wizard would be a detour.
260
+ * The states a diagnostic run can shed light on. Sign-in, another process holding the session, a pending
261
+ * restart, a plugin that stopped on an error, a page that could not load, and a connection that recovers on its
262
+ * own each have their own remedy stated on the page, so sending those to the wizard would be a detour.
259
263
  */
260
- const DIAGNOSABLE = new Set(['degraded', 'incomplete', 'missing', 'stale']);
264
+ const DIAGNOSABLE = new Set(['incomplete', 'missing', 'stale']);
261
265
 
262
266
  function render(result, config, messages, elements) {
263
267
  const suffix = result.state
@@ -310,7 +314,7 @@
310
314
  const key = control?.dataset?.preference;
311
315
  const existing = getConfig();
312
316
  if (!existing || !serial || !key) return;
313
- const defaults = { represented: true, audio: true, snapshotMode: 'Refresh' };
317
+ const defaults = { represented: true, audio: true, securitySystem: true, snapshotMode: 'Refresh' };
314
318
  if (key === 'armingModes') {
315
319
  await saveArmingMode(control, serial, existing, saveConfig, getMessages, elements);
316
320
  return;
@@ -336,9 +340,16 @@
336
340
  (candidate) => candidate.dataset.serial === serial,
337
341
  );
338
342
  tile?.classList.toggle('device-tile-disabled', !value);
343
+ const grid = control.closest('.preference-grid');
344
+ const securityOff = grid?.querySelector('[data-preference="securitySystem"]')?.checked === false;
345
+ grid?.querySelectorAll('[data-requires-representation]').forEach((setting) => {
346
+ setting.hidden = !value || (securityOff && setting.hasAttribute('data-requires-security-system'));
347
+ });
348
+ }
349
+ if (key === 'securitySystem') {
339
350
  control
340
351
  .closest('.preference-grid')
341
- ?.querySelectorAll('[data-requires-representation]')
352
+ ?.querySelectorAll('[data-requires-security-system]')
342
353
  .forEach((setting) => {
343
354
  setting.hidden = !value;
344
355
  });
@@ -390,6 +401,7 @@
390
401
  const preference = {
391
402
  represented: preferences[device.serial]?.represented ?? true,
392
403
  audio: preferences[device.serial]?.audio ?? true,
404
+ securitySystem: preferences[device.serial]?.securitySystem ?? true,
393
405
  snapshotMode: preferences[device.serial]?.snapshotMode ?? 'Refresh',
394
406
  armingModes: { ...DEFAULT_ARMING_MODES, ...preferences[device.serial]?.armingModes },
395
407
  };
@@ -9,17 +9,19 @@
9
9
  "log.action.checkCameraControl": "Check the camera in the Eufy app; this control is unavailable until it reports its state",
10
10
  "log.action.retryCameraControl": "Try the control again; the camera did not confirm the change",
11
11
  "log.action.enableCamera": "Turn the camera on in the Eufy app, then open live view again",
12
- "log.action.openDashboard": "Review device representation in the Homebridge Eufy dashboard",
12
+ "log.action.notSupportedYet": "No action is needed; these devices are not supported in HomeKit yet",
13
13
  "log.action.reduceConcurrentMedia": "Open live view again; if it keeps happening, close a camera already open or raise the concurrent camera media limit in the plugin settings",
14
14
  "log.action.reauthenticate": "Authenticate again in the Homebridge Eufy dashboard, then restart Homebridge",
15
15
  "log.action.retryArming": "Retry the security-system control manually",
16
16
  "log.action.retryLight": "Retry the smart-light control manually",
17
17
  "log.action.retryLiveView": "Open live view again, and check network access between Homebridge and the camera",
18
+ "log.action.setFfmpegPath": "Set a custom FFmpeg path in Advanced settings in the Homebridge Eufy dashboard",
19
+ "log.action.checkFfmpegPath": "FFmpeg could not be started; check the custom FFmpeg path in Advanced settings in the Homebridge Eufy dashboard",
18
20
  "log.action.retryLock": "Retry the lock control manually",
19
21
  "log.action.retryTalkback": "Close and reopen the camera, then try speaking again",
20
22
  "log.action.reviewRuntime": "Review Homebridge Eufy health in the dashboard and restart Homebridge",
21
23
  "log.action.stopOtherOwner": "Stop the other Homebridge Eufy owner before restarting Homebridge",
22
- "log.action.selectSupportedArmingMode": "Select Stay, Away, or Disarm to leave the unsupported mode",
24
+ "log.action.mapArmingMode": "Assign this mode to a HomeKit state under HomeKit guard modes in the device's settings in the Homebridge Eufy dashboard",
23
25
  "log.action.waitBattery": "Wait for complete battery capability evidence",
24
26
  "log.action.waitArming": "Wait for complete arming capability evidence",
25
27
  "log.action.waitBatteryObservation": "Wait for a valid battery observation",
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.14",
4
+ "version": "5.0.0-beta.16",
5
5
  "description": "Control Eufy Security from homebridge.",
6
6
  "type": "module",
7
7
  "license": "Apache-2.0",
@@ -47,17 +47,17 @@
47
47
  ],
48
48
  "dependencies": {
49
49
  "@homebridge/plugin-ui-utils": "^2.2.6",
50
- "@mega-yfue/eufy-sdk": "0.2.0",
50
+ "@mega-yfue/eufy-sdk": "0.3.0",
51
51
  "ffmpeg-for-homebridge": "2.2.2"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@homebridge/hap-nodejs": "2.2.3",
55
- "@types/node": "^26.6.2",
55
+ "@types/node": "^26.6.3",
56
56
  "homebridge": "^2.4.0",
57
57
  "prettier": "^3.9.9",
58
58
  "rimraf": "^6.1.3",
59
59
  "typescript": "^7.0.2",
60
- "vitest": "^5.0.1"
60
+ "vitest": "^5.0.2"
61
61
  },
62
62
  "homepage": "https://homebridge-plugins.github.io/homebridge-eufy/",
63
63
  "author": "homebridge-eufy",