@homebridge-plugins/homebridge-eufy-security 5.0.0-beta.2 → 5.0.0-beta.20
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/config.schema.json +24 -0
- package/dist/account/temporary-authentication.js +3 -1
- package/dist/account/temporary-authentication.js.map +1 -1
- package/dist/configuration.js +36 -2
- package/dist/configuration.js.map +1 -1
- package/dist/device/member-evidence.js +10 -3
- package/dist/device/member-evidence.js.map +1 -1
- package/dist/diagnostics.js +582 -83
- package/dist/diagnostics.js.map +1 -1
- package/dist/homekit/adapters/camera-streaming.js +54 -26
- package/dist/homekit/adapters/camera-streaming.js.map +1 -1
- package/dist/homekit/adapters/lock.js +36 -13
- package/dist/homekit/adapters/lock.js.map +1 -1
- package/dist/homekit/adapters/motion.js +1 -1
- package/dist/homekit/adapters/motion.js.map +1 -1
- package/dist/homekit/adapters/package-sensor.js +63 -0
- package/dist/homekit/adapters/package-sensor.js.map +1 -0
- package/dist/homekit/adapters/registry.js +2 -0
- package/dist/homekit/adapters/registry.js.map +1 -1
- package/dist/homekit/adapters/security-system.js +95 -44
- package/dist/homekit/adapters/security-system.js.map +1 -1
- package/dist/homekit/coverage-matrix.js +1 -3
- package/dist/homekit/coverage-matrix.js.map +1 -1
- package/dist/homekit/reconciler.js +6 -2
- package/dist/homekit/reconciler.js.map +1 -1
- package/dist/media/live-stream.js +98 -54
- package/dist/media/live-stream.js.map +1 -1
- package/dist/media/recording.js +7 -5
- package/dist/media/recording.js.map +1 -1
- package/dist/media/snapshot.js +4 -4
- package/dist/media/station-live-sessions.js +47 -43
- package/dist/media/station-live-sessions.js.map +1 -1
- package/dist/platform.js +25 -5
- package/dist/platform.js.map +1 -1
- package/dist/runtime/channel.js +6 -2
- package/dist/runtime/channel.js.map +1 -1
- package/dist/runtime/owner.js +3 -0
- package/dist/runtime/owner.js.map +1 -1
- package/dist/ui/dashboard.js +32 -14
- package/dist/ui/dashboard.js.map +1 -1
- package/dist/ui/server.js +27 -4
- package/dist/ui/server.js.map +1 -1
- package/homebridge-ui/public/app.css +353 -88
- package/homebridge-ui/public/i18n/en.json +108 -78
- package/homebridge-ui/public/i18n/fr.json +108 -78
- package/homebridge-ui/public/index.html +95 -106
- package/homebridge-ui/public/js/app.js +328 -345
- package/homebridge-ui/public/js/dashboard.js +139 -10
- package/homebridge-ui/public/js/profile-wizard.js +17 -54
- package/i18n/runtime/en.json +5 -3
- package/package.json +6 -6
|
@@ -8,12 +8,65 @@
|
|
|
8
8
|
.replaceAll("'", ''');
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
const EUFY_MODES = [
|
|
12
|
+
{ value: 'away', label: 'Away' },
|
|
13
|
+
{ value: 'home', label: 'Home' },
|
|
14
|
+
{ value: 'schedule', label: 'Schedule' },
|
|
15
|
+
{ value: 'custom1', label: 'Custom 1' },
|
|
16
|
+
{ value: 'custom2', label: 'Custom 2' },
|
|
17
|
+
{ value: 'custom3', label: 'Custom 3' },
|
|
18
|
+
{ value: 'off', label: 'Off' },
|
|
19
|
+
{ value: 'geo', label: 'Geofencing' },
|
|
20
|
+
{ value: 'disarmed', label: 'Disarmed' },
|
|
21
|
+
];
|
|
22
|
+
/** What each HomeKit state means where the household assigned nothing, as the adapter resolves it. */
|
|
23
|
+
const DEFAULT_ARMING_MODES = { home: 'home', away: 'away', off: 'disarmed' };
|
|
24
|
+
const ARMING_SLOTS = [
|
|
25
|
+
{ slot: 'home', label: 'armingSlotHome' },
|
|
26
|
+
{ slot: 'away', label: 'armingSlotAway' },
|
|
27
|
+
{ slot: 'night', label: 'armingSlotNight' },
|
|
28
|
+
{ slot: 'off', label: 'armingSlotOff' },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The guard-mode map: four states HomeKit names against the nine a station reports, one pairing per row.
|
|
33
|
+
*
|
|
34
|
+
* Folded shut, because the four defaults are what nearly every household keeps and the panel has a camera's
|
|
35
|
+
* worth of settings above it. The summary carries the whole map as text, so the answer is readable without
|
|
36
|
+
* opening anything, and a household that assigned a state of its own finds it already open.
|
|
37
|
+
*
|
|
38
|
+
* A row is the whole control — the state names itself and the select beside it says what it means. An
|
|
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.
|
|
42
|
+
*/
|
|
43
|
+
function armingModeControl(device, assigned, messages, securitySystem) {
|
|
44
|
+
const summary = ARMING_SLOTS.map(
|
|
45
|
+
({ slot, label }) => `${messages[label]} → ${EUFY_MODES.find((mode) => mode.value === assigned[slot])?.label ?? '—'}`,
|
|
46
|
+
).join(' · ');
|
|
47
|
+
const rows = ARMING_SLOTS.map(({ slot, label }) => {
|
|
48
|
+
const value = assigned[slot] ?? '';
|
|
49
|
+
const id = `arming-${escapeHtml(device.serial)}-${slot}`;
|
|
50
|
+
const options = EUFY_MODES.map(
|
|
51
|
+
(mode) =>
|
|
52
|
+
`<option value="${mode.value}"${value === mode.value ? ' selected' : ''}>${escapeHtml(mode.label)}</option>`,
|
|
53
|
+
).join('');
|
|
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>`;
|
|
55
|
+
}).join('');
|
|
56
|
+
const assignedItself = ARMING_SLOTS.some(({ slot }) => assigned[slot] !== DEFAULT_ARMING_MODES[slot]);
|
|
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>`;
|
|
58
|
+
}
|
|
59
|
+
|
|
11
60
|
function preferenceControl(device, key, preference, messages) {
|
|
12
61
|
const labels = {
|
|
13
62
|
represented: messages.preferenceRepresented,
|
|
14
63
|
audio: messages.preferenceAudio,
|
|
64
|
+
securitySystem: messages.preferenceSecuritySystem,
|
|
15
65
|
snapshotMode: messages.preferenceSnapshotMode,
|
|
16
66
|
};
|
|
67
|
+
if (key === 'armingModes') {
|
|
68
|
+
return armingModeControl(device, preference.armingModes, messages, preference.securitySystem);
|
|
69
|
+
}
|
|
17
70
|
const represented = preference.represented;
|
|
18
71
|
const dependent = key === 'represented' ? '' : ` data-requires-representation${represented ? '' : ' hidden'}`;
|
|
19
72
|
if (key === 'snapshotMode') {
|
|
@@ -29,7 +82,7 @@
|
|
|
29
82
|
|
|
30
83
|
function markPendingPreference(control, key, value) {
|
|
31
84
|
if (!control?.classList || control.dataset.original === undefined) return;
|
|
32
|
-
const current = key === 'snapshotMode' ? String(value) : String(Boolean(value));
|
|
85
|
+
const current = key === 'snapshotMode' || key === 'armingModes' ? String(value) : String(Boolean(value));
|
|
33
86
|
const setting = control.closest('[data-setting]');
|
|
34
87
|
const changed = current !== control.dataset.original;
|
|
35
88
|
if (key === 'snapshotMode') {
|
|
@@ -204,21 +257,59 @@
|
|
|
204
257
|
}
|
|
205
258
|
|
|
206
259
|
/*
|
|
207
|
-
* The states a diagnostic run can shed light on. Sign-in, another process holding the session,
|
|
208
|
-
* restart
|
|
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.
|
|
209
263
|
*/
|
|
210
|
-
const DIAGNOSABLE = new Set(['
|
|
264
|
+
const DIAGNOSABLE = new Set(['incomplete', 'missing', 'stale']);
|
|
211
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. */
|
|
212
303
|
function render(result, config, messages, elements) {
|
|
213
304
|
const suffix = result.state
|
|
214
305
|
.split('-')
|
|
215
306
|
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
216
307
|
.join('');
|
|
217
|
-
|
|
218
|
-
|
|
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;
|
|
219
312
|
elements.dashboard.dataset.state = result.state;
|
|
220
|
-
// Offered rather than announced: a run is worth suggesting only where the state is one a run explains.
|
|
221
|
-
if (elements.diagnose) elements.diagnose.hidden = !DIAGNOSABLE.has(result.state);
|
|
222
313
|
elements.dashboard.hidden = false;
|
|
223
314
|
elements.masthead.hidden = true;
|
|
224
315
|
renderUpdatePending(result, messages, elements);
|
|
@@ -230,6 +321,7 @@
|
|
|
230
321
|
} else if (result.state === 'authentication-required') {
|
|
231
322
|
elements.setup.hidden = false;
|
|
232
323
|
}
|
|
324
|
+
return attention(result, { title, summary }, messages);
|
|
233
325
|
}
|
|
234
326
|
|
|
235
327
|
function bindPreferences(elements, getConfig, saveConfig, getMessages, openDevice) {
|
|
@@ -260,7 +352,11 @@
|
|
|
260
352
|
const key = control?.dataset?.preference;
|
|
261
353
|
const existing = getConfig();
|
|
262
354
|
if (!existing || !serial || !key) return;
|
|
263
|
-
const defaults = { represented: true, audio: true, snapshotMode: 'Refresh' };
|
|
355
|
+
const defaults = { represented: true, audio: true, securitySystem: true, snapshotMode: 'Refresh' };
|
|
356
|
+
if (key === 'armingModes') {
|
|
357
|
+
await saveArmingMode(control, serial, existing, saveConfig, getMessages, elements);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
264
360
|
const value = key === 'snapshotMode' ? control.value : control.checked;
|
|
265
361
|
markPendingPreference(control, key, value);
|
|
266
362
|
if (key === 'snapshotMode') {
|
|
@@ -282,9 +378,16 @@
|
|
|
282
378
|
(candidate) => candidate.dataset.serial === serial,
|
|
283
379
|
);
|
|
284
380
|
tile?.classList.toggle('device-tile-disabled', !value);
|
|
381
|
+
const grid = control.closest('.preference-grid');
|
|
382
|
+
const securityOff = grid?.querySelector('[data-preference="securitySystem"]')?.checked === false;
|
|
383
|
+
grid?.querySelectorAll('[data-requires-representation]').forEach((setting) => {
|
|
384
|
+
setting.hidden = !value || (securityOff && setting.hasAttribute('data-requires-security-system'));
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
if (key === 'securitySystem') {
|
|
285
388
|
control
|
|
286
389
|
.closest('.preference-grid')
|
|
287
|
-
?.querySelectorAll('[data-requires-
|
|
390
|
+
?.querySelectorAll('[data-requires-security-system]')
|
|
288
391
|
.forEach((setting) => {
|
|
289
392
|
setting.hidden = !value;
|
|
290
393
|
});
|
|
@@ -295,6 +398,30 @@
|
|
|
295
398
|
});
|
|
296
399
|
}
|
|
297
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Records one HomeKit state's guard mode, keeping the map sparse: a state set back to the plugin's own choice
|
|
403
|
+
* is removed rather than pinned, so a later default reaches a household that never overrode it.
|
|
404
|
+
*/
|
|
405
|
+
async function saveArmingMode(control, serial, existing, saveConfig, getMessages, elements) {
|
|
406
|
+
const slot = control.dataset.slot;
|
|
407
|
+
const value = control.value;
|
|
408
|
+
markPendingPreference(control, 'armingModes', value);
|
|
409
|
+
const entityPreferences = { ...(existing.entityPreferences ?? {}) };
|
|
410
|
+
const preference = { ...(entityPreferences[serial] ?? {}) };
|
|
411
|
+
const armingModes = { ...(preference.armingModes ?? {}) };
|
|
412
|
+
if (value === '') delete armingModes[slot];
|
|
413
|
+
else armingModes[slot] = value;
|
|
414
|
+
if (Object.keys(armingModes).length === 0) delete preference.armingModes;
|
|
415
|
+
else preference.armingModes = armingModes;
|
|
416
|
+
if (Object.keys(preference).length === 0) delete entityPreferences[serial];
|
|
417
|
+
else entityPreferences[serial] = preference;
|
|
418
|
+
try {
|
|
419
|
+
await saveConfig({ ...existing, entityPreferences });
|
|
420
|
+
} catch {
|
|
421
|
+
elements.summary.textContent = getMessages().preferenceSaveFailed;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
298
425
|
/**
|
|
299
426
|
* Fills the settings page for one device, which is where its controls live rather than on the tile itself.
|
|
300
427
|
*
|
|
@@ -312,7 +439,9 @@
|
|
|
312
439
|
const preference = {
|
|
313
440
|
represented: preferences[device.serial]?.represented ?? true,
|
|
314
441
|
audio: preferences[device.serial]?.audio ?? true,
|
|
442
|
+
securitySystem: preferences[device.serial]?.securitySystem ?? true,
|
|
315
443
|
snapshotMode: preferences[device.serial]?.snapshotMode ?? 'Refresh',
|
|
444
|
+
armingModes: { ...DEFAULT_ARMING_MODES, ...preferences[device.serial]?.armingModes },
|
|
316
445
|
};
|
|
317
446
|
const controls = device.preferences
|
|
318
447
|
.map((key) => preferenceControl(device, key, preference, messages))
|
|
@@ -1,52 +1,22 @@
|
|
|
1
1
|
(function attachDiagnosticsWizard(global) {
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (state.questionIndex === questions.length - 1) {
|
|
20
|
-
return { ...state, mode: 'frequency', profile: 'other' };
|
|
21
|
-
}
|
|
22
|
-
return { ...state, questionIndex: state.questionIndex + 1 };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function direct() {
|
|
26
|
-
return { mode: 'direct', questionIndex: 0, profile: undefined, source: 'direct' };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function selectDirect(state, profile) {
|
|
30
|
-
return { ...state, mode: 'frequency', profile, source: 'direct' };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function reject(state) {
|
|
34
|
-
return state.source === 'direct' ? direct() : start();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function chooseReproductionMode(state, reproductionMode) {
|
|
38
|
-
return { ...state, mode: 'match', reproductionMode };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function backFromFrequency(state) {
|
|
42
|
-
return state.source === 'direct' ? direct() : start();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function screen(session, startingAnother) {
|
|
46
|
-
if (session.partialExportAvailable && !startingAnother) return 'review';
|
|
47
|
-
if (startingAnother || session.status === 'inactive' || session.status === 'expired') return 'choose';
|
|
48
|
-
if (session.status === 'authorized' || session.status === 'reproducing') return 'reproduce';
|
|
49
|
-
return 'status';
|
|
12
|
+
/**
|
|
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.
|
|
15
|
+
*/
|
|
16
|
+
function screen(session) {
|
|
17
|
+
if (session.partialExportAvailable) return 'review';
|
|
18
|
+
if (session.status === 'reproducing') return 'reproduce';
|
|
19
|
+
return 'choose';
|
|
50
20
|
}
|
|
51
21
|
|
|
52
22
|
function backgroundActive(session) {
|
|
@@ -54,15 +24,8 @@
|
|
|
54
24
|
}
|
|
55
25
|
|
|
56
26
|
global.HomebridgeEufyDiagnosticsWizard = {
|
|
57
|
-
answer,
|
|
58
|
-
backFromFrequency,
|
|
59
27
|
backgroundActive,
|
|
60
|
-
|
|
61
|
-
direct,
|
|
62
|
-
questions,
|
|
63
|
-
reject,
|
|
28
|
+
profiles,
|
|
64
29
|
screen,
|
|
65
|
-
selectDirect,
|
|
66
|
-
start,
|
|
67
30
|
};
|
|
68
31
|
})(window);
|
package/i18n/runtime/en.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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",
|
|
@@ -85,7 +87,7 @@
|
|
|
85
87
|
"log.notice.stateSubscriberFailed": "A runtime state consumer failed; review Homebridge Eufy health in the dashboard.",
|
|
86
88
|
"log.notice.statusPublicationFailed": "Runtime status could not be published; dashboard status may be stale.",
|
|
87
89
|
"log.notice.channelServeFailed": "Live runtime status could not be served to the plugin settings page; it will show the last published status instead.",
|
|
88
|
-
"log.notice.diagnosticsAuthorizationArmed": "
|
|
90
|
+
"log.notice.diagnosticsAuthorizationArmed": "A diagnostics capture started; finish it from the plugin settings before {finishBy}.",
|
|
89
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.",
|
|
90
92
|
"log.notice.standDownAccountReplaced": "Homebridge Eufy stood down for an authentication that replaced the account; restart Homebridge to use the new account.",
|
|
91
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.
|
|
4
|
+
"version": "5.0.0-beta.20",
|
|
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.
|
|
50
|
+
"@mega-yfue/eufy-sdk": "0.4.0-beta.13",
|
|
51
51
|
"ffmpeg-for-homebridge": "2.2.2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@homebridge/hap-nodejs": "2.2.
|
|
55
|
-
"@types/node": "^26.
|
|
54
|
+
"@homebridge/hap-nodejs": "2.2.3",
|
|
55
|
+
"@types/node": "^26.6.3",
|
|
56
56
|
"homebridge": "^2.4.0",
|
|
57
|
-
"prettier": "^3.9.
|
|
57
|
+
"prettier": "^3.9.9",
|
|
58
58
|
"rimraf": "^6.1.3",
|
|
59
59
|
"typescript": "^7.0.2",
|
|
60
|
-
"vitest": "^5.0.
|
|
60
|
+
"vitest": "^5.0.2"
|
|
61
61
|
},
|
|
62
62
|
"homepage": "https://homebridge-plugins.github.io/homebridge-eufy/",
|
|
63
63
|
"author": "homebridge-eufy",
|