@camstack/addon-post-analysis 1.2.321 → 1.2.322
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/dist/{clip-model-registry-BCx3qCL5.js → clip-model-registry-BFbP9U7z.js} +338 -0
- package/dist/{clip-model-registry-Dnm9dfy0.mjs → clip-model-registry-CwK2SLQD.mjs} +285 -1
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CPcZN3mA.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Dmt5eh93.mjs} +2 -2
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CGUnHrR_.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-mzYK6V9S.mjs} +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CuOcvcK7.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-Dt-jaGuG.mjs → hostInit-DtKO_7UT.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +269 -3
- package/dist/pipeline-analytics/index.mjs +269 -3
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CVH5tT7I.mjs +0 -26
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-BFbP9U7z.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1948,6 +1948,21 @@ var ncAlarmPanelSchema = require_clip_model_registry.object({
|
|
|
1948
1948
|
exitDelaySec: require_clip_model_registry.number().optional(),
|
|
1949
1949
|
entryDelaySec: require_clip_model_registry.number().optional(),
|
|
1950
1950
|
triggeredDurationSec: require_clip_model_registry.number().optional()
|
|
1951
|
+
})).default([]),
|
|
1952
|
+
/**
|
|
1953
|
+
* Active alarm configuration profile id. Empty / absent on a blob older than
|
|
1954
|
+
* profiles → seeded to Default on first read via {@link ensureAlarmProfiles}.
|
|
1955
|
+
*/
|
|
1956
|
+
activeProfileId: require_clip_model_registry.string().default(require_clip_model_registry.NC_ALARM_DEFAULT_PROFILE_ID),
|
|
1957
|
+
/**
|
|
1958
|
+
* Named settings blobs. Loose on purpose — each profile's settings are
|
|
1959
|
+
* re-validated through {@link settingsBlobFromPersisted} so a corrupt entry
|
|
1960
|
+
* drops rather than taking down the panel.
|
|
1961
|
+
*/
|
|
1962
|
+
profiles: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
1963
|
+
id: require_clip_model_registry.string(),
|
|
1964
|
+
name: require_clip_model_registry.string(),
|
|
1965
|
+
settings: require_clip_model_registry.record(require_clip_model_registry.string(), require_clip_model_registry.unknown())
|
|
1951
1966
|
})).default([])
|
|
1952
1967
|
});
|
|
1953
1968
|
/**
|
|
@@ -1970,6 +1985,106 @@ function modeDeviceListFromPersisted(raw) {
|
|
|
1970
1985
|
return lists;
|
|
1971
1986
|
}
|
|
1972
1987
|
/**
|
|
1988
|
+
* Rebuild a trusted {@link NcAlarmSettings} from a persisted profile blob.
|
|
1989
|
+
* Unknown / corrupt fields fall back to system-safe empties via the flat-key
|
|
1990
|
+
* readers the panel already uses for the live settings.
|
|
1991
|
+
*/
|
|
1992
|
+
function settingsBlobFromPersisted(raw) {
|
|
1993
|
+
const exit = typeof raw["exitDelaySec"] === "number" ? raw["exitDelaySec"] : DEFAULT_DELAYS.exitDelaySec;
|
|
1994
|
+
const entry = typeof raw["entryDelaySec"] === "number" ? raw["entryDelaySec"] : DEFAULT_DELAYS.entryDelaySec;
|
|
1995
|
+
const triggered = typeof raw["triggeredDurationSec"] === "number" ? raw["triggeredDurationSec"] : DEFAULT_DELAYS.triggeredDurationSec;
|
|
1996
|
+
const announceTargets = Array.isArray(raw["announceTargets"]) ? raw["announceTargets"].filter((t) => typeof t === "string") : [];
|
|
1997
|
+
const lists = (key) => {
|
|
1998
|
+
const v = raw[key];
|
|
1999
|
+
if (!Array.isArray(v)) return [];
|
|
2000
|
+
const rows = [];
|
|
2001
|
+
for (const item of v) {
|
|
2002
|
+
if (typeof item !== "object" || item === null) continue;
|
|
2003
|
+
const rec = item;
|
|
2004
|
+
if (typeof rec["mode"] !== "string" || !Array.isArray(rec["deviceIds"])) continue;
|
|
2005
|
+
rows.push({
|
|
2006
|
+
mode: rec["mode"],
|
|
2007
|
+
deviceIds: rec["deviceIds"].filter((id) => typeof id === "number")
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
return modeDeviceListFromPersisted(rows);
|
|
2011
|
+
};
|
|
2012
|
+
const modeDelaysRaw = Array.isArray(raw["modeDelays"]) ? raw["modeDelays"] : [];
|
|
2013
|
+
const modeDelaysLoose = [];
|
|
2014
|
+
for (const item of modeDelaysRaw) {
|
|
2015
|
+
if (typeof item !== "object" || item === null) continue;
|
|
2016
|
+
const rec = item;
|
|
2017
|
+
if (typeof rec["mode"] !== "string") continue;
|
|
2018
|
+
const entry = { mode: rec["mode"] };
|
|
2019
|
+
if (typeof rec["exitDelaySec"] === "number") entry.exitDelaySec = rec["exitDelaySec"];
|
|
2020
|
+
if (typeof rec["entryDelaySec"] === "number") entry.entryDelaySec = rec["entryDelaySec"];
|
|
2021
|
+
if (typeof rec["triggeredDurationSec"] === "number") entry.triggeredDurationSec = rec["triggeredDurationSec"];
|
|
2022
|
+
modeDelaysLoose.push(entry);
|
|
2023
|
+
}
|
|
2024
|
+
return {
|
|
2025
|
+
exitDelaySec: Math.max(0, Math.min(600, Math.trunc(exit))),
|
|
2026
|
+
entryDelaySec: Math.max(0, Math.min(600, Math.trunc(entry))),
|
|
2027
|
+
triggeredDurationSec: Math.max(0, Math.min(3600, Math.trunc(triggered))),
|
|
2028
|
+
announceTargets,
|
|
2029
|
+
nonBlocking: lists("nonBlocking"),
|
|
2030
|
+
blocking: lists("blocking"),
|
|
2031
|
+
secureOnArmOff: lists("secureOnArmOff"),
|
|
2032
|
+
modeDelays: modeDelaysFromPersisted(modeDelaysLoose).entries
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
function settingsToPersisted(settings) {
|
|
2036
|
+
return {
|
|
2037
|
+
exitDelaySec: settings.exitDelaySec,
|
|
2038
|
+
entryDelaySec: settings.entryDelaySec,
|
|
2039
|
+
triggeredDurationSec: settings.triggeredDurationSec,
|
|
2040
|
+
announceTargets: [...settings.announceTargets],
|
|
2041
|
+
nonBlocking: settings.nonBlocking.map((l) => ({
|
|
2042
|
+
mode: l.mode,
|
|
2043
|
+
deviceIds: [...l.deviceIds]
|
|
2044
|
+
})),
|
|
2045
|
+
blocking: settings.blocking.map((l) => ({
|
|
2046
|
+
mode: l.mode,
|
|
2047
|
+
deviceIds: [...l.deviceIds]
|
|
2048
|
+
})),
|
|
2049
|
+
secureOnArmOff: settings.secureOnArmOff.map((l) => ({
|
|
2050
|
+
mode: l.mode,
|
|
2051
|
+
deviceIds: [...l.deviceIds]
|
|
2052
|
+
})),
|
|
2053
|
+
modeDelays: settings.modeDelays.map((e) => ({ ...e }))
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
function profilesStateFromPersisted(activeId, rows, current) {
|
|
2057
|
+
const profiles = rows.filter((r) => r.id.length > 0 && r.name.trim().length > 0).map((r) => ({
|
|
2058
|
+
id: r.id,
|
|
2059
|
+
name: r.name,
|
|
2060
|
+
settings: settingsBlobFromPersisted(r.settings)
|
|
2061
|
+
}));
|
|
2062
|
+
return require_clip_model_registry.ensureAlarmProfiles(profiles.length > 0 ? {
|
|
2063
|
+
activeId,
|
|
2064
|
+
profiles
|
|
2065
|
+
} : null, current);
|
|
2066
|
+
}
|
|
2067
|
+
function profilesToPersisted(state) {
|
|
2068
|
+
return {
|
|
2069
|
+
activeProfileId: state.activeId,
|
|
2070
|
+
profiles: state.profiles.map((p) => ({
|
|
2071
|
+
id: p.id,
|
|
2072
|
+
name: p.name,
|
|
2073
|
+
settings: settingsToPersisted(p.settings)
|
|
2074
|
+
}))
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function profilesViewOf(state) {
|
|
2078
|
+
return {
|
|
2079
|
+
activeId: state.activeId,
|
|
2080
|
+
items: state.profiles.map((p) => ({
|
|
2081
|
+
id: p.id,
|
|
2082
|
+
name: p.name,
|
|
2083
|
+
isDefault: require_clip_model_registry.isDefaultAlarmProfileId(p.id)
|
|
2084
|
+
}))
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
1973
2088
|
* The log line for a trigger the state machine ignored, per state. `trigger`
|
|
1974
2089
|
* ignores four: disarmed, arming (the exit delay), and pending / triggered
|
|
1975
2090
|
* (already counting down or going off — a second sensor must not restart it).
|
|
@@ -2217,7 +2332,134 @@ var NcAlarmPanelDevice = class extends require_clip_model_registry.BaseDevice {
|
|
|
2217
2332
|
changed: membershipChanged
|
|
2218
2333
|
}
|
|
2219
2334
|
});
|
|
2220
|
-
|
|
2335
|
+
const settled = this.settings();
|
|
2336
|
+
await this.persistProfilesState(require_clip_model_registry.syncActiveAlarmProfileSettings(this.readProfilesState(), settled));
|
|
2337
|
+
return settled;
|
|
2338
|
+
}
|
|
2339
|
+
/** Named profiles + which one is active — for {@link NcAlarmConfig.profiles}. */
|
|
2340
|
+
profilesView() {
|
|
2341
|
+
return profilesViewOf(this.readProfilesState());
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Create a named profile (system defaults, or clone). Does not activate.
|
|
2345
|
+
* `newId` is assigned by the caller (usually `randomUUID`).
|
|
2346
|
+
*/
|
|
2347
|
+
async createProfile(input) {
|
|
2348
|
+
const result = require_clip_model_registry.createAlarmProfile(this.readProfilesState(), {
|
|
2349
|
+
name: input.name,
|
|
2350
|
+
id: input.newId,
|
|
2351
|
+
source: input.source
|
|
2352
|
+
});
|
|
2353
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2354
|
+
await this.persistProfilesState(result.state);
|
|
2355
|
+
this.ctx.logger.info("alarm profile created", {
|
|
2356
|
+
tags: { deviceId: this.id },
|
|
2357
|
+
meta: {
|
|
2358
|
+
profileId: input.newId,
|
|
2359
|
+
name: input.name.trim(),
|
|
2360
|
+
source: input.source.kind
|
|
2361
|
+
}
|
|
2362
|
+
});
|
|
2363
|
+
return profilesViewOf(result.state);
|
|
2364
|
+
}
|
|
2365
|
+
async renameProfile(id, name) {
|
|
2366
|
+
const result = require_clip_model_registry.renameAlarmProfile(this.readProfilesState(), {
|
|
2367
|
+
id,
|
|
2368
|
+
name
|
|
2369
|
+
});
|
|
2370
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2371
|
+
await this.persistProfilesState(result.state);
|
|
2372
|
+
this.ctx.logger.info("alarm profile renamed", {
|
|
2373
|
+
tags: { deviceId: this.id },
|
|
2374
|
+
meta: {
|
|
2375
|
+
profileId: id,
|
|
2376
|
+
name: name.trim()
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2379
|
+
return profilesViewOf(result.state);
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* Delete a non-default profile. If it was active, Default becomes active and
|
|
2383
|
+
* its settings are applied to the flat keys (arm path).
|
|
2384
|
+
*/
|
|
2385
|
+
async deleteProfile(id) {
|
|
2386
|
+
const before = this.readProfilesState();
|
|
2387
|
+
const result = require_clip_model_registry.deleteAlarmProfile(before, id);
|
|
2388
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2389
|
+
const switched = before.activeId === id;
|
|
2390
|
+
await this.persistProfilesState(result.state);
|
|
2391
|
+
if (switched) {
|
|
2392
|
+
const def = result.state.profiles.find((p) => p.id === require_clip_model_registry.NC_ALARM_DEFAULT_PROFILE_ID);
|
|
2393
|
+
if (def !== void 0) await this.replaceFlatSettings(def.settings);
|
|
2394
|
+
}
|
|
2395
|
+
this.ctx.logger.info("alarm profile deleted", {
|
|
2396
|
+
tags: { deviceId: this.id },
|
|
2397
|
+
meta: {
|
|
2398
|
+
profileId: id,
|
|
2399
|
+
activatedDefault: switched
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
return profilesViewOf(result.state);
|
|
2403
|
+
}
|
|
2404
|
+
/**
|
|
2405
|
+
* Make a profile active and apply its blob onto the flat settings the arm
|
|
2406
|
+
* path reads.
|
|
2407
|
+
*/
|
|
2408
|
+
async activateProfile(id) {
|
|
2409
|
+
const result = require_clip_model_registry.activateAlarmProfile(require_clip_model_registry.syncActiveAlarmProfileSettings(this.readProfilesState(), this.settings()), id);
|
|
2410
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2411
|
+
const next = result.state.profiles.find((p) => p.id === id);
|
|
2412
|
+
if (next === void 0) throw this.profilesRefuseError("not-found", id);
|
|
2413
|
+
await this.persistProfilesState(result.state);
|
|
2414
|
+
await this.replaceFlatSettings(next.settings);
|
|
2415
|
+
this.ctx.logger.info("alarm profile activated", {
|
|
2416
|
+
tags: { deviceId: this.id },
|
|
2417
|
+
meta: {
|
|
2418
|
+
profileId: id,
|
|
2419
|
+
name: next.name
|
|
2420
|
+
}
|
|
2421
|
+
});
|
|
2422
|
+
return profilesViewOf(result.state);
|
|
2423
|
+
}
|
|
2424
|
+
readProfilesState() {
|
|
2425
|
+
return profilesStateFromPersisted(this.config.get("activeProfileId"), this.config.get("profiles"), this.settings());
|
|
2426
|
+
}
|
|
2427
|
+
async persistProfilesState(state) {
|
|
2428
|
+
const persisted = profilesToPersisted(state);
|
|
2429
|
+
await Promise.all([this.config.set("activeProfileId", persisted.activeProfileId), this.config.set("profiles", persisted.profiles)]);
|
|
2430
|
+
}
|
|
2431
|
+
/** Write a full settings blob onto the flat keys the arm path reads. */
|
|
2432
|
+
async replaceFlatSettings(settings) {
|
|
2433
|
+
const blob = require_clip_model_registry.cloneAlarmSettings(settings);
|
|
2434
|
+
await Promise.all([
|
|
2435
|
+
this.config.set("exitDelaySec", blob.exitDelaySec),
|
|
2436
|
+
this.config.set("entryDelaySec", blob.entryDelaySec),
|
|
2437
|
+
this.config.set("triggeredDurationSec", blob.triggeredDurationSec),
|
|
2438
|
+
this.config.set("announceTargets", [...blob.announceTargets]),
|
|
2439
|
+
this.config.set("nonBlocking", blob.nonBlocking.map((l) => ({
|
|
2440
|
+
mode: l.mode,
|
|
2441
|
+
deviceIds: [...l.deviceIds]
|
|
2442
|
+
}))),
|
|
2443
|
+
this.config.set("blocking", blob.blocking.map((l) => ({
|
|
2444
|
+
mode: l.mode,
|
|
2445
|
+
deviceIds: [...l.deviceIds]
|
|
2446
|
+
}))),
|
|
2447
|
+
this.config.set("secureOnArmOff", blob.secureOnArmOff.map((l) => ({
|
|
2448
|
+
mode: l.mode,
|
|
2449
|
+
deviceIds: [...l.deviceIds]
|
|
2450
|
+
}))),
|
|
2451
|
+
this.config.set("modeDelays", blob.modeDelays.map((e) => ({ ...e })))
|
|
2452
|
+
]);
|
|
2453
|
+
}
|
|
2454
|
+
profilesRefuseError(reason, detail) {
|
|
2455
|
+
switch (reason) {
|
|
2456
|
+
case "name-empty": return /* @__PURE__ */ new Error("alarm profile name must not be empty");
|
|
2457
|
+
case "name-taken": return /* @__PURE__ */ new Error(`an alarm profile named '${detail ?? ""}' already exists`);
|
|
2458
|
+
case "cannot-delete-default": return /* @__PURE__ */ new Error("the Default alarm profile cannot be deleted");
|
|
2459
|
+
case "clone-source-missing": return /* @__PURE__ */ new Error(`alarm profile '${detail ?? ""}' was not found to clone`);
|
|
2460
|
+
case "not-found": return /* @__PURE__ */ new Error(`alarm profile '${detail ?? ""}' was not found`);
|
|
2461
|
+
default: return /* @__PURE__ */ new Error(`alarm profile operation refused (${reason})`);
|
|
2462
|
+
}
|
|
2221
2463
|
}
|
|
2222
2464
|
/**
|
|
2223
2465
|
* Refuse a mode this panel does not offer — logged by reason, then thrown. A membership edit
|
|
@@ -2950,6 +3192,11 @@ function ncAlarmPanelPort(panel) {
|
|
|
2950
3192
|
deviceId: panel.id,
|
|
2951
3193
|
settings: () => panel.settings(),
|
|
2952
3194
|
applySettings: (patch) => panel.applySettings(patch),
|
|
3195
|
+
profilesView: () => panel.profilesView(),
|
|
3196
|
+
createProfile: (input) => panel.createProfile(input),
|
|
3197
|
+
renameProfile: (id, name) => panel.renameProfile(id, name),
|
|
3198
|
+
deleteProfile: (id) => panel.deleteProfile(id),
|
|
3199
|
+
activateProfile: (id) => panel.activateProfile(id),
|
|
2953
3200
|
availableModes: () => NC_ALARM_MODES,
|
|
2954
3201
|
noteRuleTrigger: (source) => panel.noteRuleTrigger(source),
|
|
2955
3202
|
excludedDevices: () => panel.excludedDevices(),
|
|
@@ -39304,7 +39551,18 @@ var NotificationCenter = class {
|
|
|
39304
39551
|
},
|
|
39305
39552
|
getAlarmConfig: async () => this.alarmConfig(),
|
|
39306
39553
|
getAlarmSensors: async (input) => this.alarmSensors(input),
|
|
39307
|
-
setAlarmConfig: async ({ patch }) => this.setAlarmConfig(patch)
|
|
39554
|
+
setAlarmConfig: async ({ patch }) => this.setAlarmConfig(patch),
|
|
39555
|
+
createAlarmProfile: async ({ name, cloneFromId }) => this.mutateAlarmProfile((panel) => panel.createProfile({
|
|
39556
|
+
name,
|
|
39557
|
+
newId: (0, node_crypto.randomUUID)(),
|
|
39558
|
+
source: cloneFromId === void 0 ? { kind: "defaults" } : {
|
|
39559
|
+
kind: "clone",
|
|
39560
|
+
fromId: cloneFromId
|
|
39561
|
+
}
|
|
39562
|
+
})),
|
|
39563
|
+
renameAlarmProfile: async ({ id, name }) => this.mutateAlarmProfile((panel) => panel.renameProfile(id, name)),
|
|
39564
|
+
deleteAlarmProfile: async ({ id }) => this.mutateAlarmProfile((panel) => panel.deleteProfile(id)),
|
|
39565
|
+
activateAlarmProfile: async ({ id }) => this.mutateAlarmProfile((panel) => panel.activateProfile(id))
|
|
39308
39566
|
};
|
|
39309
39567
|
}
|
|
39310
39568
|
/**
|
|
@@ -39597,6 +39855,13 @@ var NotificationCenter = class {
|
|
|
39597
39855
|
});
|
|
39598
39856
|
return this.alarmConfig();
|
|
39599
39857
|
}
|
|
39858
|
+
/** Profile CRUD — each mutation returns the whole config (settings applied). */
|
|
39859
|
+
async mutateAlarmProfile(run) {
|
|
39860
|
+
const panel = this.alarmPanel;
|
|
39861
|
+
if (panel === null) throw new Error("this node has no alarm panel");
|
|
39862
|
+
await run(panel);
|
|
39863
|
+
return this.alarmConfig();
|
|
39864
|
+
}
|
|
39600
39865
|
/**
|
|
39601
39866
|
* The panel as it is NOW — see `alarm-live.ts`.
|
|
39602
39867
|
*
|
|
@@ -39693,6 +39958,7 @@ var NotificationCenter = class {
|
|
|
39693
39958
|
return {
|
|
39694
39959
|
deviceId: panel.deviceId,
|
|
39695
39960
|
settings: panel.settings(),
|
|
39961
|
+
profiles: panel.profilesView(),
|
|
39696
39962
|
live: await this.alarmLive(panel),
|
|
39697
39963
|
coverage: this.coverageFor(panel).map((c) => ({
|
|
39698
39964
|
...c,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as NC_ALARM_DELAY_MAX_SEC, $n as isEventOwnerType, $r as defineCustomActions, $t as SCENE_DEFAULT_QUIET_SECONDS, A as EMBEDDING_REQUIREMENTS_FAILED, An as consumableLowPercentOf, Ar as servedTemplateVars, At as NcRuleUpdateInputSchema, B as ListTrackRelationsInputSchema, Br as templateVarsFor, Bt as NcTemplateVarDescriptorSchema, C as DEFAULT_EVENT_COLOR, Cn as applyNonBlockingEdit, Cr as ruleBelongsToAlarm$1, Ct as NcAlarmSettingsPatchSchema, D as DeclaredDevices, Dn as audioModeOf, Dr as sceneMonitorCapability, Dt as NcRulePatchSchema, E as DETECTION_PIPELINE_CAP_NAME, En as audioMetricsCapability, Er as ruleKindOf, Et as NcRuleInputSchema, F as EXPORT_DENSE_MAX_RANGES, Fn as describeSystemEventDeliveryProblem, Fr as systemEventFamilyProblems, Ft as NcStateSummarySectionIdSchema, G as MAX_BIRTH_LATENCY_PROXY_MS, Gn as faceGalleryCapability, Gr as evaluateSensorEdge, Gt as RECORDING_SOURCE_CAMSTACK_ADDON, H as LockStateSchema, Hn as encodeVectorBase64, Hr as zoneAnalyticsCapability, Ht as OpsLogEntrySchema, I as FIRST_LEVEL_MACRO_CLASSES, In as describeSystemEventFamilyProblem, Ir as systemEventFilterApplies, It as NcSystemEventKindSchema, J as MOTION_CLOSE_AFTER_MS, Jn as gifFromMp4Args, Jr as errMsg, Jt as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, K as MAX_TRACK_PURITIES, Kn as failureContributionCapability, Kr as isSourceCap, Kt as RebuildObjectEmbeddingsRefusalSchema, L as FULL_IMAGE_BBOX, Lr as systemEventSummaryScheduleOf, Lt as NcTaxonomySchema, M as EVENT_DENSITY_BATCH_MAX, Mn as createAlarmProfile, Mr as subKindsOf, Mt as NcSnoozeInputSchema, N as EVENT_KIND_BY_CAP, Nn as deleteAlarmProfile, Nr as syncActiveAlarmProfileSettings, Nt as NcSnoozeSchema, O as DetectionSourceSchema, On as buildEventKindDescriptor, Or as secureActuationFor, Ot as NcRuleSchema, P as EVENT_OWNER_TYPES, Pn as deriveRecordingMode, Pr as systemEventDeliveryProblems, Pt as NcSnoozeSuppressedSchema, Q as NC_ALARM_DELAY_FIELDS, Qn as isDetectionMacroClass, Qr as customAction, Qt as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, R as FailureCounters, Rr as systemEventSummarySplitOf, Rt as NcTemplatePreviewInputSchema, S as CoverStateSchema, Si as EventCategory, Sn as applyModeDelaysEdits, St as NcAlarmSensorsSchema, T as DETECTION_MACRO_CLASSES, Tn as assertTimelapseCadences, Tr as ruleGroupingApplies, Tt as NcConditionsSchema, U as MAX_ARCHIVED_DEBUG_NOTES, Un as ensureAlarmProfiles, Ur as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, V as ListTrackRelationsResultSchema, Vr as vectorDimFromBase64, Vt as OPS_LOG_DEFAULT_LIMIT, W as MAX_BIRTH_DECISION_RECORDS, Wn as evictionPolicyOfLocation, Wr as canonicalHash, Wt as RECORDING_EXPORT_MAX_READ_BYTES, X as MotionTriggeredBySchema, Xn as isAudioClassName, Xr as CamProfileSchema, Xt as SCENE_DEFAULT_ANCHOR_THRESHOLD, Y as MediaFileKindEnum, Yn as isAlarmControllableCap, Yr as BaseAddon, Yt as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, Z as NC_ALARM_DEFAULT_PROFILE_ID, Zn as isDefaultAlarmProfileId, Zr as createEvent, Zt as SCENE_DEFAULT_CHECK_INTERVAL_SEC, _ as BaseDevice, _i as string, _n as activateAlarmProfile, _r as readTimelapseGeneratedAt, _t as NC_TANK_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, ai as ZodObject, an as SetTrackPurityResultSchema, ar as isSystemEventSummaryRule, at as NC_CLIP_PRE_ROLL_DEFAULT_SEC, b as ClearTrackRelationInputSchema, bi as union, bn as alarmPanelCapability, br as resolveLocationMode, bt as NcAlarmConfigSchema, c as resolveClusterModelPin, ci as boolean, cn as TRACK_RELATIONS_DEFAULT_LIMIT, cr as mayWriteToLocation, ct as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, d as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, di as looseObject, dn as TimelapseRuleSchema, dr as occupancyScope, dt as NC_GROUPING_DEFAULT_SEC, ei as hydrateSchema, en as SCENE_DEFAULT_UNCOVERED_POLICY, er as isKnownTemplateVar, et as NC_ALARM_SYSTEM_EVENT_KINDS, f as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, fi as number, fn as TrackPuritySchema, ft as NC_GROUPING_MAX_SEC, g as BIRTH_DECISION_DEFAULT_LIMIT, gi as strictObject, gn as TrackSourceSchema, gr as readMotionTriggeredBy, gt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, h as ArchivedDebugNoteSchema, hi as record, hn as TrackSideSchema, hr as readDeviceStateFrom, ht as NC_SNOOZE_MAX_MINUTES, ii as sleep$1, in as SetTrackPurityInputSchema, ir as isSystemDelivery, it as NC_CLIP_POST_ROLL_DEFAULT_SEC, j as EMBEDDING_REQUIREMENTS_UNAVAILABLE, jn as cosineSimilarity$1, jr as storageOccupancyCapability, jt as NcScheduleSchema, k as EMBEDDING_MODEL_FAILED, kn as cloneAlarmSettings, kr as sensorRaisedOf, kt as NcRuleTargetSchema, l as APPEARANCE_BIN_COUNT, li as discriminatedUnion, ln as TimelapseRuleInputSchema, lr as memoizePinnedFfmpeg, lt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, m as AlarmArmModeSchema, mi as partialRecord, mn as TrackRelationSchema, mr as plateGalleryCapability, mt as NC_SENSOR_TRANSITION_KINDS, ni as nodePin, nn as SCENE_RESET_RECAPTURES, nr as isRetiredClearKey, nt as NC_AUDIO_CONFIRM_HITS_DEFAULT, o as FACE_EMBEDDING_STEP_ID, oi as _enum, on as SetTrackRelationInputSchema, or as isVectorIndexDimConflict, ot as NC_CLIP_RETENTION_MAX_SEC, p as AUDIO_MACRO_LABELS, pi as object, pn as TrackRelationKindSchema, pr as pipelineAnalyticsCapability, pt as NC_GROUPING_MIN_SEC, q as MAX_TRACK_RELATIONS, qn as gifDeriveSpeed, qr as DeviceType, qt as RetrainStatusSchema, rn as SceneMonitorSchema, rr as isScheduleActive, rt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, s as readClusterModelPinStrict, si as array, sn as TIMELAPSE_DENSE_FLOOR_SEC, sr as kebabToCamel, st as NC_CONDITION_CATALOG, t as BUILTIN_CLIP_MODELS, ti as isDeviceScopedCap, tn as SCENE_DIVERGED, tr as isMediaOwnerType, tt as NC_ALARM_TRIGGERED_MAX_SEC, u as APPEARANCE_BLOCKS, ui as literal, un as TimelapseRulePatchSchema, ur as notificationRulesCapability, ut as NC_DEFAULT_SNOOZE_MINUTES, v as BirthDecisionRecordSchema, vi as templateLiteral, vn as addonWidgetsSourceCapability, vr as renameAlarmProfile, vt as NC_TAXONOMY, w as DEFAULT_TIMELAPSE_PREVIEW_TEXT, wn as applySecureOnArmEdit, wr as ruleDeviceCompatCapQueries, wt as NcConditionDescriptorSchema, x as ClearTrackRelationResultSchema, xi as unknown, xn as applyBlockingEdit, xr as resolveModeDelays, xt as NcAlarmSensorsInputSchema, y as COCO_TO_MACRO, yi as tuple, yn as alarmDeviceSecureOnArm, yr as resolveClipSearchMinScore, yt as NC_TEMPLATE_VARS, z as LabelAttributionSchema, zn as embeddingRefusalOf, zr as templateCarriesSummary, zt as NcTemplatePreviewSchema } from "../clip-model-registry-CwK2SLQD.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -1941,6 +1941,21 @@ var ncAlarmPanelSchema = object({
|
|
|
1941
1941
|
exitDelaySec: number().optional(),
|
|
1942
1942
|
entryDelaySec: number().optional(),
|
|
1943
1943
|
triggeredDurationSec: number().optional()
|
|
1944
|
+
})).default([]),
|
|
1945
|
+
/**
|
|
1946
|
+
* Active alarm configuration profile id. Empty / absent on a blob older than
|
|
1947
|
+
* profiles → seeded to Default on first read via {@link ensureAlarmProfiles}.
|
|
1948
|
+
*/
|
|
1949
|
+
activeProfileId: string().default(NC_ALARM_DEFAULT_PROFILE_ID),
|
|
1950
|
+
/**
|
|
1951
|
+
* Named settings blobs. Loose on purpose — each profile's settings are
|
|
1952
|
+
* re-validated through {@link settingsBlobFromPersisted} so a corrupt entry
|
|
1953
|
+
* drops rather than taking down the panel.
|
|
1954
|
+
*/
|
|
1955
|
+
profiles: array(object({
|
|
1956
|
+
id: string(),
|
|
1957
|
+
name: string(),
|
|
1958
|
+
settings: record(string(), unknown())
|
|
1944
1959
|
})).default([])
|
|
1945
1960
|
});
|
|
1946
1961
|
/**
|
|
@@ -1963,6 +1978,106 @@ function modeDeviceListFromPersisted(raw) {
|
|
|
1963
1978
|
return lists;
|
|
1964
1979
|
}
|
|
1965
1980
|
/**
|
|
1981
|
+
* Rebuild a trusted {@link NcAlarmSettings} from a persisted profile blob.
|
|
1982
|
+
* Unknown / corrupt fields fall back to system-safe empties via the flat-key
|
|
1983
|
+
* readers the panel already uses for the live settings.
|
|
1984
|
+
*/
|
|
1985
|
+
function settingsBlobFromPersisted(raw) {
|
|
1986
|
+
const exit = typeof raw["exitDelaySec"] === "number" ? raw["exitDelaySec"] : DEFAULT_DELAYS.exitDelaySec;
|
|
1987
|
+
const entry = typeof raw["entryDelaySec"] === "number" ? raw["entryDelaySec"] : DEFAULT_DELAYS.entryDelaySec;
|
|
1988
|
+
const triggered = typeof raw["triggeredDurationSec"] === "number" ? raw["triggeredDurationSec"] : DEFAULT_DELAYS.triggeredDurationSec;
|
|
1989
|
+
const announceTargets = Array.isArray(raw["announceTargets"]) ? raw["announceTargets"].filter((t) => typeof t === "string") : [];
|
|
1990
|
+
const lists = (key) => {
|
|
1991
|
+
const v = raw[key];
|
|
1992
|
+
if (!Array.isArray(v)) return [];
|
|
1993
|
+
const rows = [];
|
|
1994
|
+
for (const item of v) {
|
|
1995
|
+
if (typeof item !== "object" || item === null) continue;
|
|
1996
|
+
const rec = item;
|
|
1997
|
+
if (typeof rec["mode"] !== "string" || !Array.isArray(rec["deviceIds"])) continue;
|
|
1998
|
+
rows.push({
|
|
1999
|
+
mode: rec["mode"],
|
|
2000
|
+
deviceIds: rec["deviceIds"].filter((id) => typeof id === "number")
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
return modeDeviceListFromPersisted(rows);
|
|
2004
|
+
};
|
|
2005
|
+
const modeDelaysRaw = Array.isArray(raw["modeDelays"]) ? raw["modeDelays"] : [];
|
|
2006
|
+
const modeDelaysLoose = [];
|
|
2007
|
+
for (const item of modeDelaysRaw) {
|
|
2008
|
+
if (typeof item !== "object" || item === null) continue;
|
|
2009
|
+
const rec = item;
|
|
2010
|
+
if (typeof rec["mode"] !== "string") continue;
|
|
2011
|
+
const entry = { mode: rec["mode"] };
|
|
2012
|
+
if (typeof rec["exitDelaySec"] === "number") entry.exitDelaySec = rec["exitDelaySec"];
|
|
2013
|
+
if (typeof rec["entryDelaySec"] === "number") entry.entryDelaySec = rec["entryDelaySec"];
|
|
2014
|
+
if (typeof rec["triggeredDurationSec"] === "number") entry.triggeredDurationSec = rec["triggeredDurationSec"];
|
|
2015
|
+
modeDelaysLoose.push(entry);
|
|
2016
|
+
}
|
|
2017
|
+
return {
|
|
2018
|
+
exitDelaySec: Math.max(0, Math.min(600, Math.trunc(exit))),
|
|
2019
|
+
entryDelaySec: Math.max(0, Math.min(600, Math.trunc(entry))),
|
|
2020
|
+
triggeredDurationSec: Math.max(0, Math.min(3600, Math.trunc(triggered))),
|
|
2021
|
+
announceTargets,
|
|
2022
|
+
nonBlocking: lists("nonBlocking"),
|
|
2023
|
+
blocking: lists("blocking"),
|
|
2024
|
+
secureOnArmOff: lists("secureOnArmOff"),
|
|
2025
|
+
modeDelays: modeDelaysFromPersisted(modeDelaysLoose).entries
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
function settingsToPersisted(settings) {
|
|
2029
|
+
return {
|
|
2030
|
+
exitDelaySec: settings.exitDelaySec,
|
|
2031
|
+
entryDelaySec: settings.entryDelaySec,
|
|
2032
|
+
triggeredDurationSec: settings.triggeredDurationSec,
|
|
2033
|
+
announceTargets: [...settings.announceTargets],
|
|
2034
|
+
nonBlocking: settings.nonBlocking.map((l) => ({
|
|
2035
|
+
mode: l.mode,
|
|
2036
|
+
deviceIds: [...l.deviceIds]
|
|
2037
|
+
})),
|
|
2038
|
+
blocking: settings.blocking.map((l) => ({
|
|
2039
|
+
mode: l.mode,
|
|
2040
|
+
deviceIds: [...l.deviceIds]
|
|
2041
|
+
})),
|
|
2042
|
+
secureOnArmOff: settings.secureOnArmOff.map((l) => ({
|
|
2043
|
+
mode: l.mode,
|
|
2044
|
+
deviceIds: [...l.deviceIds]
|
|
2045
|
+
})),
|
|
2046
|
+
modeDelays: settings.modeDelays.map((e) => ({ ...e }))
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
function profilesStateFromPersisted(activeId, rows, current) {
|
|
2050
|
+
const profiles = rows.filter((r) => r.id.length > 0 && r.name.trim().length > 0).map((r) => ({
|
|
2051
|
+
id: r.id,
|
|
2052
|
+
name: r.name,
|
|
2053
|
+
settings: settingsBlobFromPersisted(r.settings)
|
|
2054
|
+
}));
|
|
2055
|
+
return ensureAlarmProfiles(profiles.length > 0 ? {
|
|
2056
|
+
activeId,
|
|
2057
|
+
profiles
|
|
2058
|
+
} : null, current);
|
|
2059
|
+
}
|
|
2060
|
+
function profilesToPersisted(state) {
|
|
2061
|
+
return {
|
|
2062
|
+
activeProfileId: state.activeId,
|
|
2063
|
+
profiles: state.profiles.map((p) => ({
|
|
2064
|
+
id: p.id,
|
|
2065
|
+
name: p.name,
|
|
2066
|
+
settings: settingsToPersisted(p.settings)
|
|
2067
|
+
}))
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
function profilesViewOf(state) {
|
|
2071
|
+
return {
|
|
2072
|
+
activeId: state.activeId,
|
|
2073
|
+
items: state.profiles.map((p) => ({
|
|
2074
|
+
id: p.id,
|
|
2075
|
+
name: p.name,
|
|
2076
|
+
isDefault: isDefaultAlarmProfileId(p.id)
|
|
2077
|
+
}))
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
1966
2081
|
* The log line for a trigger the state machine ignored, per state. `trigger`
|
|
1967
2082
|
* ignores four: disarmed, arming (the exit delay), and pending / triggered
|
|
1968
2083
|
* (already counting down or going off — a second sensor must not restart it).
|
|
@@ -2210,7 +2325,134 @@ var NcAlarmPanelDevice = class extends BaseDevice {
|
|
|
2210
2325
|
changed: membershipChanged
|
|
2211
2326
|
}
|
|
2212
2327
|
});
|
|
2213
|
-
|
|
2328
|
+
const settled = this.settings();
|
|
2329
|
+
await this.persistProfilesState(syncActiveAlarmProfileSettings(this.readProfilesState(), settled));
|
|
2330
|
+
return settled;
|
|
2331
|
+
}
|
|
2332
|
+
/** Named profiles + which one is active — for {@link NcAlarmConfig.profiles}. */
|
|
2333
|
+
profilesView() {
|
|
2334
|
+
return profilesViewOf(this.readProfilesState());
|
|
2335
|
+
}
|
|
2336
|
+
/**
|
|
2337
|
+
* Create a named profile (system defaults, or clone). Does not activate.
|
|
2338
|
+
* `newId` is assigned by the caller (usually `randomUUID`).
|
|
2339
|
+
*/
|
|
2340
|
+
async createProfile(input) {
|
|
2341
|
+
const result = createAlarmProfile(this.readProfilesState(), {
|
|
2342
|
+
name: input.name,
|
|
2343
|
+
id: input.newId,
|
|
2344
|
+
source: input.source
|
|
2345
|
+
});
|
|
2346
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2347
|
+
await this.persistProfilesState(result.state);
|
|
2348
|
+
this.ctx.logger.info("alarm profile created", {
|
|
2349
|
+
tags: { deviceId: this.id },
|
|
2350
|
+
meta: {
|
|
2351
|
+
profileId: input.newId,
|
|
2352
|
+
name: input.name.trim(),
|
|
2353
|
+
source: input.source.kind
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2356
|
+
return profilesViewOf(result.state);
|
|
2357
|
+
}
|
|
2358
|
+
async renameProfile(id, name) {
|
|
2359
|
+
const result = renameAlarmProfile(this.readProfilesState(), {
|
|
2360
|
+
id,
|
|
2361
|
+
name
|
|
2362
|
+
});
|
|
2363
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2364
|
+
await this.persistProfilesState(result.state);
|
|
2365
|
+
this.ctx.logger.info("alarm profile renamed", {
|
|
2366
|
+
tags: { deviceId: this.id },
|
|
2367
|
+
meta: {
|
|
2368
|
+
profileId: id,
|
|
2369
|
+
name: name.trim()
|
|
2370
|
+
}
|
|
2371
|
+
});
|
|
2372
|
+
return profilesViewOf(result.state);
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Delete a non-default profile. If it was active, Default becomes active and
|
|
2376
|
+
* its settings are applied to the flat keys (arm path).
|
|
2377
|
+
*/
|
|
2378
|
+
async deleteProfile(id) {
|
|
2379
|
+
const before = this.readProfilesState();
|
|
2380
|
+
const result = deleteAlarmProfile(before, id);
|
|
2381
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2382
|
+
const switched = before.activeId === id;
|
|
2383
|
+
await this.persistProfilesState(result.state);
|
|
2384
|
+
if (switched) {
|
|
2385
|
+
const def = result.state.profiles.find((p) => p.id === NC_ALARM_DEFAULT_PROFILE_ID);
|
|
2386
|
+
if (def !== void 0) await this.replaceFlatSettings(def.settings);
|
|
2387
|
+
}
|
|
2388
|
+
this.ctx.logger.info("alarm profile deleted", {
|
|
2389
|
+
tags: { deviceId: this.id },
|
|
2390
|
+
meta: {
|
|
2391
|
+
profileId: id,
|
|
2392
|
+
activatedDefault: switched
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2395
|
+
return profilesViewOf(result.state);
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Make a profile active and apply its blob onto the flat settings the arm
|
|
2399
|
+
* path reads.
|
|
2400
|
+
*/
|
|
2401
|
+
async activateProfile(id) {
|
|
2402
|
+
const result = activateAlarmProfile(syncActiveAlarmProfileSettings(this.readProfilesState(), this.settings()), id);
|
|
2403
|
+
if (result.kind === "refused") throw this.profilesRefuseError(result.reason, result.detail);
|
|
2404
|
+
const next = result.state.profiles.find((p) => p.id === id);
|
|
2405
|
+
if (next === void 0) throw this.profilesRefuseError("not-found", id);
|
|
2406
|
+
await this.persistProfilesState(result.state);
|
|
2407
|
+
await this.replaceFlatSettings(next.settings);
|
|
2408
|
+
this.ctx.logger.info("alarm profile activated", {
|
|
2409
|
+
tags: { deviceId: this.id },
|
|
2410
|
+
meta: {
|
|
2411
|
+
profileId: id,
|
|
2412
|
+
name: next.name
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2415
|
+
return profilesViewOf(result.state);
|
|
2416
|
+
}
|
|
2417
|
+
readProfilesState() {
|
|
2418
|
+
return profilesStateFromPersisted(this.config.get("activeProfileId"), this.config.get("profiles"), this.settings());
|
|
2419
|
+
}
|
|
2420
|
+
async persistProfilesState(state) {
|
|
2421
|
+
const persisted = profilesToPersisted(state);
|
|
2422
|
+
await Promise.all([this.config.set("activeProfileId", persisted.activeProfileId), this.config.set("profiles", persisted.profiles)]);
|
|
2423
|
+
}
|
|
2424
|
+
/** Write a full settings blob onto the flat keys the arm path reads. */
|
|
2425
|
+
async replaceFlatSettings(settings) {
|
|
2426
|
+
const blob = cloneAlarmSettings(settings);
|
|
2427
|
+
await Promise.all([
|
|
2428
|
+
this.config.set("exitDelaySec", blob.exitDelaySec),
|
|
2429
|
+
this.config.set("entryDelaySec", blob.entryDelaySec),
|
|
2430
|
+
this.config.set("triggeredDurationSec", blob.triggeredDurationSec),
|
|
2431
|
+
this.config.set("announceTargets", [...blob.announceTargets]),
|
|
2432
|
+
this.config.set("nonBlocking", blob.nonBlocking.map((l) => ({
|
|
2433
|
+
mode: l.mode,
|
|
2434
|
+
deviceIds: [...l.deviceIds]
|
|
2435
|
+
}))),
|
|
2436
|
+
this.config.set("blocking", blob.blocking.map((l) => ({
|
|
2437
|
+
mode: l.mode,
|
|
2438
|
+
deviceIds: [...l.deviceIds]
|
|
2439
|
+
}))),
|
|
2440
|
+
this.config.set("secureOnArmOff", blob.secureOnArmOff.map((l) => ({
|
|
2441
|
+
mode: l.mode,
|
|
2442
|
+
deviceIds: [...l.deviceIds]
|
|
2443
|
+
}))),
|
|
2444
|
+
this.config.set("modeDelays", blob.modeDelays.map((e) => ({ ...e })))
|
|
2445
|
+
]);
|
|
2446
|
+
}
|
|
2447
|
+
profilesRefuseError(reason, detail) {
|
|
2448
|
+
switch (reason) {
|
|
2449
|
+
case "name-empty": return /* @__PURE__ */ new Error("alarm profile name must not be empty");
|
|
2450
|
+
case "name-taken": return /* @__PURE__ */ new Error(`an alarm profile named '${detail ?? ""}' already exists`);
|
|
2451
|
+
case "cannot-delete-default": return /* @__PURE__ */ new Error("the Default alarm profile cannot be deleted");
|
|
2452
|
+
case "clone-source-missing": return /* @__PURE__ */ new Error(`alarm profile '${detail ?? ""}' was not found to clone`);
|
|
2453
|
+
case "not-found": return /* @__PURE__ */ new Error(`alarm profile '${detail ?? ""}' was not found`);
|
|
2454
|
+
default: return /* @__PURE__ */ new Error(`alarm profile operation refused (${reason})`);
|
|
2455
|
+
}
|
|
2214
2456
|
}
|
|
2215
2457
|
/**
|
|
2216
2458
|
* Refuse a mode this panel does not offer — logged by reason, then thrown. A membership edit
|
|
@@ -2943,6 +3185,11 @@ function ncAlarmPanelPort(panel) {
|
|
|
2943
3185
|
deviceId: panel.id,
|
|
2944
3186
|
settings: () => panel.settings(),
|
|
2945
3187
|
applySettings: (patch) => panel.applySettings(patch),
|
|
3188
|
+
profilesView: () => panel.profilesView(),
|
|
3189
|
+
createProfile: (input) => panel.createProfile(input),
|
|
3190
|
+
renameProfile: (id, name) => panel.renameProfile(id, name),
|
|
3191
|
+
deleteProfile: (id) => panel.deleteProfile(id),
|
|
3192
|
+
activateProfile: (id) => panel.activateProfile(id),
|
|
2946
3193
|
availableModes: () => NC_ALARM_MODES,
|
|
2947
3194
|
noteRuleTrigger: (source) => panel.noteRuleTrigger(source),
|
|
2948
3195
|
excludedDevices: () => panel.excludedDevices(),
|
|
@@ -39281,7 +39528,18 @@ var NotificationCenter = class {
|
|
|
39281
39528
|
},
|
|
39282
39529
|
getAlarmConfig: async () => this.alarmConfig(),
|
|
39283
39530
|
getAlarmSensors: async (input) => this.alarmSensors(input),
|
|
39284
|
-
setAlarmConfig: async ({ patch }) => this.setAlarmConfig(patch)
|
|
39531
|
+
setAlarmConfig: async ({ patch }) => this.setAlarmConfig(patch),
|
|
39532
|
+
createAlarmProfile: async ({ name, cloneFromId }) => this.mutateAlarmProfile((panel) => panel.createProfile({
|
|
39533
|
+
name,
|
|
39534
|
+
newId: randomUUID(),
|
|
39535
|
+
source: cloneFromId === void 0 ? { kind: "defaults" } : {
|
|
39536
|
+
kind: "clone",
|
|
39537
|
+
fromId: cloneFromId
|
|
39538
|
+
}
|
|
39539
|
+
})),
|
|
39540
|
+
renameAlarmProfile: async ({ id, name }) => this.mutateAlarmProfile((panel) => panel.renameProfile(id, name)),
|
|
39541
|
+
deleteAlarmProfile: async ({ id }) => this.mutateAlarmProfile((panel) => panel.deleteProfile(id)),
|
|
39542
|
+
activateAlarmProfile: async ({ id }) => this.mutateAlarmProfile((panel) => panel.activateProfile(id))
|
|
39285
39543
|
};
|
|
39286
39544
|
}
|
|
39287
39545
|
/**
|
|
@@ -39574,6 +39832,13 @@ var NotificationCenter = class {
|
|
|
39574
39832
|
});
|
|
39575
39833
|
return this.alarmConfig();
|
|
39576
39834
|
}
|
|
39835
|
+
/** Profile CRUD — each mutation returns the whole config (settings applied). */
|
|
39836
|
+
async mutateAlarmProfile(run) {
|
|
39837
|
+
const panel = this.alarmPanel;
|
|
39838
|
+
if (panel === null) throw new Error("this node has no alarm panel");
|
|
39839
|
+
await run(panel);
|
|
39840
|
+
return this.alarmConfig();
|
|
39841
|
+
}
|
|
39577
39842
|
/**
|
|
39578
39843
|
* The panel as it is NOW — see `alarm-live.ts`.
|
|
39579
39844
|
*
|
|
@@ -39670,6 +39935,7 @@ var NotificationCenter = class {
|
|
|
39670
39935
|
return {
|
|
39671
39936
|
deviceId: panel.deviceId,
|
|
39672
39937
|
settings: panel.settings(),
|
|
39938
|
+
profiles: panel.profilesView(),
|
|
39673
39939
|
live: await this.alarmLive(panel),
|
|
39674
39940
|
coverage: this.coverageFor(panel).map((c) => ({
|
|
39675
39941
|
...c,
|