@camstack/system 1.1.53 → 1.1.54
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/addon-runner.js +2 -2
- package/dist/addon-runner.mjs +2 -2
- package/dist/addon-utils.d.ts +1 -0
- package/dist/addon-utils.js +2 -0
- package/dist/addon-utils.mjs +2 -2
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +46 -37
- package/dist/builtins/device-manager/device-manager.addon.mjs +46 -37
- package/dist/builtins/device-manager/device-store-sections.d.ts +26 -0
- package/dist/builtins/doorbell/doorbell-settings.d.ts +58 -0
- package/dist/builtins/doorbell/trigger-engine.d.ts +32 -30
- package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +39 -22
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +207 -191
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +207 -191
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-BNro5CRu.js → dist-B5C4dVvO.js} +201 -7
- package/dist/{dist-D7_69kDt.mjs → dist-DssImRC5.mjs} +201 -7
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{manifest-python-deps-EM2H-jjS.mjs → manifest-python-deps-CWom-eqp.mjs} +1 -1
- package/dist/{manifest-python-deps-DszE7bYn.js → manifest-python-deps-kkvku9yU.js} +1 -1
- package/package.json +2 -2
package/dist/addon-runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
3
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
2
|
+
const require_dist = require("./dist-B5C4dVvO.js");
|
|
3
|
+
const require_manifest_python_deps = require("./manifest-python-deps-kkvku9yU.js");
|
|
4
4
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
6
6
|
node_fs = require_chunk.__toESM(node_fs);
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { j as isCollectionArrayMethod, t as ALL_CAPABILITY_DEFINITIONS } from "./dist-
|
|
2
|
-
import { F as createUdsLoggerWithControl, I as LocalChildClient, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps } from "./manifest-python-deps-
|
|
1
|
+
import { j as isCollectionArrayMethod, t as ALL_CAPABILITY_DEFINITIONS } from "./dist-DssImRC5.mjs";
|
|
2
|
+
import { F as createUdsLoggerWithControl, I as LocalChildClient, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps } from "./manifest-python-deps-CWom-eqp.mjs";
|
|
3
3
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
4
4
|
import { register } from "node:module";
|
|
5
5
|
import * as fs from "node:fs";
|
package/dist/addon-utils.d.ts
CHANGED
|
@@ -17,4 +17,5 @@
|
|
|
17
17
|
export { downloadFile, ensureModel, isModelDownloaded, deleteModelFromDisk, collectModelFiles, getModelFilePath, } from './download/model-downloader.js';
|
|
18
18
|
export { ModelDownloadService } from './download/model-download-service.js';
|
|
19
19
|
export { createFileDataPlaneHandler } from './http/file-data-plane.js';
|
|
20
|
+
export { parseRangeHeader, contentTypeFor, type ByteRange, } from './http/authenticated-file-server.js';
|
|
20
21
|
export { CustomActionRegistry } from './kernel/custom-action-registry.js';
|
package/dist/addon-utils.js
CHANGED
|
@@ -5,9 +5,11 @@ const require_custom_action_registry = require("./custom-action-registry-vLYEFTt
|
|
|
5
5
|
exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
|
|
6
6
|
exports.ModelDownloadService = require_model_download_service.ModelDownloadService;
|
|
7
7
|
exports.collectModelFiles = require_model_download_service.collectModelFiles;
|
|
8
|
+
exports.contentTypeFor = require_model_download_service.contentTypeFor;
|
|
8
9
|
exports.createFileDataPlaneHandler = require_model_download_service.createFileDataPlaneHandler;
|
|
9
10
|
exports.deleteModelFromDisk = require_model_download_service.deleteModelFromDisk;
|
|
10
11
|
exports.downloadFile = require_model_download_service.downloadFile;
|
|
11
12
|
exports.ensureModel = require_model_download_service.ensureModel;
|
|
12
13
|
exports.getModelFilePath = require_model_download_service.getModelFilePath;
|
|
13
14
|
exports.isModelDownloaded = require_model_download_service.isModelDownloaded;
|
|
15
|
+
exports.parseRangeHeader = require_model_download_service.parseRangeHeader;
|
package/dist/addon-utils.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { c as getModelFilePath, i as downloadFile, l as isModelDownloaded, n as collectModelFiles, o as ensureModel, r as deleteModelFromDisk, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
|
|
1
|
+
import { c as getModelFilePath, d as contentTypeFor, i as downloadFile, l as isModelDownloaded, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
3
|
-
export { CustomActionRegistry, ModelDownloadService, collectModelFiles, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, getModelFilePath, isModelDownloaded };
|
|
3
|
+
export { CustomActionRegistry, ModelDownloadService, collectModelFiles, contentTypeFor, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, getModelFilePath, isModelDownloaded, parseRangeHeader };
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-B5C4dVvO.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as addonPagesCapability, it as BaseAddon, kt as EventCategory, rt as errMsg } from "../../dist-
|
|
1
|
+
import { f as addonPagesCapability, it as BaseAddon, kt as EventCategory, rt as errMsg } from "../../dist-DssImRC5.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-B5C4dVvO.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { it as BaseAddon, kt as EventCategory, m as addonWidgetsCapability, rt as errMsg } from "../../dist-
|
|
1
|
+
import { it as BaseAddon, kt as EventCategory, m as addonWidgetsCapability, rt as errMsg } from "../../dist-DssImRC5.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-B5C4dVvO.js");
|
|
7
7
|
//#region src/builtins/alerts/alerts.addon.ts
|
|
8
8
|
var CATEGORY_MAPPINGS = {
|
|
9
9
|
"addon.crashed": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _t as createEvent, h as alertsCapability, it as BaseAddon, kt as EventCategory, n as AlertSchema, rt as errMsg } from "../../dist-
|
|
1
|
+
import { _t as createEvent, h as alertsCapability, it as BaseAddon, kt as EventCategory, n as AlertSchema, rt as errMsg } from "../../dist-DssImRC5.mjs";
|
|
2
2
|
//#region src/builtins/alerts/alerts.addon.ts
|
|
3
3
|
var CATEGORY_MAPPINGS = {
|
|
4
4
|
"addon.crashed": {
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-B5C4dVvO.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
let node_fs$1 = require_chunk.__toESM(node_fs, 1);
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { it as BaseAddon, kt as EventCategory, v as backupCapability } from "../../dist-
|
|
1
|
+
import { it as BaseAddon, kt as EventCategory, v as backupCapability } from "../../dist-DssImRC5.mjs";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import Kt from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-B5C4dVvO.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/console-logging/console-destination.ts
|
|
9
9
|
var LEVEL_RANK = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { L as logDestinationCapability, it as BaseAddon } from "../../dist-
|
|
1
|
+
import { L as logDestinationCapability, it as BaseAddon } from "../../dist-DssImRC5.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/console-logging/console-destination.ts
|
|
4
4
|
var LEVEL_RANK = {
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-B5C4dVvO.js");
|
|
7
7
|
let _camstack_types_node = require("@camstack/types/node");
|
|
8
8
|
let zod = require("zod");
|
|
9
9
|
let node_crypto = require("node:crypto");
|
|
@@ -1333,6 +1333,39 @@ async function applyLinkedDevicesPatch(pctx, deviceId, patch) {
|
|
|
1333
1333
|
return { success: true };
|
|
1334
1334
|
}
|
|
1335
1335
|
//#endregion
|
|
1336
|
+
//#region src/builtins/device-manager/device-store-sections.ts
|
|
1337
|
+
/** The device-manager's device-store-backed sections. Add an entry to extend. */
|
|
1338
|
+
var DEVICE_STORE_SECTIONS = [{
|
|
1339
|
+
keys: [LINKED_MODE_CONFIG_KEY, LINKED_IDS_CONFIG_KEY],
|
|
1340
|
+
apply: (pctx, deviceId, patch) => applyLinkedDevicesPatch(pctx, deviceId, patch)
|
|
1341
|
+
}];
|
|
1342
|
+
/**
|
|
1343
|
+
* Split a `writerCapName: 'device-manager'` patch into device-store slices
|
|
1344
|
+
* (one per matching registered section) and the leftover driver patch.
|
|
1345
|
+
* Generic — consults {@link DEVICE_STORE_SECTIONS}, no hardcoded key list.
|
|
1346
|
+
*/
|
|
1347
|
+
function splitDeviceStoreKeys(patch) {
|
|
1348
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1349
|
+
const storeGroups = [];
|
|
1350
|
+
for (const section of DEVICE_STORE_SECTIONS) {
|
|
1351
|
+
const slice = {};
|
|
1352
|
+
for (const key of section.keys) if (key in patch) {
|
|
1353
|
+
slice[key] = patch[key];
|
|
1354
|
+
claimed.add(key);
|
|
1355
|
+
}
|
|
1356
|
+
if (Object.keys(slice).length > 0) storeGroups.push({
|
|
1357
|
+
section,
|
|
1358
|
+
patch: slice
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
const driverPatch = {};
|
|
1362
|
+
for (const [key, value] of Object.entries(patch)) if (!claimed.has(key)) driverPatch[key] = value;
|
|
1363
|
+
return {
|
|
1364
|
+
storeGroups,
|
|
1365
|
+
driverPatch
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
//#endregion
|
|
1336
1369
|
//#region src/builtins/device-manager/device-bindings-store.ts
|
|
1337
1370
|
/**
|
|
1338
1371
|
* Persisted wrapper-activation + per-device binding resolution for the
|
|
@@ -2031,40 +2064,9 @@ async function dispatchDerivedFormSave(builderId, deviceId, patch, dcProvider, c
|
|
|
2031
2064
|
throw new Error(`[device-manager] device-config cap "${capName}" provider exposes neither setSettings nor setProfile`);
|
|
2032
2065
|
}
|
|
2033
2066
|
async function updateDeviceField(deps, input) {
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
if (!settings) throw new Error("[device-manager] linked-devices save requires the settings surface");
|
|
2038
|
-
await applyLinkedDevicesPatch({ settings }, input.deviceId, { [input.key]: input.value });
|
|
2039
|
-
return { success: true };
|
|
2040
|
-
}
|
|
2041
|
-
const hubRegistry = deps.ctx.kernel?.deviceRegistry;
|
|
2042
|
-
const found = hubRegistry ? resolveDeviceById(hubRegistry, input.deviceId) : null;
|
|
2043
|
-
if (found) {
|
|
2044
|
-
await found.device.applySettingsPatch({ [input.key]: input.value });
|
|
2045
|
-
return { success: true };
|
|
2046
|
-
}
|
|
2047
|
-
const ops = deps.capabilityRegistry?.getNativeProvider("device-ops", input.deviceId);
|
|
2048
|
-
if (!ops) throw new Error(`[device-manager] device "${input.deviceId}" not found (no hub-local entry, no device-ops native provider)`);
|
|
2049
|
-
await ops.setConfig({
|
|
2050
|
-
deviceId: input.deviceId,
|
|
2051
|
-
values: { [input.key]: input.value }
|
|
2052
|
-
});
|
|
2053
|
-
return { success: true };
|
|
2054
|
-
}
|
|
2055
|
-
const registry = deps.capabilityRegistry;
|
|
2056
|
-
if (!registry) throw new Error("[device-manager] updateDeviceField requires capability registry — unavailable on this node");
|
|
2057
|
-
const def = registry.getDefinition(input.writerCapName);
|
|
2058
|
-
if (require_dist.isDeviceConfigCap(def)) {
|
|
2059
|
-
if (def.deviceConfig.ui.kind === "widget") return { success: true };
|
|
2060
|
-
const dcProvider = registry.getProviderForDevice(input.writerCapName, input.deviceId);
|
|
2061
|
-
if (!dcProvider) throw new Error(`[device-manager] no provider for device-config cap "${input.writerCapName}" on device ${input.deviceId}`);
|
|
2062
|
-
await dispatchDerivedFormSave(def.deviceConfig.ui.builderId, input.deviceId, { [input.key]: input.value }, dcProvider, input.writerCapName);
|
|
2063
|
-
return { success: true };
|
|
2064
|
-
}
|
|
2065
|
-
if (!def?.exposesDeviceSettings) throw new Error(`[device-manager] cap "${input.writerCapName}" does not expose device settings`);
|
|
2066
|
-
await resolveContributionProvider(registry, def, input.writerCapName, input.writerAddonId, input.deviceId).applyDeviceSettingsPatch({
|
|
2067
|
-
deviceId: input.deviceId,
|
|
2067
|
+
await applyGroupPatch(deps, input.deviceId, {
|
|
2068
|
+
writerCapName: input.writerCapName,
|
|
2069
|
+
writerAddonId: input.writerAddonId,
|
|
2068
2070
|
patch: { [input.key]: input.value }
|
|
2069
2071
|
});
|
|
2070
2072
|
return { success: true };
|
|
@@ -2157,17 +2159,24 @@ async function updateDeviceFieldsBatch(deps, input) {
|
|
|
2157
2159
|
* registry lookup). Used by `updateDeviceFieldsBatch`. */
|
|
2158
2160
|
async function applyGroupPatch(deps, deviceId, group) {
|
|
2159
2161
|
if (group.writerCapName === "device-manager") {
|
|
2162
|
+
const { storeGroups, driverPatch } = splitDeviceStoreKeys(group.patch);
|
|
2163
|
+
if (storeGroups.length > 0) {
|
|
2164
|
+
const settings = deps.ctx.settings;
|
|
2165
|
+
if (!settings) throw new Error("[device-manager] device-store save requires the settings surface");
|
|
2166
|
+
for (const { section, patch } of storeGroups) await section.apply({ settings }, deviceId, patch);
|
|
2167
|
+
}
|
|
2168
|
+
if (Object.keys(driverPatch).length === 0) return;
|
|
2160
2169
|
const hubRegistry = deps.ctx.kernel?.deviceRegistry;
|
|
2161
2170
|
const found = hubRegistry ? resolveDeviceById(hubRegistry, deviceId) : null;
|
|
2162
2171
|
if (found) {
|
|
2163
|
-
await found.device.applySettingsPatch(
|
|
2172
|
+
await found.device.applySettingsPatch(driverPatch);
|
|
2164
2173
|
return;
|
|
2165
2174
|
}
|
|
2166
2175
|
const ops = deps.capabilityRegistry?.getNativeProvider("device-ops", deviceId);
|
|
2167
2176
|
if (!ops) throw new Error(`[device-manager] device "${deviceId}" not found (no hub-local entry, no device-ops native provider)`);
|
|
2168
2177
|
await ops.setConfig({
|
|
2169
2178
|
deviceId,
|
|
2170
|
-
values:
|
|
2179
|
+
values: driverPatch
|
|
2171
2180
|
});
|
|
2172
2181
|
return;
|
|
2173
2182
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as evaluateLinkExpression, J as setByPath, Ot as sleep, S as deviceStatusCapability, St as isDeviceConfigCap, T as enumerateSchemaFields, U as parseStreamParamsFormPatch, V as normalizeUnit, a as DeviceStatusSchema, b as deviceManagerCapability, c as STREAM_PROFILE_META, ct as DeviceFeature, et as toExpressionValue, g as applyTransform, i as CAP_NAMES_WITH_STATUS, it as BaseAddon, k as getByPath, kt as EventCategory, lt as DeviceRole, nt as validateExpressionSource, pt as WELL_KNOWN_TAB_MAP, rt as errMsg, t as ALL_CAPABILITY_DEFINITIONS, ut as DeviceType, w as enumerateItemArrayFields, x as deviceStateCapability, y as buildStreamParamsConfigSchema } from "../../dist-
|
|
1
|
+
import { E as evaluateLinkExpression, J as setByPath, Ot as sleep, S as deviceStatusCapability, St as isDeviceConfigCap, T as enumerateSchemaFields, U as parseStreamParamsFormPatch, V as normalizeUnit, a as DeviceStatusSchema, b as deviceManagerCapability, c as STREAM_PROFILE_META, ct as DeviceFeature, et as toExpressionValue, g as applyTransform, i as CAP_NAMES_WITH_STATUS, it as BaseAddon, k as getByPath, kt as EventCategory, lt as DeviceRole, nt as validateExpressionSource, pt as WELL_KNOWN_TAB_MAP, rt as errMsg, t as ALL_CAPABILITY_DEFINITIONS, ut as DeviceType, w as enumerateItemArrayFields, x as deviceStateCapability, y as buildStreamParamsConfigSchema } from "../../dist-DssImRC5.mjs";
|
|
2
2
|
import { canonicalDeviceFingerprint } from "@camstack/types/node";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -1328,6 +1328,39 @@ async function applyLinkedDevicesPatch(pctx, deviceId, patch) {
|
|
|
1328
1328
|
return { success: true };
|
|
1329
1329
|
}
|
|
1330
1330
|
//#endregion
|
|
1331
|
+
//#region src/builtins/device-manager/device-store-sections.ts
|
|
1332
|
+
/** The device-manager's device-store-backed sections. Add an entry to extend. */
|
|
1333
|
+
var DEVICE_STORE_SECTIONS = [{
|
|
1334
|
+
keys: [LINKED_MODE_CONFIG_KEY, LINKED_IDS_CONFIG_KEY],
|
|
1335
|
+
apply: (pctx, deviceId, patch) => applyLinkedDevicesPatch(pctx, deviceId, patch)
|
|
1336
|
+
}];
|
|
1337
|
+
/**
|
|
1338
|
+
* Split a `writerCapName: 'device-manager'` patch into device-store slices
|
|
1339
|
+
* (one per matching registered section) and the leftover driver patch.
|
|
1340
|
+
* Generic — consults {@link DEVICE_STORE_SECTIONS}, no hardcoded key list.
|
|
1341
|
+
*/
|
|
1342
|
+
function splitDeviceStoreKeys(patch) {
|
|
1343
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1344
|
+
const storeGroups = [];
|
|
1345
|
+
for (const section of DEVICE_STORE_SECTIONS) {
|
|
1346
|
+
const slice = {};
|
|
1347
|
+
for (const key of section.keys) if (key in patch) {
|
|
1348
|
+
slice[key] = patch[key];
|
|
1349
|
+
claimed.add(key);
|
|
1350
|
+
}
|
|
1351
|
+
if (Object.keys(slice).length > 0) storeGroups.push({
|
|
1352
|
+
section,
|
|
1353
|
+
patch: slice
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
const driverPatch = {};
|
|
1357
|
+
for (const [key, value] of Object.entries(patch)) if (!claimed.has(key)) driverPatch[key] = value;
|
|
1358
|
+
return {
|
|
1359
|
+
storeGroups,
|
|
1360
|
+
driverPatch
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
//#endregion
|
|
1331
1364
|
//#region src/builtins/device-manager/device-bindings-store.ts
|
|
1332
1365
|
/**
|
|
1333
1366
|
* Persisted wrapper-activation + per-device binding resolution for the
|
|
@@ -2026,40 +2059,9 @@ async function dispatchDerivedFormSave(builderId, deviceId, patch, dcProvider, c
|
|
|
2026
2059
|
throw new Error(`[device-manager] device-config cap "${capName}" provider exposes neither setSettings nor setProfile`);
|
|
2027
2060
|
}
|
|
2028
2061
|
async function updateDeviceField(deps, input) {
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
if (!settings) throw new Error("[device-manager] linked-devices save requires the settings surface");
|
|
2033
|
-
await applyLinkedDevicesPatch({ settings }, input.deviceId, { [input.key]: input.value });
|
|
2034
|
-
return { success: true };
|
|
2035
|
-
}
|
|
2036
|
-
const hubRegistry = deps.ctx.kernel?.deviceRegistry;
|
|
2037
|
-
const found = hubRegistry ? resolveDeviceById(hubRegistry, input.deviceId) : null;
|
|
2038
|
-
if (found) {
|
|
2039
|
-
await found.device.applySettingsPatch({ [input.key]: input.value });
|
|
2040
|
-
return { success: true };
|
|
2041
|
-
}
|
|
2042
|
-
const ops = deps.capabilityRegistry?.getNativeProvider("device-ops", input.deviceId);
|
|
2043
|
-
if (!ops) throw new Error(`[device-manager] device "${input.deviceId}" not found (no hub-local entry, no device-ops native provider)`);
|
|
2044
|
-
await ops.setConfig({
|
|
2045
|
-
deviceId: input.deviceId,
|
|
2046
|
-
values: { [input.key]: input.value }
|
|
2047
|
-
});
|
|
2048
|
-
return { success: true };
|
|
2049
|
-
}
|
|
2050
|
-
const registry = deps.capabilityRegistry;
|
|
2051
|
-
if (!registry) throw new Error("[device-manager] updateDeviceField requires capability registry — unavailable on this node");
|
|
2052
|
-
const def = registry.getDefinition(input.writerCapName);
|
|
2053
|
-
if (isDeviceConfigCap(def)) {
|
|
2054
|
-
if (def.deviceConfig.ui.kind === "widget") return { success: true };
|
|
2055
|
-
const dcProvider = registry.getProviderForDevice(input.writerCapName, input.deviceId);
|
|
2056
|
-
if (!dcProvider) throw new Error(`[device-manager] no provider for device-config cap "${input.writerCapName}" on device ${input.deviceId}`);
|
|
2057
|
-
await dispatchDerivedFormSave(def.deviceConfig.ui.builderId, input.deviceId, { [input.key]: input.value }, dcProvider, input.writerCapName);
|
|
2058
|
-
return { success: true };
|
|
2059
|
-
}
|
|
2060
|
-
if (!def?.exposesDeviceSettings) throw new Error(`[device-manager] cap "${input.writerCapName}" does not expose device settings`);
|
|
2061
|
-
await resolveContributionProvider(registry, def, input.writerCapName, input.writerAddonId, input.deviceId).applyDeviceSettingsPatch({
|
|
2062
|
-
deviceId: input.deviceId,
|
|
2062
|
+
await applyGroupPatch(deps, input.deviceId, {
|
|
2063
|
+
writerCapName: input.writerCapName,
|
|
2064
|
+
writerAddonId: input.writerAddonId,
|
|
2063
2065
|
patch: { [input.key]: input.value }
|
|
2064
2066
|
});
|
|
2065
2067
|
return { success: true };
|
|
@@ -2152,17 +2154,24 @@ async function updateDeviceFieldsBatch(deps, input) {
|
|
|
2152
2154
|
* registry lookup). Used by `updateDeviceFieldsBatch`. */
|
|
2153
2155
|
async function applyGroupPatch(deps, deviceId, group) {
|
|
2154
2156
|
if (group.writerCapName === "device-manager") {
|
|
2157
|
+
const { storeGroups, driverPatch } = splitDeviceStoreKeys(group.patch);
|
|
2158
|
+
if (storeGroups.length > 0) {
|
|
2159
|
+
const settings = deps.ctx.settings;
|
|
2160
|
+
if (!settings) throw new Error("[device-manager] device-store save requires the settings surface");
|
|
2161
|
+
for (const { section, patch } of storeGroups) await section.apply({ settings }, deviceId, patch);
|
|
2162
|
+
}
|
|
2163
|
+
if (Object.keys(driverPatch).length === 0) return;
|
|
2155
2164
|
const hubRegistry = deps.ctx.kernel?.deviceRegistry;
|
|
2156
2165
|
const found = hubRegistry ? resolveDeviceById(hubRegistry, deviceId) : null;
|
|
2157
2166
|
if (found) {
|
|
2158
|
-
await found.device.applySettingsPatch(
|
|
2167
|
+
await found.device.applySettingsPatch(driverPatch);
|
|
2159
2168
|
return;
|
|
2160
2169
|
}
|
|
2161
2170
|
const ops = deps.capabilityRegistry?.getNativeProvider("device-ops", deviceId);
|
|
2162
2171
|
if (!ops) throw new Error(`[device-manager] device "${deviceId}" not found (no hub-local entry, no device-ops native provider)`);
|
|
2163
2172
|
await ops.setConfig({
|
|
2164
2173
|
deviceId,
|
|
2165
|
-
values:
|
|
2174
|
+
values: driverPatch
|
|
2166
2175
|
});
|
|
2167
2176
|
return;
|
|
2168
2177
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ProviderContext } from './device-provider-context.js';
|
|
2
|
+
/** One device-store-backed section: the keys it owns + how to persist them. */
|
|
3
|
+
export interface DeviceStoreSection {
|
|
4
|
+
/** Reserved config keys this section owns (must not reach the driver). */
|
|
5
|
+
readonly keys: readonly string[];
|
|
6
|
+
/** Persist this section's slice of a save into the device-store blob. */
|
|
7
|
+
apply(pctx: Pick<ProviderContext, 'settings'>, deviceId: number, patch: Record<string, unknown>): Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
/** The device-manager's device-store-backed sections. Add an entry to extend. */
|
|
10
|
+
export declare const DEVICE_STORE_SECTIONS: readonly DeviceStoreSection[];
|
|
11
|
+
/** Result of splitting a `device-manager`-writer patch by persistence target. */
|
|
12
|
+
export interface DeviceStoreSplit {
|
|
13
|
+
/** Per-section slices that persist to the device-store blob. */
|
|
14
|
+
readonly storeGroups: ReadonlyArray<{
|
|
15
|
+
section: DeviceStoreSection;
|
|
16
|
+
patch: Record<string, unknown>;
|
|
17
|
+
}>;
|
|
18
|
+
/** The remaining keys, destined for the device driver's own config. */
|
|
19
|
+
readonly driverPatch: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Split a `writerCapName: 'device-manager'` patch into device-store slices
|
|
23
|
+
* (one per matching registered section) and the leftover driver patch.
|
|
24
|
+
* Generic — consults {@link DEVICE_STORE_SECTIONS}, no hardcoded key list.
|
|
25
|
+
*/
|
|
26
|
+
export declare function splitDeviceStoreKeys(patch: Record<string, unknown>): DeviceStoreSplit;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure per-camera settings parsing/normalization for the virtual-doorbell
|
|
3
|
+
* builtin. Kept separate from the addon class (and the runtime trigger
|
|
4
|
+
* engine) so the persisted-shape handling — including tolerant migration of
|
|
5
|
+
* the legacy single-source blob — is unit-testable in isolation.
|
|
6
|
+
*
|
|
7
|
+
* PERSISTED SHAPE (extensible by design):
|
|
8
|
+
* doorbellSources: Array<{ deviceId: number }>
|
|
9
|
+
*
|
|
10
|
+
* The source is an OBJECT rather than a bare id so future per-source options
|
|
11
|
+
* (a custom trigger override, an event-type match, a per-source debounce)
|
|
12
|
+
* can be added without another migration. Today only `deviceId` is stored;
|
|
13
|
+
* the reaction is DERIVED at runtime from the source device's cap (see
|
|
14
|
+
* `trigger-engine.ts`).
|
|
15
|
+
*/
|
|
16
|
+
/** Device-store key holding the source list. */
|
|
17
|
+
export declare const KEY_SOURCES = "doorbellSources";
|
|
18
|
+
/**
|
|
19
|
+
* Legacy device-store keys from the pre-simplification shape (single source
|
|
20
|
+
* device + explicit trigger + event type). Retained ONLY for tolerant
|
|
21
|
+
* migration on read and cleanup on write — never written afresh.
|
|
22
|
+
*/
|
|
23
|
+
export declare const LEGACY_KEY_SOURCE = "doorbellSourceDeviceId";
|
|
24
|
+
export declare const LEGACY_KEY_TRIGGER = "doorbellTrigger";
|
|
25
|
+
export declare const LEGACY_KEY_EVENT_TYPE = "doorbellEventType";
|
|
26
|
+
/**
|
|
27
|
+
* One source device driving a camera's virtual doorbell. Intentionally an
|
|
28
|
+
* object so it can grow (custom trigger, event type) without a migration.
|
|
29
|
+
*/
|
|
30
|
+
export interface DoorbellSource {
|
|
31
|
+
readonly deviceId: number;
|
|
32
|
+
}
|
|
33
|
+
/** Coerce a persisted / form-supplied id (number or numeric string) to a
|
|
34
|
+
* positive integer device id, or null when it is not a usable id. */
|
|
35
|
+
export declare function coerceDeviceId(value: unknown): number | null;
|
|
36
|
+
/**
|
|
37
|
+
* Parse the persisted doorbell sources for one camera. Tolerant of BOTH:
|
|
38
|
+
* - the new `doorbellSources: [{deviceId}]` shape (ids may arrive as
|
|
39
|
+
* numbers, numeric strings, or `{deviceId}` records); and
|
|
40
|
+
* - the legacy single `doorbellSourceDeviceId` scalar, mapped onto one
|
|
41
|
+
* source (the old trigger/eventType are dropped — the reaction is now
|
|
42
|
+
* derived at runtime).
|
|
43
|
+
*
|
|
44
|
+
* Never throws on malformed data — unknown entries are skipped.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseDoorbellSources(blob: Record<string, unknown>): readonly DoorbellSource[];
|
|
47
|
+
/**
|
|
48
|
+
* Normalize a settings-patch value for the `doorbellSources` multiselect
|
|
49
|
+
* (a `string[]` of device ids from the form, or an already-shaped
|
|
50
|
+
* `{deviceId}[]` / `number[]`) into the persisted extensible shape.
|
|
51
|
+
*/
|
|
52
|
+
export declare function normalizeDoorbellSources(value: unknown): DoorbellSource[];
|
|
53
|
+
/**
|
|
54
|
+
* Produce the next device-store blob after applying a doorbell-sources
|
|
55
|
+
* patch: writes the normalized new shape and strips every legacy key so a
|
|
56
|
+
* migrated camera never carries stale single-source fields.
|
|
57
|
+
*/
|
|
58
|
+
export declare function applyDoorbellSourcesPatch(current: Record<string, unknown>, patchValue: unknown): Record<string, unknown>;
|
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure trigger logic for the virtual-doorbell builtin — edge detection
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Pure trigger logic for the virtual-doorbell builtin — edge detection over
|
|
3
|
+
* runtime-state slices with per-camera debounce. Extracted from the addon
|
|
4
|
+
* class so the behaviour is unit-testable without instantiating the addon or
|
|
5
|
+
* mocking the event bus.
|
|
6
|
+
*
|
|
7
|
+
* REACTION MODEL (simplified — operator picks a source, the system reacts
|
|
8
|
+
* with a sensible default): a source device rings a camera's doorbell when a
|
|
9
|
+
* known binary / switch cap on it RISES to its active state (false→true) — a
|
|
10
|
+
* contact opening, a switch turning on, an alarm sensor firing. There is no
|
|
11
|
+
* per-source trigger configuration today; the shape stays extensible for a
|
|
12
|
+
* future custom-trigger override (see `doorbell-settings.ts`).
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Known binary / switch source caps → the boolean slice field whose
|
|
16
|
+
* false→true rise rings the doorbell. Every entry is "ring on active".
|
|
17
|
+
* Sensors whose "active" reading is not a plain boolean (presence's string
|
|
18
|
+
* state, connectivity's connected flag) are deliberately excluded — a
|
|
19
|
+
* reconnect is not a doorbell press.
|
|
7
20
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare const
|
|
11
|
-
/**
|
|
12
|
-
export declare function
|
|
21
|
+
export declare const SOURCE_CAP_ACTIVE_FIELD: Readonly<Record<string, string>>;
|
|
22
|
+
/** Cap names whose presence in a device's bindings qualify it as a source. */
|
|
23
|
+
export declare const SOURCE_CAPS: readonly string[];
|
|
24
|
+
/** True when a cap is a recognised binary/switch doorbell source. */
|
|
25
|
+
export declare function isSourceCap(capName: string): boolean;
|
|
13
26
|
/**
|
|
14
|
-
* One camera's virtual-doorbell wiring:
|
|
15
|
-
*
|
|
27
|
+
* One camera's virtual-doorbell wiring: a source device that rings it. A
|
|
28
|
+
* camera with several sources produces several assignments (one per source).
|
|
16
29
|
*/
|
|
17
30
|
export interface DoorbellAssignment {
|
|
18
31
|
readonly cameraId: number;
|
|
19
32
|
readonly sourceDeviceId: number;
|
|
20
|
-
readonly trigger: DoorbellTrigger;
|
|
21
|
-
/** Only meaningful when `trigger === 'event'` — matched verbatim
|
|
22
|
-
* against the event-emitter `onEvent` payload `eventType`. */
|
|
23
|
-
readonly eventType: string | null;
|
|
24
33
|
}
|
|
25
34
|
/** Debounce window guarding against sensor chatter (reed-switch bounce,
|
|
26
35
|
* duplicate HA state pushes). One ring per camera per window. */
|
|
@@ -29,10 +38,10 @@ export declare const DEFAULT_DEBOUNCE_MS = 2000;
|
|
|
29
38
|
* Stateful (but side-effect-free towards the outside) trigger engine.
|
|
30
39
|
*
|
|
31
40
|
* Tracks the last observed binary value per (sourceDevice, cap) so a
|
|
32
|
-
* `DeviceStateChanged` re-emission (the slice's `lastChangedAt` moves
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
41
|
+
* `DeviceStateChanged` re-emission (the slice's `lastChangedAt` moves while
|
|
42
|
+
* the binary value does not) never double-fires, and the FIRST observation
|
|
43
|
+
* after boot only seeds the baseline — a state hydration at startup must not
|
|
44
|
+
* ring anybody's doorbell.
|
|
36
45
|
*/
|
|
37
46
|
export declare class DoorbellTriggerEngine {
|
|
38
47
|
private assignments;
|
|
@@ -53,17 +62,10 @@ export declare class DoorbellTriggerEngine {
|
|
|
53
62
|
hasSource(deviceId: number): boolean;
|
|
54
63
|
/**
|
|
55
64
|
* Feed one `DeviceStateChanged` slice. Returns the camera ids whose
|
|
56
|
-
* doorbell should ring (already debounced).
|
|
57
|
-
*
|
|
65
|
+
* doorbell should ring (already debounced). Only a false→true rise on a
|
|
66
|
+
* recognised source cap fires; baseline-only sightings, re-emissions, and
|
|
67
|
+
* the falling edge return an empty array.
|
|
58
68
|
*/
|
|
59
69
|
onStateSlice(deviceId: number, capName: string, slice: Record<string, unknown>): number[];
|
|
60
|
-
/**
|
|
61
|
-
* Feed one event-emitter `onEvent` firing. Returns the camera ids to
|
|
62
|
-
* ring — assignments with `trigger: 'event'`, a matching source and a
|
|
63
|
-
* verbatim `eventType` match. An assignment with no configured
|
|
64
|
-
* eventType never matches (misconfiguration must not ring on every
|
|
65
|
-
* event the device emits).
|
|
66
|
-
*/
|
|
67
|
-
onEmitterEvent(deviceId: number, eventType: string): number[];
|
|
68
70
|
private matchAndDebounce;
|
|
69
71
|
}
|