@homebridge-plugins/homebridge-eufy-security 5.0.0-beta.15 → 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.
- package/config.schema.json +4 -0
- package/dist/configuration.js +11 -1
- package/dist/configuration.js.map +1 -1
- package/dist/diagnostics.js +105 -19
- package/dist/diagnostics.js.map +1 -1
- package/dist/homekit/adapters/lock.js +27 -11
- package/dist/homekit/adapters/lock.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 +10 -0
- package/dist/homekit/adapters/security-system.js.map +1 -1
- package/dist/homekit/coverage-matrix.js +0 -2
- package/dist/homekit/coverage-matrix.js.map +1 -1
- package/dist/homekit/reconciler.js +1 -0
- package/dist/homekit/reconciler.js.map +1 -1
- package/dist/platform.js +1 -0
- 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 +7 -1
- package/dist/ui/dashboard.js.map +1 -1
- package/dist/ui/server.js +1 -0
- package/dist/ui/server.js.map +1 -1
- package/homebridge-ui/public/app.css +117 -229
- package/homebridge-ui/public/i18n/en.json +86 -79
- package/homebridge-ui/public/i18n/fr.json +87 -80
- package/homebridge-ui/public/index.html +84 -94
- package/homebridge-ui/public/js/app.js +246 -423
- package/homebridge-ui/public/js/dashboard.js +58 -9
- package/homebridge-ui/public/js/profile-wizard.js +6 -55
- package/i18n/runtime/en.json +1 -1
- package/package.json +1 -1
package/config.schema.json
CHANGED
package/dist/configuration.js
CHANGED
|
@@ -9,7 +9,13 @@ export const DEFAULT_MAX_CONCURRENT_MEDIA_SESSIONS = 0;
|
|
|
9
9
|
export const DEFAULT_WARM_UP_EVENTS = ['doorbellPress'];
|
|
10
10
|
/** What each HomeKit state means where the user assigned nothing. */
|
|
11
11
|
export const DEFAULT_ARMING_MODES = { home: 'home', away: 'away', off: 'disarmed' };
|
|
12
|
-
const ENTITY_PREFERENCE_KEYS = new Set([
|
|
12
|
+
const ENTITY_PREFERENCE_KEYS = new Set([
|
|
13
|
+
'represented',
|
|
14
|
+
'audio',
|
|
15
|
+
'securitySystem',
|
|
16
|
+
'snapshotMode',
|
|
17
|
+
'armingModes',
|
|
18
|
+
]);
|
|
13
19
|
const SNAPSHOT_MODES = new Set(['Cloud', 'Live', 'Refresh']);
|
|
14
20
|
const ARMING_SLOTS = new Set(['home', 'away', 'night', 'off']);
|
|
15
21
|
function isRecord(value) {
|
|
@@ -47,6 +53,9 @@ function parseEntityPreferences(value) {
|
|
|
47
53
|
if (candidate.audio !== undefined && typeof candidate.audio !== 'boolean') {
|
|
48
54
|
throw new TypeError(`entityPreferences.${serial}.audio must be a boolean`);
|
|
49
55
|
}
|
|
56
|
+
if (candidate.securitySystem !== undefined && typeof candidate.securitySystem !== 'boolean') {
|
|
57
|
+
throw new TypeError(`entityPreferences.${serial}.securitySystem must be a boolean`);
|
|
58
|
+
}
|
|
50
59
|
if (candidate.snapshotMode !== undefined && !SNAPSHOT_MODES.has(candidate.snapshotMode)) {
|
|
51
60
|
throw new TypeError(`entityPreferences.${serial}.snapshotMode must be Cloud, Live, or Refresh`);
|
|
52
61
|
}
|
|
@@ -184,6 +193,7 @@ export function resolveEntityPreference(config, serial) {
|
|
|
184
193
|
return {
|
|
185
194
|
represented: preference?.represented ?? true,
|
|
186
195
|
audio: preference?.audio ?? true,
|
|
196
|
+
securitySystem: preference?.securitySystem ?? true,
|
|
187
197
|
snapshotMode: preference?.snapshotMode ?? 'Refresh',
|
|
188
198
|
armingModes: { ...DEFAULT_ARMING_MODES, ...preference?.armingModes },
|
|
189
199
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../src/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,iBAAiB,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,2BAA2B,GAAG,iBAAiB,CAAC;AAC7D,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,CAAC;AACnD,+FAA+F;AAC/F,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,eAAe,CAAC,CAAC;AAuB3E,qEAAqE;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../src/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,iBAAiB,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,2BAA2B,GAAG,iBAAiB,CAAC;AAC7D,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,CAAC;AACnD,+FAA+F;AAC/F,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,eAAe,CAAC,CAAC;AAuB3E,qEAAqE;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;AAgDnG,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAyB;IAC7D,aAAa;IACb,OAAO;IACP,gBAAgB;IAChB,cAAc;IACd,aAAa;CACd,CAAC,CAAC;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAC3E,MAAM,YAAY,GAAG,IAAI,GAAG,CAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,cAAc,CAAC,MAA+B,EAAE,GAAW;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,GAAG,GAAG,mBAAmB,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CAAC,gEAAgE,CAAC,CAAC;IACxF,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE;QAChD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,IAAI,SAAS,oBAAoB,CAAC,CAAC;QACpF,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,GAA6B,CAAC,EAAE,CAAC;gBAC/D,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,IAAI,GAAG,gCAAgC,CAAC,CAAC;YAC1F,CAAC;QACH,CAAC;QACD,IAAI,SAAS,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,SAAS,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACtF,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,gCAAgC,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1E,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,0BAA0B,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,IAAI,OAAO,SAAS,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YAC5F,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,mCAAmC,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,SAAS,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,YAA4B,CAAC,EAAE,CAAC;YACxG,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,+CAA+C,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,SAAS,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,uDAAuD,CAAC,CAAC;YAC1G,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;gBACjE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAkB,CAAC,EAAE,CAAC;oBAC1C,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,gBAAgB,IAAI,kCAAkC,CAAC,CAAC;gBACzG,CAAC;gBACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC;oBACjE,MAAM,IAAI,SAAS,CACjB,qBAAqB,MAAM,gBAAgB,IAAI,mBAAmB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACvG,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,MAAM;YACN;gBACE,GAAG,SAAS;gBACZ,GAAG,CAAC,SAAS,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,GAAG,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;aAC1E;SACtB,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAc;IAC9C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,EAC/F,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CAAC,oBAAoB,aAAa,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,eAAe,CAAC;IACpE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,iBAAiB,GAAG,cAAc,CAAC,KAAK,EAAE,mBAAmB,CAAC,IAAI,2BAA2B,CAAC;IACpG,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,sBAAsB,GAAG,KAAK,CAAC,sBAAsB,IAAI,gCAAgC,CAAC;IAChG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,sBAAsB,CAAC,IAAK,sBAAiC,GAAG,CAAC,EAAE,CAAC;QACxF,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,0BAA0B,GAAG,KAAK,CAAC,0BAA0B,IAAI,qCAAqC,CAAC;IAC7G,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,IAAK,0BAAqC,GAAG,CAAC,EAAE,CAAC;QAChG,MAAM,IAAI,SAAS,CAAC,2DAA2D,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3D,MAAM,oBAAoB,GAAG,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACjE,IAAI,oBAAoB,KAAK,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACtD,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,aAAa;QACvB,QAAQ,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC;QAC3C,QAAQ,EAAE,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC;QAC3C,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE;QAC9B,iBAAiB;QACjB,sBAAsB,EAAE,sBAAgC;QACxD,0BAA0B,EAAE,0BAAoC;QAChE,YAAY;QACZ,UAAU,EAAE,oBAAoB,IAAI,iBAAiB;QACrD,iBAAiB,EAAE,sBAAsB,CAAC,KAAK,CAAC,iBAAiB,CAAC;QAClE,mBAAmB,EAAE,wBAAwB,CAAC,KAAK,CAAC,mBAAmB,CAAC;QACxE,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,KAAK,IAAI;KAChE,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,KAAK,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,sBAAsB,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACvD,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAkB;IAChD,OAAO;QACL,QAAQ,EAAE,aAAa;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,sBAAsB,EAAE,MAAM,CAAC,sBAAsB;QACrD,0BAA0B,EAAE,MAAM,CAAC,0BAA0B;QAC7D,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;QACtC,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,iBAAiB,EAAE,MAAM,CAAC,WAAW,CACnC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC,CACpG;QACD,GAAG,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,CAAC,GAAG,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1G,GAAG,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,uBAAuB,CAAC,MAAkB,EAAE,MAAc;IACxE,MAAM,UAAU,GAAG,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACpD,OAAO;QACL,WAAW,EAAE,UAAU,EAAE,WAAW,IAAI,IAAI;QAC5C,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,IAAI;QAChC,cAAc,EAAE,UAAU,EAAE,cAAc,IAAI,IAAI;QAClD,YAAY,EAAE,UAAU,EAAE,YAAY,IAAI,SAAS;QACnD,WAAW,EAAE,EAAE,GAAG,oBAAoB,EAAE,GAAG,UAAU,EAAE,WAAW,EAAE;KACrE,CAAC;AACJ,CAAC"}
|
package/dist/diagnostics.js
CHANGED
|
@@ -67,6 +67,7 @@ const HOMEKIT_LIVE_REQUEST_EVENTS = new Set([
|
|
|
67
67
|
* failure.
|
|
68
68
|
*/
|
|
69
69
|
const SDK_ERROR_TYPES = [
|
|
70
|
+
'DeviceChannelUnresolvedError',
|
|
70
71
|
'Error',
|
|
71
72
|
'RangeError',
|
|
72
73
|
'SessionExpiredError',
|
|
@@ -269,13 +270,14 @@ const BUG_REPORT_FORM = 'https://github.com/homebridge-plugins/homebridge-eufy/i
|
|
|
269
270
|
function bugReportUrl(session, missingEvidence, host) {
|
|
270
271
|
const url = new URL(BUG_REPORT_FORM);
|
|
271
272
|
url.searchParams.set('template', 'bug_report.yml');
|
|
273
|
+
url.searchParams.set('title', `[Diagnostics] ${BUG_REPORT_AREAS[session.profile]}: `);
|
|
272
274
|
url.searchParams.set('environment', [
|
|
273
275
|
`- **Plugin Version**: ${PLUGIN_VERSION}`,
|
|
274
276
|
`- **eufy SDK**: ${SDK_VERSION}`,
|
|
275
277
|
`- **Node.js Version**: ${process.version}`,
|
|
276
278
|
`- **Homebridge Version**: ${host?.homebridge ?? ''}`,
|
|
277
279
|
`- **OS**: ${process.platform} ${process.arch}`,
|
|
278
|
-
`- **Diagnostics profile**: ${session.profile}
|
|
280
|
+
`- **Diagnostics profile**: ${session.profile}`,
|
|
279
281
|
`- **Missing evidence**: ${missingEvidence.length ? missingEvidence.join(', ') : 'none'}`,
|
|
280
282
|
].join('\n'));
|
|
281
283
|
url.searchParams.set('area', BUG_REPORT_AREAS[session.profile]);
|
|
@@ -288,7 +290,7 @@ const DIAGNOSTICS_PROFILES = {
|
|
|
288
290
|
'live-media': ['plugin-log', 'sdk-log', 'homekit-log', 'ffmpeg-log'],
|
|
289
291
|
'hksv-recording': ['plugin-log', 'sdk-log', 'ffmpeg-log'],
|
|
290
292
|
'dashboard-ui': ['plugin-log', 'ui-log'],
|
|
291
|
-
other: ['plugin-log', 'sdk-log', 'homekit-log'],
|
|
293
|
+
other: ['plugin-log', 'sdk-log', 'homekit-log', 'ffmpeg-log'],
|
|
292
294
|
};
|
|
293
295
|
/** Narrows external profile input against the diagnostics-owned profile registry. */
|
|
294
296
|
export function isDiagnosticsProfile(value) {
|
|
@@ -338,6 +340,22 @@ function activeDiagnosticsSession(storageRoot, now) {
|
|
|
338
340
|
const session = readDiagnosticsSession(storageRoot);
|
|
339
341
|
return session && Date.parse(session.expiresAt) > now ? session : undefined;
|
|
340
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* A session as it stands at `now`: a capture still running when its authorization ends is finished at that end.
|
|
345
|
+
*
|
|
346
|
+
* A finished capture can be reviewed and exported until the archive retention has elapsed past the end of its
|
|
347
|
+
* authorization, so a capture that ran out its 72 hours keeps what it captured.
|
|
348
|
+
*/
|
|
349
|
+
function settledDiagnosticsSession(session, now) {
|
|
350
|
+
const expiresAt = Date.parse(session.expiresAt);
|
|
351
|
+
const settled = session.reproductionStartedAt && !session.reproductionEndedAt && expiresAt <= now
|
|
352
|
+
? { ...session, reproductionEndedAt: session.expiresAt }
|
|
353
|
+
: session;
|
|
354
|
+
return {
|
|
355
|
+
session: settled,
|
|
356
|
+
exportable: Boolean(settled.reproductionEndedAt) && now < expiresAt + SUPPORT_ARCHIVE_RETENTION_MS,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
341
359
|
/**
|
|
342
360
|
* Collapses one supplied string to bounded printable text, or nothing where none of it survives.
|
|
343
361
|
*
|
|
@@ -576,6 +594,22 @@ export class GuidedDiagnostics {
|
|
|
576
594
|
await this.ensureMarker(session, 'reproduction-started', session.reproductionStartedAt);
|
|
577
595
|
return this.project(session);
|
|
578
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Deletes the persisted session without an archive, which also ends the verbose retention it authorized. UI events
|
|
599
|
+
* already queued are written first and later ones are refused until it is gone, so none lands after the delete.
|
|
600
|
+
*/
|
|
601
|
+
async cancel() {
|
|
602
|
+
this.uiEventsClosing = true;
|
|
603
|
+
try {
|
|
604
|
+
await this.uiEventWrites;
|
|
605
|
+
this.pendingSupportArchive = undefined;
|
|
606
|
+
await rm(diagnosticsSessionPath(this.storageRoot), { force: true });
|
|
607
|
+
return await this.status();
|
|
608
|
+
}
|
|
609
|
+
finally {
|
|
610
|
+
this.uiEventsClosing = false;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
579
613
|
async endReproduction() {
|
|
580
614
|
let session = this.requireAuthorized();
|
|
581
615
|
if (!session.reproductionStartedAt) {
|
|
@@ -656,10 +690,16 @@ export class GuidedDiagnostics {
|
|
|
656
690
|
/** Prepares the exact manifest and evidence snapshot that one subsequent export may encrypt. */
|
|
657
691
|
async reviewSupportArchive() {
|
|
658
692
|
await this.uiEventWrites;
|
|
659
|
-
const
|
|
693
|
+
const persisted = readDiagnosticsSession(this.storageRoot);
|
|
694
|
+
const { session, exportable } = persisted
|
|
695
|
+
? settledDiagnosticsSession(persisted, this.now())
|
|
696
|
+
: { session: undefined, exportable: false };
|
|
660
697
|
if (!session?.reproductionStartedAt || !session.reproductionEndedAt) {
|
|
661
698
|
throw new Error('A completed reproduction is required before archive review');
|
|
662
699
|
}
|
|
700
|
+
if (!exportable) {
|
|
701
|
+
throw new Error('Diagnostics authorization is inactive or expired');
|
|
702
|
+
}
|
|
663
703
|
const collected = await this.collectSupportEvidence(session);
|
|
664
704
|
const reproductionStartedAt = Date.parse(session.reproductionStartedAt);
|
|
665
705
|
const selected = DIAGNOSTICS_PROFILES[session.profile];
|
|
@@ -702,7 +742,13 @@ export class GuidedDiagnostics {
|
|
|
702
742
|
this.pendingSupportArchive = { reviewId, manifest, evidence: collected };
|
|
703
743
|
return { reviewId, manifest };
|
|
704
744
|
}
|
|
705
|
-
/**
|
|
745
|
+
/**
|
|
746
|
+
* Consumes one reviewed snapshot and returns an encrypted envelope without writing plaintext or an archive to disk.
|
|
747
|
+
*
|
|
748
|
+
* The session outlives the export, because nothing here proves the envelope reached the reporter: a fresh review
|
|
749
|
+
* exports it again until the reporter leaves it with {@link cancel}, another capture replaces it, or the archive
|
|
750
|
+
* retention ends it.
|
|
751
|
+
*/
|
|
706
752
|
async exportSupportArchive(reviewId) {
|
|
707
753
|
const pending = this.pendingSupportArchive;
|
|
708
754
|
if (!pending || pending.reviewId !== reviewId) {
|
|
@@ -748,11 +794,12 @@ export class GuidedDiagnostics {
|
|
|
748
794
|
authTag: cipher.getAuthTag().toString('base64'),
|
|
749
795
|
ciphertext: ciphertext.toString('base64'),
|
|
750
796
|
};
|
|
751
|
-
|
|
797
|
+
const exported = {
|
|
752
798
|
filename: `homebridge-eufy-${pending.manifest.supportCaseId}.eufysupport.gz`,
|
|
753
799
|
mediaType: 'application/gzip',
|
|
754
800
|
archive: await gzip(Buffer.from(`${JSON.stringify(envelope)}\n`, 'utf8')),
|
|
755
801
|
};
|
|
802
|
+
return exported;
|
|
756
803
|
}
|
|
757
804
|
finally {
|
|
758
805
|
contentKey.fill(0);
|
|
@@ -1037,16 +1084,17 @@ export class GuidedDiagnostics {
|
|
|
1037
1084
|
throw new Error('Diagnostics authorization is inactive or expired');
|
|
1038
1085
|
return session;
|
|
1039
1086
|
}
|
|
1040
|
-
async project(
|
|
1041
|
-
if (!
|
|
1087
|
+
async project(persisted) {
|
|
1088
|
+
if (!persisted) {
|
|
1042
1089
|
return { status: 'inactive', selectedEvidence: [], missingEvidence: [], partialExportAvailable: false };
|
|
1043
1090
|
}
|
|
1091
|
+
const { session, exportable } = settledDiagnosticsSession(persisted, this.now());
|
|
1044
1092
|
const selectedEvidence = DIAGNOSTICS_PROFILES[session.profile];
|
|
1045
1093
|
const collected = session.reproductionStartedAt && session.reproductionEndedAt
|
|
1046
1094
|
? await this.collectSupportEvidence(session)
|
|
1047
1095
|
: undefined;
|
|
1048
1096
|
const missingEvidence = collected ? missingEvidenceClasses(selectedEvidence, collected) : [];
|
|
1049
|
-
const expired = Date.parse(session.expiresAt) <= this.now();
|
|
1097
|
+
const expired = Date.parse(session.expiresAt) <= this.now() && !exportable;
|
|
1050
1098
|
const status = expired
|
|
1051
1099
|
? 'expired'
|
|
1052
1100
|
: session.reproductionEndedAt
|
|
@@ -1065,7 +1113,7 @@ export class GuidedDiagnostics {
|
|
|
1065
1113
|
expiresAt: session.expiresAt,
|
|
1066
1114
|
...(session.reproductionStartedAt ? { reproductionStartedAt: session.reproductionStartedAt } : {}),
|
|
1067
1115
|
...(session.reproductionEndedAt ? { reproductionEndedAt: session.reproductionEndedAt } : {}),
|
|
1068
|
-
partialExportAvailable:
|
|
1116
|
+
partialExportAvailable: exportable,
|
|
1069
1117
|
...(session.affectedDevices === undefined ? {} : { affectedDevices: session.affectedDevices }),
|
|
1070
1118
|
issueUrl: bugReportUrl(session, missingEvidence, readHostEnvironment(this.storageRoot)),
|
|
1071
1119
|
};
|
|
@@ -1719,6 +1767,7 @@ const HOMEKIT_EVENT_ROUTES = {
|
|
|
1719
1767
|
'battery.status': new Set(['battery-alert', 'battery-level']),
|
|
1720
1768
|
'contact.sensor': new Set(['contact-state']),
|
|
1721
1769
|
'doorbell.press': new Set(['doorbell-press']),
|
|
1770
|
+
'doorbell.package': new Set(['package-presence']),
|
|
1722
1771
|
'lock.mechanism': new Set(['lock-state']),
|
|
1723
1772
|
'motion.sensor': new Set(['motion-detection']),
|
|
1724
1773
|
'arming.security-system': new Set(['arming-mode-changed', 'security-system-alarm']),
|
|
@@ -1982,6 +2031,33 @@ const HOMEKIT_REASON_ACTIONS = {
|
|
|
1982
2031
|
'camera-recording-unavailable:adapter-missing': 'log.action.setFfmpegPath',
|
|
1983
2032
|
'camera-live-session-failed:adaptation-spawn-failed': 'log.action.checkFfmpegPath',
|
|
1984
2033
|
};
|
|
2034
|
+
/** Projects a value onto an active condition, or nothing where the catalog does not write that code with those keys. */
|
|
2035
|
+
export function knownCondition(value) {
|
|
2036
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
2037
|
+
return undefined;
|
|
2038
|
+
const { code, summaryKey, actionKey, serials } = value;
|
|
2039
|
+
if (typeof code !== 'string')
|
|
2040
|
+
return undefined;
|
|
2041
|
+
const runtime = Object.values(RUNTIME_CONDITIONS).find((condition) => condition.code === code);
|
|
2042
|
+
const homeKit = Object.hasOwn(HOMEKIT_CONDITIONS, code)
|
|
2043
|
+
? HOMEKIT_CONDITIONS[code]
|
|
2044
|
+
: undefined;
|
|
2045
|
+
const [summary, ...actions] = runtime
|
|
2046
|
+
? [runtime.summaryKey, runtime.actionKey]
|
|
2047
|
+
: homeKit
|
|
2048
|
+
? [
|
|
2049
|
+
...homeKit,
|
|
2050
|
+
...Object.entries(HOMEKIT_REASON_ACTIONS)
|
|
2051
|
+
.filter(([key]) => key.startsWith(`${code}:`))
|
|
2052
|
+
.map(([, action]) => action),
|
|
2053
|
+
]
|
|
2054
|
+
: [];
|
|
2055
|
+
const action = allowlistedLabel(actionKey, actions);
|
|
2056
|
+
if (summary === undefined || summaryKey !== summary || action === undefined)
|
|
2057
|
+
return undefined;
|
|
2058
|
+
const named = Array.isArray(serials) ? serials.filter((serial) => typeof serial === 'string') : [];
|
|
2059
|
+
return { code, summaryKey: summary, actionKey: action, ...(named.length === 0 ? {} : { serials: named }) };
|
|
2060
|
+
}
|
|
1985
2061
|
/** HomeKit conditions that state a gap nothing in the user's setup closes, so an active one is not a warning. */
|
|
1986
2062
|
const INFORMATIONAL_HOMEKIT_CONDITIONS = new Set(['recognized-device-not-represented']);
|
|
1987
2063
|
/** The level and action one HomeKit condition is written with, decided once for the console and the record. */
|
|
@@ -2177,7 +2253,7 @@ export function reportRuntimeNotice(target, code, fields = {}) {
|
|
|
2177
2253
|
const notice = RUNTIME_NOTICES[code];
|
|
2178
2254
|
const durationMs = fields.durationMs === undefined ? undefined : Math.max(0, Math.trunc(fields.durationMs));
|
|
2179
2255
|
const messageKey = durationMs !== undefined && 'durationMessageKey' in notice ? notice.durationMessageKey : notice.messageKey;
|
|
2180
|
-
target[notice.level]?.(`[${code}] ${localize(target, messageKey, { durationMs: durationMs ?? 0 })}`);
|
|
2256
|
+
target[notice.level]?.(`[${code}] ${localize(target, messageKey, { durationMs: durationMs ?? 0, finishBy: fields.finishBy ?? '' })}`);
|
|
2181
2257
|
target.debug?.(JSON.stringify({
|
|
2182
2258
|
scope: 'runtime-notice',
|
|
2183
2259
|
level: notice.level,
|
|
@@ -2192,14 +2268,17 @@ export function reportRuntimeNotice(target, code, fields = {}) {
|
|
|
2192
2268
|
* The file is the authority: the supplied identifier only names which window the caller believes was opened,
|
|
2193
2269
|
* and a session the file does not hold as active changes nothing and reports nothing. A confirmed pickup emits
|
|
2194
2270
|
* one operational record, which is the evidence that this process began retaining the window's scopes, taken
|
|
2195
|
-
* at the moment it did rather than whenever a later record arrives
|
|
2271
|
+
* at the moment it did rather than whenever a later record arrives, and its console line names the time the
|
|
2272
|
+
* capture stops on its own.
|
|
2196
2273
|
*/
|
|
2197
2274
|
export function armDiagnosticsAuthorization(target, storageRoot, supportCaseId, now = Date.now) {
|
|
2198
|
-
|
|
2199
|
-
|
|
2275
|
+
const session = activeDiagnosticsSession(storageRoot, now());
|
|
2276
|
+
if (!isSupportCaseId(supportCaseId) || session?.supportCaseId !== supportCaseId) {
|
|
2200
2277
|
return false;
|
|
2201
2278
|
}
|
|
2202
|
-
reportRuntimeNotice(target, 'diagnostics-authorization-armed'
|
|
2279
|
+
reportRuntimeNotice(target, 'diagnostics-authorization-armed', {
|
|
2280
|
+
finishBy: new Date(session.expiresAt).toLocaleString(),
|
|
2281
|
+
});
|
|
2203
2282
|
return true;
|
|
2204
2283
|
}
|
|
2205
2284
|
/** Emits the startup notice for discarded settings awaiting acknowledgement. */
|
|
@@ -2573,7 +2652,11 @@ export class DiagnosticConditions {
|
|
|
2573
2652
|
const alias = this.aliases.get(serial);
|
|
2574
2653
|
return name === undefined ? undefined : alias === undefined ? name : `${name} (${alias})`;
|
|
2575
2654
|
})
|
|
2576
|
-
.filter((named) => named !== undefined));
|
|
2655
|
+
.filter((named) => named !== undefined), uniqueDeviceIds);
|
|
2656
|
+
}
|
|
2657
|
+
/** Every condition active now, each held from the transition that raised it to the one that clears it. */
|
|
2658
|
+
current() {
|
|
2659
|
+
return [...this.active.values()].map(({ condition }) => condition);
|
|
2577
2660
|
}
|
|
2578
2661
|
/**
|
|
2579
2662
|
* The support-case alias for one device identity, minted on first use.
|
|
@@ -2591,13 +2674,16 @@ export class DiagnosticConditions {
|
|
|
2591
2674
|
}
|
|
2592
2675
|
write(code, active, reason, summaryKey, actionKey, level, fields = {}, conditionKey = code,
|
|
2593
2676
|
/** Accessory names for the console line only — never retained, see {@link DiagnosticConditions}. */
|
|
2594
|
-
names) {
|
|
2595
|
-
const fingerprint = JSON.stringify({ active, reason, ...fields });
|
|
2677
|
+
names, serials = []) {
|
|
2678
|
+
const fingerprint = JSON.stringify({ active, reason, ...fields, serials });
|
|
2596
2679
|
if (active) {
|
|
2597
|
-
if (this.active.get(conditionKey) === fingerprint) {
|
|
2680
|
+
if (this.active.get(conditionKey)?.fingerprint === fingerprint) {
|
|
2598
2681
|
return;
|
|
2599
2682
|
}
|
|
2600
|
-
this.active.set(conditionKey,
|
|
2683
|
+
this.active.set(conditionKey, {
|
|
2684
|
+
fingerprint,
|
|
2685
|
+
condition: { code, summaryKey, actionKey, ...(serials.length === 0 ? {} : { serials: [...serials] }) },
|
|
2686
|
+
});
|
|
2601
2687
|
}
|
|
2602
2688
|
else if (!this.active.delete(conditionKey)) {
|
|
2603
2689
|
return;
|