@camstack/addon-motion-wasm 0.1.1
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/chunk-FXGWBT3O.mjs +34 -0
- package/dist/chunk-FXGWBT3O.mjs.map +1 -0
- package/dist/index.d.mts +129 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +7138 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +472 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib-OC5G62TM.mjs +6628 -0
- package/dist/lib-OC5G62TM.mjs.map +1 -0
- package/package.json +80 -0
- package/wasm/motion.wasm +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
12
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// ../../node_modules/tsup/assets/esm_shims.js
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
var getFilename, getDirname, __dirname;
|
|
19
|
+
var init_esm_shims = __esm({
|
|
20
|
+
"../../node_modules/tsup/assets/esm_shims.js"() {
|
|
21
|
+
"use strict";
|
|
22
|
+
getFilename = () => fileURLToPath(import.meta.url);
|
|
23
|
+
getDirname = () => path.dirname(getFilename());
|
|
24
|
+
__dirname = /* @__PURE__ */ getDirname();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
__require,
|
|
30
|
+
__commonJS,
|
|
31
|
+
__dirname,
|
|
32
|
+
init_esm_shims
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=chunk-FXGWBT3O.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/tsup/assets/esm_shims.js"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n"],"mappings":";;;;;;;;;;;;;;;AACA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAF9B,IAIM,aACA,YAEO;AAPb;AAAA;AAAA;AAIA,IAAM,cAAc,MAAM,cAAc,YAAY,GAAG;AACvD,IAAM,aAAa,MAAM,KAAK,QAAQ,YAAY,CAAC;AAE5C,IAAM,YAA4B,2BAAW;AAAA;AAAA;","names":[]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { BaseAddon, IMotionDetectionProvider, ProviderRegistration, FrameInput, DetectorOutput, SpatialDetection, MotionAnalysisResult, ConfigUISchema, ConfigUISchemaWithValues } from '@camstack/types';
|
|
2
|
+
|
|
3
|
+
declare class MotionWasmAddon extends BaseAddon implements IMotionDetectionProvider {
|
|
4
|
+
private detector;
|
|
5
|
+
private readonly cameras;
|
|
6
|
+
private readonly deviceConfigCache;
|
|
7
|
+
private static readonly DEVICE_CONFIG_TTL_MS;
|
|
8
|
+
/**
|
|
9
|
+
* Per-device {@link DeviceProxy} used for zone gating. Built lazily
|
|
10
|
+
* on first analyze() for a device; the proxy's reactive state
|
|
11
|
+
* handles (`state.zones`, `state.zoneRules`) keep their `.value`
|
|
12
|
+
* fresh via the kernel's runtime-state mirror, so the gate reads
|
|
13
|
+
* are sync and free of bus plumbing inside this addon.
|
|
14
|
+
*/
|
|
15
|
+
private readonly proxies;
|
|
16
|
+
/** Unsubscribe pins kept so the slice handles stay subscribed for
|
|
17
|
+
* the camera's lifetime — `.value` only flows through the cache
|
|
18
|
+
* when at least one watcher is active. */
|
|
19
|
+
private readonly proxyUnsubs;
|
|
20
|
+
constructor();
|
|
21
|
+
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
22
|
+
private static readonly PIPELINE_STEP_KEY;
|
|
23
|
+
/**
|
|
24
|
+
* Pipeline step interface — called by PipelineRunner.
|
|
25
|
+
* Uses single-camera state (one instance per camera via AddonEngineManager).
|
|
26
|
+
*/
|
|
27
|
+
process(input: FrameInput): Promise<DetectorOutput & {
|
|
28
|
+
rawDetections: ReadonlyArray<SpatialDetection>;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* motion-detection cap: analyze a frame, return regions + stats.
|
|
32
|
+
* Per-device state keyed by the numeric deviceId (stringified internally
|
|
33
|
+
* so the pipeline-step sentinel shares the same Map).
|
|
34
|
+
*/
|
|
35
|
+
analyze({ deviceId, frame }: {
|
|
36
|
+
deviceId: number;
|
|
37
|
+
frame: FrameInput;
|
|
38
|
+
}): Promise<MotionAnalysisResult>;
|
|
39
|
+
private analyzeInternal;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve (and cache) a {@link DeviceProxy} for the given device.
|
|
42
|
+
* Pins the `state.zones` + `state.zoneRules` slice handles so the
|
|
43
|
+
* kernel runtime-state mirror keeps `.value` warm — subsequent
|
|
44
|
+
* analyze() calls read both slices synchronously without any bus
|
|
45
|
+
* plumbing or per-frame cap query. Returns null on first-call
|
|
46
|
+
* failure (logged) so motion analysis never blocks on zone
|
|
47
|
+
* resolution.
|
|
48
|
+
*/
|
|
49
|
+
private ensureProxy;
|
|
50
|
+
/** Drop the proxy + slice subscriptions for a device. */
|
|
51
|
+
private releaseProxy;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the effective per-device config for a camera. Reads the raw
|
|
54
|
+
* device store and overlays it on top of the schema defaults via
|
|
55
|
+
* `hydrateSchema()`, then narrows to the typed `WasmMotionConfig` shape.
|
|
56
|
+
* Cached with a TTL to avoid hammering the settings store on every frame.
|
|
57
|
+
*/
|
|
58
|
+
private getDeviceConfig;
|
|
59
|
+
removeCamera({ deviceId }: {
|
|
60
|
+
deviceId: number;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
reset(): Promise<void>;
|
|
63
|
+
protected onShutdown(): Promise<void>;
|
|
64
|
+
protected deviceSettingsSchema(): ConfigUISchema;
|
|
65
|
+
getDeviceSettings(deviceId: number): Promise<ConfigUISchemaWithValues>;
|
|
66
|
+
updateDeviceSettings(deviceId: number, patch: Record<string, unknown>): Promise<void>;
|
|
67
|
+
getDeviceSettingsContribution(input: {
|
|
68
|
+
deviceId: number;
|
|
69
|
+
}): Promise<ConfigUISchemaWithValues | null>;
|
|
70
|
+
getDeviceLiveContribution(_input: {
|
|
71
|
+
deviceId: number;
|
|
72
|
+
}): Promise<ConfigUISchemaWithValues | null>;
|
|
73
|
+
applyDeviceSettingsPatch(input: {
|
|
74
|
+
deviceId: number;
|
|
75
|
+
patch: Record<string, unknown>;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
success: true;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Best-effort camera-type check. Used by the settings/live contribution
|
|
81
|
+
* methods to short-circuit on non-camera devices (Lights, Switches,
|
|
82
|
+
* Sensors, Buttons). Returns `true` on lookup failure so a transient
|
|
83
|
+
* device-manager hiccup never silently hides legitimate camera
|
|
84
|
+
* sections.
|
|
85
|
+
*/
|
|
86
|
+
private isCameraDevice;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface WasmMotionConfig {
|
|
90
|
+
readonly threshold: number;
|
|
91
|
+
readonly blurRadius: number;
|
|
92
|
+
readonly dilateRadius: number;
|
|
93
|
+
readonly minArea: number;
|
|
94
|
+
}
|
|
95
|
+
interface WasmMotionRegion {
|
|
96
|
+
readonly x: number;
|
|
97
|
+
readonly y: number;
|
|
98
|
+
readonly w: number;
|
|
99
|
+
readonly h: number;
|
|
100
|
+
readonly pixels: number;
|
|
101
|
+
}
|
|
102
|
+
/** Result containing both all regions and those passing minArea filter. */
|
|
103
|
+
interface WasmMotionResult {
|
|
104
|
+
/** All regions from CCL (unfiltered). */
|
|
105
|
+
readonly raw: ReadonlyArray<WasmMotionRegion>;
|
|
106
|
+
/** Regions passing the minArea threshold. */
|
|
107
|
+
readonly filtered: ReadonlyArray<WasmMotionRegion>;
|
|
108
|
+
}
|
|
109
|
+
declare class WasmMotionDetector {
|
|
110
|
+
private wasm;
|
|
111
|
+
private prevOffset;
|
|
112
|
+
private currOffset;
|
|
113
|
+
private regionOffset;
|
|
114
|
+
/** Load the WASM module. Call once before detect(). */
|
|
115
|
+
load(): Promise<void>;
|
|
116
|
+
/** Initialize for given frame dimensions. Call when resolution changes. */
|
|
117
|
+
init(w: number, h: number): void;
|
|
118
|
+
/**
|
|
119
|
+
* Detect motion between previous and current grayscale frames.
|
|
120
|
+
*
|
|
121
|
+
* @param prevGray - previous frame (Uint8Array, width×height)
|
|
122
|
+
* @param currGray - current frame (Uint8Array, width×height)
|
|
123
|
+
* @param config - detection parameters (optional, uses defaults)
|
|
124
|
+
* @returns raw (all CCL regions) + filtered (passing minArea) regions
|
|
125
|
+
*/
|
|
126
|
+
detect(prevGray: Uint8Array | Buffer, currGray: Uint8Array | Buffer, config?: Partial<WasmMotionConfig>): WasmMotionResult;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { MotionWasmAddon, type WasmMotionConfig, WasmMotionDetector, type WasmMotionRegion };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { BaseAddon, IMotionDetectionProvider, ProviderRegistration, FrameInput, DetectorOutput, SpatialDetection, MotionAnalysisResult, ConfigUISchema, ConfigUISchemaWithValues } from '@camstack/types';
|
|
2
|
+
|
|
3
|
+
declare class MotionWasmAddon extends BaseAddon implements IMotionDetectionProvider {
|
|
4
|
+
private detector;
|
|
5
|
+
private readonly cameras;
|
|
6
|
+
private readonly deviceConfigCache;
|
|
7
|
+
private static readonly DEVICE_CONFIG_TTL_MS;
|
|
8
|
+
/**
|
|
9
|
+
* Per-device {@link DeviceProxy} used for zone gating. Built lazily
|
|
10
|
+
* on first analyze() for a device; the proxy's reactive state
|
|
11
|
+
* handles (`state.zones`, `state.zoneRules`) keep their `.value`
|
|
12
|
+
* fresh via the kernel's runtime-state mirror, so the gate reads
|
|
13
|
+
* are sync and free of bus plumbing inside this addon.
|
|
14
|
+
*/
|
|
15
|
+
private readonly proxies;
|
|
16
|
+
/** Unsubscribe pins kept so the slice handles stay subscribed for
|
|
17
|
+
* the camera's lifetime — `.value` only flows through the cache
|
|
18
|
+
* when at least one watcher is active. */
|
|
19
|
+
private readonly proxyUnsubs;
|
|
20
|
+
constructor();
|
|
21
|
+
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
22
|
+
private static readonly PIPELINE_STEP_KEY;
|
|
23
|
+
/**
|
|
24
|
+
* Pipeline step interface — called by PipelineRunner.
|
|
25
|
+
* Uses single-camera state (one instance per camera via AddonEngineManager).
|
|
26
|
+
*/
|
|
27
|
+
process(input: FrameInput): Promise<DetectorOutput & {
|
|
28
|
+
rawDetections: ReadonlyArray<SpatialDetection>;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* motion-detection cap: analyze a frame, return regions + stats.
|
|
32
|
+
* Per-device state keyed by the numeric deviceId (stringified internally
|
|
33
|
+
* so the pipeline-step sentinel shares the same Map).
|
|
34
|
+
*/
|
|
35
|
+
analyze({ deviceId, frame }: {
|
|
36
|
+
deviceId: number;
|
|
37
|
+
frame: FrameInput;
|
|
38
|
+
}): Promise<MotionAnalysisResult>;
|
|
39
|
+
private analyzeInternal;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve (and cache) a {@link DeviceProxy} for the given device.
|
|
42
|
+
* Pins the `state.zones` + `state.zoneRules` slice handles so the
|
|
43
|
+
* kernel runtime-state mirror keeps `.value` warm — subsequent
|
|
44
|
+
* analyze() calls read both slices synchronously without any bus
|
|
45
|
+
* plumbing or per-frame cap query. Returns null on first-call
|
|
46
|
+
* failure (logged) so motion analysis never blocks on zone
|
|
47
|
+
* resolution.
|
|
48
|
+
*/
|
|
49
|
+
private ensureProxy;
|
|
50
|
+
/** Drop the proxy + slice subscriptions for a device. */
|
|
51
|
+
private releaseProxy;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the effective per-device config for a camera. Reads the raw
|
|
54
|
+
* device store and overlays it on top of the schema defaults via
|
|
55
|
+
* `hydrateSchema()`, then narrows to the typed `WasmMotionConfig` shape.
|
|
56
|
+
* Cached with a TTL to avoid hammering the settings store on every frame.
|
|
57
|
+
*/
|
|
58
|
+
private getDeviceConfig;
|
|
59
|
+
removeCamera({ deviceId }: {
|
|
60
|
+
deviceId: number;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
reset(): Promise<void>;
|
|
63
|
+
protected onShutdown(): Promise<void>;
|
|
64
|
+
protected deviceSettingsSchema(): ConfigUISchema;
|
|
65
|
+
getDeviceSettings(deviceId: number): Promise<ConfigUISchemaWithValues>;
|
|
66
|
+
updateDeviceSettings(deviceId: number, patch: Record<string, unknown>): Promise<void>;
|
|
67
|
+
getDeviceSettingsContribution(input: {
|
|
68
|
+
deviceId: number;
|
|
69
|
+
}): Promise<ConfigUISchemaWithValues | null>;
|
|
70
|
+
getDeviceLiveContribution(_input: {
|
|
71
|
+
deviceId: number;
|
|
72
|
+
}): Promise<ConfigUISchemaWithValues | null>;
|
|
73
|
+
applyDeviceSettingsPatch(input: {
|
|
74
|
+
deviceId: number;
|
|
75
|
+
patch: Record<string, unknown>;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
success: true;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Best-effort camera-type check. Used by the settings/live contribution
|
|
81
|
+
* methods to short-circuit on non-camera devices (Lights, Switches,
|
|
82
|
+
* Sensors, Buttons). Returns `true` on lookup failure so a transient
|
|
83
|
+
* device-manager hiccup never silently hides legitimate camera
|
|
84
|
+
* sections.
|
|
85
|
+
*/
|
|
86
|
+
private isCameraDevice;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface WasmMotionConfig {
|
|
90
|
+
readonly threshold: number;
|
|
91
|
+
readonly blurRadius: number;
|
|
92
|
+
readonly dilateRadius: number;
|
|
93
|
+
readonly minArea: number;
|
|
94
|
+
}
|
|
95
|
+
interface WasmMotionRegion {
|
|
96
|
+
readonly x: number;
|
|
97
|
+
readonly y: number;
|
|
98
|
+
readonly w: number;
|
|
99
|
+
readonly h: number;
|
|
100
|
+
readonly pixels: number;
|
|
101
|
+
}
|
|
102
|
+
/** Result containing both all regions and those passing minArea filter. */
|
|
103
|
+
interface WasmMotionResult {
|
|
104
|
+
/** All regions from CCL (unfiltered). */
|
|
105
|
+
readonly raw: ReadonlyArray<WasmMotionRegion>;
|
|
106
|
+
/** Regions passing the minArea threshold. */
|
|
107
|
+
readonly filtered: ReadonlyArray<WasmMotionRegion>;
|
|
108
|
+
}
|
|
109
|
+
declare class WasmMotionDetector {
|
|
110
|
+
private wasm;
|
|
111
|
+
private prevOffset;
|
|
112
|
+
private currOffset;
|
|
113
|
+
private regionOffset;
|
|
114
|
+
/** Load the WASM module. Call once before detect(). */
|
|
115
|
+
load(): Promise<void>;
|
|
116
|
+
/** Initialize for given frame dimensions. Call when resolution changes. */
|
|
117
|
+
init(w: number, h: number): void;
|
|
118
|
+
/**
|
|
119
|
+
* Detect motion between previous and current grayscale frames.
|
|
120
|
+
*
|
|
121
|
+
* @param prevGray - previous frame (Uint8Array, width×height)
|
|
122
|
+
* @param currGray - current frame (Uint8Array, width×height)
|
|
123
|
+
* @param config - detection parameters (optional, uses defaults)
|
|
124
|
+
* @returns raw (all CCL regions) + filtered (passing minArea) regions
|
|
125
|
+
*/
|
|
126
|
+
detect(prevGray: Uint8Array | Buffer, currGray: Uint8Array | Buffer, config?: Partial<WasmMotionConfig>): WasmMotionResult;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { MotionWasmAddon, type WasmMotionConfig, WasmMotionDetector, type WasmMotionRegion };
|