@apocaliss92/camstack-js-sdk 0.1.5
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/README.md +57 -0
- package/dist/index.cjs +1074 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +759 -0
- package/dist/index.d.ts +759 -0
- package/dist/index.js +995 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK client configuration types.
|
|
3
|
+
*
|
|
4
|
+
* NVR types are now in nvr.ts (shared across app and proxy).
|
|
5
|
+
*/
|
|
6
|
+
interface HlsInfo {
|
|
7
|
+
hlsUrl: string;
|
|
8
|
+
}
|
|
9
|
+
/** Public provider info (returned by providers.list). */
|
|
10
|
+
interface ProviderInfo {
|
|
11
|
+
id: string;
|
|
12
|
+
type: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
interface ProxyClientConfig {
|
|
16
|
+
proxyUrl: string;
|
|
17
|
+
/** Optional auth token for admin endpoints. */
|
|
18
|
+
token?: string;
|
|
19
|
+
}
|
|
20
|
+
interface DirectClientConfig {
|
|
21
|
+
directUrl: string;
|
|
22
|
+
type: "frigate";
|
|
23
|
+
username?: string;
|
|
24
|
+
password?: string;
|
|
25
|
+
}
|
|
26
|
+
type CamStackClientConfig = ProxyClientConfig | DirectClientConfig;
|
|
27
|
+
declare function isProxyConfig(config: CamStackClientConfig): config is ProxyClientConfig;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* NVR types — system-agnostic types used by providers and the SDK client.
|
|
31
|
+
*
|
|
32
|
+
* These are the canonical types from the proxy's providers/types.ts,
|
|
33
|
+
* now owned by the SDK so both app and proxy can share them.
|
|
34
|
+
*/
|
|
35
|
+
interface StreamOption {
|
|
36
|
+
/** Unique identifier for this option (e.g. "auto", "cam_main", "mjpeg"). */
|
|
37
|
+
id: string;
|
|
38
|
+
/** Display label (e.g. "Auto", "High (WebRTC)", "MJPEG"). */
|
|
39
|
+
label: string;
|
|
40
|
+
/** Streaming technology. */
|
|
41
|
+
method: "webrtc" | "mjpeg";
|
|
42
|
+
/** go2rtc stream name (required for WebRTC). */
|
|
43
|
+
streamName?: string;
|
|
44
|
+
}
|
|
45
|
+
interface NvrCamera {
|
|
46
|
+
name: string;
|
|
47
|
+
/** Display label (may differ from internal name). */
|
|
48
|
+
label?: string;
|
|
49
|
+
enabled: boolean;
|
|
50
|
+
hasAudio: boolean;
|
|
51
|
+
hasPtz: boolean;
|
|
52
|
+
/** Available stream names for this camera (e.g. ["cam_main", "cam_sub"]). */
|
|
53
|
+
streams: string[];
|
|
54
|
+
/** Available streaming options with method and quality info. Always includes "Auto". */
|
|
55
|
+
streamOptions: StreamOption[];
|
|
56
|
+
}
|
|
57
|
+
interface StreamInfo {
|
|
58
|
+
name: string;
|
|
59
|
+
/** Camera this stream belongs to (if determinable). */
|
|
60
|
+
camera?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Normalized bounding box in 0–1 range (provider-agnostic). */
|
|
63
|
+
interface NormalizedBoundingBox {
|
|
64
|
+
x1: number;
|
|
65
|
+
y1: number;
|
|
66
|
+
x2: number;
|
|
67
|
+
y2: number;
|
|
68
|
+
}
|
|
69
|
+
/** Detection data attached to an event. */
|
|
70
|
+
interface DetectionData {
|
|
71
|
+
label: string;
|
|
72
|
+
subLabel?: string;
|
|
73
|
+
/** Confidence score 0–1. */
|
|
74
|
+
score: number;
|
|
75
|
+
/** Best confidence score observed during the event. */
|
|
76
|
+
topScore?: number;
|
|
77
|
+
/** Bounding box of the detected object, normalized 0–1. */
|
|
78
|
+
boundingBox?: NormalizedBoundingBox;
|
|
79
|
+
/** Detection region, normalized 0–1. */
|
|
80
|
+
region?: NormalizedBoundingBox;
|
|
81
|
+
/** Area as fraction of frame area (0–1). */
|
|
82
|
+
area?: number;
|
|
83
|
+
/** Named zones the object is currently in. */
|
|
84
|
+
currentZones?: string[];
|
|
85
|
+
/** Named zones the object has entered during this event. */
|
|
86
|
+
enteredZones?: string[];
|
|
87
|
+
/** Attributes detected (e.g., { face: 0.64, glasses: 0.82 }). */
|
|
88
|
+
attributes?: Record<string, number>;
|
|
89
|
+
/** Current estimated speed. */
|
|
90
|
+
currentSpeed?: number;
|
|
91
|
+
/** Average estimated speed over the event. */
|
|
92
|
+
averageSpeed?: number;
|
|
93
|
+
/** Velocity angle in degrees. */
|
|
94
|
+
velocityAngle?: number;
|
|
95
|
+
/** License plate recognition result. */
|
|
96
|
+
licensePlate?: {
|
|
97
|
+
text: string;
|
|
98
|
+
score: number;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Snapshot metadata associated with an event. */
|
|
102
|
+
interface EventSnapshotData {
|
|
103
|
+
boundingBox?: NormalizedBoundingBox;
|
|
104
|
+
score?: number;
|
|
105
|
+
/** Area as fraction of frame area (0–1). */
|
|
106
|
+
area?: number;
|
|
107
|
+
attributes?: Record<string, number>;
|
|
108
|
+
}
|
|
109
|
+
interface NvrEvent {
|
|
110
|
+
id: string;
|
|
111
|
+
camera: string;
|
|
112
|
+
label: string;
|
|
113
|
+
startTime: number;
|
|
114
|
+
endTime: number | null;
|
|
115
|
+
severity?: string;
|
|
116
|
+
hasClip: boolean;
|
|
117
|
+
hasSnapshot: boolean;
|
|
118
|
+
thumbnailPath?: string;
|
|
119
|
+
snapshotPath?: string;
|
|
120
|
+
clipPath?: string;
|
|
121
|
+
detection?: DetectionData;
|
|
122
|
+
snapshot?: EventSnapshotData;
|
|
123
|
+
falsePositive?: boolean;
|
|
124
|
+
retainIndefinitely?: boolean;
|
|
125
|
+
raw?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
interface RecordingSegment {
|
|
128
|
+
id: string;
|
|
129
|
+
camera: string;
|
|
130
|
+
startTime: number;
|
|
131
|
+
endTime: number;
|
|
132
|
+
duration: number;
|
|
133
|
+
motion?: number;
|
|
134
|
+
objects?: number;
|
|
135
|
+
dBFS?: number;
|
|
136
|
+
}
|
|
137
|
+
interface MotionBucket {
|
|
138
|
+
camera: string;
|
|
139
|
+
startTime: number;
|
|
140
|
+
motion?: number;
|
|
141
|
+
audio?: number;
|
|
142
|
+
}
|
|
143
|
+
type LiveEventType = "motion" | "detection" | "audio" | "event" | "review";
|
|
144
|
+
interface LiveEvent {
|
|
145
|
+
type: LiveEventType;
|
|
146
|
+
camera: string;
|
|
147
|
+
timestamp: number;
|
|
148
|
+
data: Record<string, unknown>;
|
|
149
|
+
}
|
|
150
|
+
interface VideoClipsQuery {
|
|
151
|
+
camera: string;
|
|
152
|
+
after: number;
|
|
153
|
+
before: number;
|
|
154
|
+
limit?: number;
|
|
155
|
+
offset?: number;
|
|
156
|
+
}
|
|
157
|
+
interface NvrVideoClip {
|
|
158
|
+
id: string;
|
|
159
|
+
camera: string;
|
|
160
|
+
startTime: number;
|
|
161
|
+
endTime: number;
|
|
162
|
+
duration: number;
|
|
163
|
+
label?: string;
|
|
164
|
+
hasClip: boolean;
|
|
165
|
+
hasSnapshot: boolean;
|
|
166
|
+
source: "event" | "recording";
|
|
167
|
+
}
|
|
168
|
+
interface VideoClipsResult {
|
|
169
|
+
clips: NvrVideoClip[];
|
|
170
|
+
total: number;
|
|
171
|
+
}
|
|
172
|
+
interface VideoClipUrlResult {
|
|
173
|
+
url: string;
|
|
174
|
+
mimeType: string;
|
|
175
|
+
}
|
|
176
|
+
interface CameraDayData {
|
|
177
|
+
events: NvrEvent[];
|
|
178
|
+
motion: MotionBucket[];
|
|
179
|
+
recordings: RecordingSegment[];
|
|
180
|
+
}
|
|
181
|
+
interface CameraStatus {
|
|
182
|
+
camera: string;
|
|
183
|
+
online: boolean;
|
|
184
|
+
recording: boolean;
|
|
185
|
+
detecting: boolean;
|
|
186
|
+
motionActive: boolean;
|
|
187
|
+
}
|
|
188
|
+
interface EventsQuery {
|
|
189
|
+
after?: number;
|
|
190
|
+
before?: number;
|
|
191
|
+
cameras?: string[];
|
|
192
|
+
limit?: number;
|
|
193
|
+
labels?: string[];
|
|
194
|
+
}
|
|
195
|
+
interface MotionQuery {
|
|
196
|
+
after: number;
|
|
197
|
+
before: number;
|
|
198
|
+
scale?: number;
|
|
199
|
+
cameras?: string[];
|
|
200
|
+
}
|
|
201
|
+
interface RecordingsQuery {
|
|
202
|
+
camera: string;
|
|
203
|
+
after: number;
|
|
204
|
+
before: number;
|
|
205
|
+
}
|
|
206
|
+
interface NvrConfig {
|
|
207
|
+
type: string;
|
|
208
|
+
version?: string;
|
|
209
|
+
raw?: unknown;
|
|
210
|
+
}
|
|
211
|
+
interface ProviderConfigs {
|
|
212
|
+
cameras: Array<{
|
|
213
|
+
id: string;
|
|
214
|
+
name: string;
|
|
215
|
+
}>;
|
|
216
|
+
enabledDetectionSources?: string[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Timeline & event types — shared between CamStack app and proxy.
|
|
221
|
+
*
|
|
222
|
+
* Names are unified: the proxy's naming convention is canonical.
|
|
223
|
+
* The app re-exports aliases for backward compatibility where needed.
|
|
224
|
+
*/
|
|
225
|
+
/** Frigate severity: alert = notable, detection = regular. */
|
|
226
|
+
type TimelineEventSeverity = "alert" | "detection";
|
|
227
|
+
/** Motion fragment type: start/end from event boundaries, update from motion API. */
|
|
228
|
+
type MotionFragmentType = "start" | "end" | "update";
|
|
229
|
+
/**
|
|
230
|
+
* A detection event for timeline display and grouping.
|
|
231
|
+
* Unified from app's TimelineDetectionEvent and proxy's DetectionEvent.
|
|
232
|
+
*/
|
|
233
|
+
interface DetectionEvent {
|
|
234
|
+
id?: string;
|
|
235
|
+
timestamp: number;
|
|
236
|
+
source?: string;
|
|
237
|
+
classes?: string[];
|
|
238
|
+
/** @deprecated Use `classes` instead. Kept for backward compat with some app code. */
|
|
239
|
+
detectionClasses?: string[];
|
|
240
|
+
label?: string;
|
|
241
|
+
thumbnailUrl?: string;
|
|
242
|
+
imageUrl?: string;
|
|
243
|
+
videoUrl?: string;
|
|
244
|
+
gifUrl?: string;
|
|
245
|
+
severity?: TimelineEventSeverity;
|
|
246
|
+
deviceName?: string;
|
|
247
|
+
deviceId?: string;
|
|
248
|
+
ruleType?: string;
|
|
249
|
+
croppedThumbnailUrl?: string;
|
|
250
|
+
[key: string]: unknown;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Motion on/off item for timeline bar.
|
|
254
|
+
* Unified from app's TimelineMotionItem and proxy's MotionItem.
|
|
255
|
+
*/
|
|
256
|
+
interface MotionItem {
|
|
257
|
+
timestamp: number;
|
|
258
|
+
deviceId?: string;
|
|
259
|
+
motion: "on" | "off";
|
|
260
|
+
/** Fragment type or "motion" | "audio" (AN audio thresholds). */
|
|
261
|
+
type?: MotionFragmentType | "motion" | "audio" | string;
|
|
262
|
+
/** For type "audio": level 1–5 (threshold crossed). */
|
|
263
|
+
level?: number;
|
|
264
|
+
/** For type "audio": dBFS at threshold crossing. */
|
|
265
|
+
dBFS?: number;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Rule artifact for timeline (from Scrypted AN plugin rules).
|
|
269
|
+
* Unified from app's TimelineArtifactItem and proxy's TimelineArtifact.
|
|
270
|
+
*/
|
|
271
|
+
interface TimelineArtifact {
|
|
272
|
+
ruleName: string;
|
|
273
|
+
ruleType?: string;
|
|
274
|
+
timestamp: number;
|
|
275
|
+
imageUrl?: string;
|
|
276
|
+
gifUrl?: string;
|
|
277
|
+
videoUrl?: string;
|
|
278
|
+
}
|
|
279
|
+
/** Audio level segment for timeline bar. */
|
|
280
|
+
interface AudioLevelSegment {
|
|
281
|
+
start: number;
|
|
282
|
+
end: number;
|
|
283
|
+
level: number;
|
|
284
|
+
dBFS?: number;
|
|
285
|
+
}
|
|
286
|
+
/** Recording segment for timeline bar (where recording exists). */
|
|
287
|
+
interface TimelineRecordingSegment {
|
|
288
|
+
start: number;
|
|
289
|
+
end: number;
|
|
290
|
+
}
|
|
291
|
+
/** Day data for timeline (events, motion, artifacts). */
|
|
292
|
+
interface CameraDayDataResponse {
|
|
293
|
+
events: DetectionEvent[];
|
|
294
|
+
motion: MotionItem[];
|
|
295
|
+
artifacts: TimelineArtifact[];
|
|
296
|
+
recordings?: TimelineRecordingSegment[];
|
|
297
|
+
}
|
|
298
|
+
/** Clustered day data response (server-side bucketing). */
|
|
299
|
+
interface ClusteredDayDataResponse {
|
|
300
|
+
clusters: TimelineCluster[];
|
|
301
|
+
motion: MotionItem[];
|
|
302
|
+
artifacts: TimelineArtifact[];
|
|
303
|
+
motionSegments?: TimelineRecordingSegment[];
|
|
304
|
+
audioLevelSegments?: AudioLevelSegment[];
|
|
305
|
+
recordings?: TimelineRecordingSegment[];
|
|
306
|
+
}
|
|
307
|
+
/** A time-bucketed cluster of detection events. */
|
|
308
|
+
interface TimelineCluster {
|
|
309
|
+
events: DetectionEvent[];
|
|
310
|
+
representative: DetectionEvent;
|
|
311
|
+
classes: string[];
|
|
312
|
+
labels: string[];
|
|
313
|
+
startMs: number;
|
|
314
|
+
endMs: number;
|
|
315
|
+
}
|
|
316
|
+
/** A group of related detection events with a representative. */
|
|
317
|
+
interface DetectionGroup {
|
|
318
|
+
events: DetectionEvent[];
|
|
319
|
+
representative: DetectionEvent;
|
|
320
|
+
classes: string[];
|
|
321
|
+
labels: string[];
|
|
322
|
+
}
|
|
323
|
+
/** Query for clustered day data. */
|
|
324
|
+
interface ClusteredDayDataQuery {
|
|
325
|
+
camera: string;
|
|
326
|
+
days: string[];
|
|
327
|
+
bucketMs?: number;
|
|
328
|
+
enabledClasses?: string[];
|
|
329
|
+
classFilter?: string;
|
|
330
|
+
}
|
|
331
|
+
/** Query for grouped events. */
|
|
332
|
+
interface GroupedEventsQuery {
|
|
333
|
+
after?: number;
|
|
334
|
+
before?: number;
|
|
335
|
+
limit?: number;
|
|
336
|
+
offset?: number;
|
|
337
|
+
cameras?: string[];
|
|
338
|
+
detectionClasses?: string[];
|
|
339
|
+
eventSource?: string;
|
|
340
|
+
filter?: string;
|
|
341
|
+
groupingRange?: number;
|
|
342
|
+
}
|
|
343
|
+
/** Result of grouped events query. */
|
|
344
|
+
interface GroupedEventsResult {
|
|
345
|
+
groups: DetectionGroup[];
|
|
346
|
+
total: number;
|
|
347
|
+
ruleArtifacts?: DetectionGroup[];
|
|
348
|
+
}
|
|
349
|
+
/** Query for events within a cluster time range. */
|
|
350
|
+
interface ClusterEventsQuery {
|
|
351
|
+
camera: string;
|
|
352
|
+
startMs: number;
|
|
353
|
+
endMs: number;
|
|
354
|
+
}
|
|
355
|
+
/** Result of cluster events query. */
|
|
356
|
+
interface ClusterEventsResult {
|
|
357
|
+
events: DetectionEvent[];
|
|
358
|
+
}
|
|
359
|
+
/** A reel event (carousel-friendly). */
|
|
360
|
+
interface ReelEvent {
|
|
361
|
+
id: string;
|
|
362
|
+
camera: string;
|
|
363
|
+
cameraName: string;
|
|
364
|
+
label: string;
|
|
365
|
+
startTime: number;
|
|
366
|
+
thumbnail?: string;
|
|
367
|
+
croppedThumbnailUrl?: string;
|
|
368
|
+
hasClip?: boolean;
|
|
369
|
+
gifUrl?: string;
|
|
370
|
+
videoUrl?: string;
|
|
371
|
+
imageUrl?: string;
|
|
372
|
+
classes?: string[];
|
|
373
|
+
}
|
|
374
|
+
/** Query for reel events. */
|
|
375
|
+
interface ReelEventsQuery {
|
|
376
|
+
limit?: number;
|
|
377
|
+
offset?: number;
|
|
378
|
+
cameras?: string[];
|
|
379
|
+
eventSource?: string;
|
|
380
|
+
detectionClasses?: string[];
|
|
381
|
+
}
|
|
382
|
+
/** Result of reel events query. */
|
|
383
|
+
interface ReelEventsResult {
|
|
384
|
+
events: ReelEvent[];
|
|
385
|
+
total: number;
|
|
386
|
+
}
|
|
387
|
+
/** @deprecated Use DetectionEvent instead. */
|
|
388
|
+
type TimelineDetectionEvent = DetectionEvent;
|
|
389
|
+
/** @deprecated Use MotionItem instead. */
|
|
390
|
+
type TimelineMotionItem = MotionItem;
|
|
391
|
+
/** @deprecated Use TimelineArtifact instead. */
|
|
392
|
+
type TimelineArtifactItem = TimelineArtifact;
|
|
393
|
+
/** @deprecated Use TimelineCluster instead. */
|
|
394
|
+
type TimelineClusterFromServer = TimelineCluster;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* CamStackClient — unified API, dual mode (proxy or direct).
|
|
398
|
+
*
|
|
399
|
+
* Usage:
|
|
400
|
+
* const client = createCamStackClient({ proxyUrl: 'http://localhost:4000' });
|
|
401
|
+
* const client = createCamStackClient({ directUrl: 'http://frigate:5000', type: 'frigate' });
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
declare class CamStackClient {
|
|
405
|
+
private proxy;
|
|
406
|
+
private direct;
|
|
407
|
+
readonly isProxy: boolean;
|
|
408
|
+
constructor(config: CamStackClientConfig);
|
|
409
|
+
listProviders(): Promise<ProviderInfo[]>;
|
|
410
|
+
getConfig(providerId?: string): Promise<NvrConfig>;
|
|
411
|
+
getStreams(providerId?: string): Promise<StreamInfo[]>;
|
|
412
|
+
listCameras(providerId?: string): Promise<NvrCamera[]>;
|
|
413
|
+
getSnapshot(camera: string, providerId?: string): Promise<string>;
|
|
414
|
+
getEvents(params: EventsQuery & {
|
|
415
|
+
providerId?: string;
|
|
416
|
+
}): Promise<NvrEvent[]>;
|
|
417
|
+
getRecordings(params: RecordingsQuery & {
|
|
418
|
+
providerId?: string;
|
|
419
|
+
}): Promise<RecordingSegment[]>;
|
|
420
|
+
getMotionActivity(params: MotionQuery & {
|
|
421
|
+
providerId?: string;
|
|
422
|
+
}): Promise<MotionBucket[]>;
|
|
423
|
+
getAudioSegments(camera: string, after: number, before: number, providerId?: string): Promise<AudioLevelSegment[]>;
|
|
424
|
+
getEventThumbnail(eventId: string, providerId?: string): Promise<string>;
|
|
425
|
+
getEventSnapshot(eventId: string, providerId?: string): Promise<string>;
|
|
426
|
+
webrtcOffer(streamName: string, sdpOffer: string, providerId?: string): Promise<string>;
|
|
427
|
+
getRecordingHlsUrl(camera: string, startTime: number, endTime: number, providerId?: string): Promise<string>;
|
|
428
|
+
getEventHlsUrl(eventId: string, providerId?: string): Promise<string>;
|
|
429
|
+
subscribeLiveEvents(callback: (event: LiveEvent) => void, options?: {
|
|
430
|
+
cameras?: string[];
|
|
431
|
+
types?: string[];
|
|
432
|
+
providerId?: string;
|
|
433
|
+
}): {
|
|
434
|
+
unsubscribe: () => void;
|
|
435
|
+
};
|
|
436
|
+
subscribeMotion(camera: string, callback: (event: LiveEvent) => void, providerId?: string): {
|
|
437
|
+
unsubscribe: () => void;
|
|
438
|
+
};
|
|
439
|
+
subscribeDetection(camera: string, callback: (event: LiveEvent) => void, providerId?: string): {
|
|
440
|
+
unsubscribe: () => void;
|
|
441
|
+
};
|
|
442
|
+
subscribeAudioLevel(camera: string, callback: (event: LiveEvent) => void, providerId?: string): {
|
|
443
|
+
unsubscribe: () => void;
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
declare function createCamStackClient(config: CamStackClientConfig): CamStackClient;
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Proxy client — connects to camstack-js-proxy via tRPC.
|
|
450
|
+
* Full features: queries, mutations, subscriptions, WebRTC, HLS.
|
|
451
|
+
*/
|
|
452
|
+
|
|
453
|
+
declare class ProxyClient {
|
|
454
|
+
private trpc;
|
|
455
|
+
private baseUrl;
|
|
456
|
+
constructor(config: ProxyClientConfig);
|
|
457
|
+
listProviders(): Promise<ProviderInfo[]>;
|
|
458
|
+
getConfig(providerId?: string): Promise<NvrConfig>;
|
|
459
|
+
getStreams(providerId?: string): Promise<StreamInfo[]>;
|
|
460
|
+
listCameras(providerId?: string): Promise<NvrCamera[]>;
|
|
461
|
+
getSnapshot(camera: string, providerId?: string): Promise<string>;
|
|
462
|
+
getEvents(params: EventsQuery & {
|
|
463
|
+
providerId?: string;
|
|
464
|
+
}): Promise<NvrEvent[]>;
|
|
465
|
+
getRecordings(params: RecordingsQuery & {
|
|
466
|
+
providerId?: string;
|
|
467
|
+
}): Promise<RecordingSegment[]>;
|
|
468
|
+
getMotionActivity(params: MotionQuery & {
|
|
469
|
+
providerId?: string;
|
|
470
|
+
}): Promise<MotionBucket[]>;
|
|
471
|
+
getAudioSegments(camera: string, after: number, before: number, providerId?: string): Promise<AudioLevelSegment[]>;
|
|
472
|
+
getEventThumbnail(eventId: string, providerId?: string): Promise<string>;
|
|
473
|
+
getEventSnapshot(eventId: string, providerId?: string): Promise<string>;
|
|
474
|
+
webrtcOffer(streamName: string, sdpOffer: string, providerId?: string): Promise<string>;
|
|
475
|
+
getRecordingHls(camera: string, startTime: number, endTime: number, providerId?: string): Promise<string>;
|
|
476
|
+
getEventHls(eventId: string, providerId?: string): Promise<string>;
|
|
477
|
+
subscribeLiveEvents(callback: (event: LiveEvent) => void, options?: {
|
|
478
|
+
cameras?: string[];
|
|
479
|
+
types?: string[];
|
|
480
|
+
providerId?: string;
|
|
481
|
+
onError?: (err: unknown) => void;
|
|
482
|
+
onStarted?: () => void;
|
|
483
|
+
onStopped?: () => void;
|
|
484
|
+
}): {
|
|
485
|
+
unsubscribe: () => void;
|
|
486
|
+
};
|
|
487
|
+
subscribeMotion(camera: string, callback: (event: LiveEvent) => void, providerId?: string): {
|
|
488
|
+
unsubscribe: () => void;
|
|
489
|
+
};
|
|
490
|
+
subscribeDetection(camera: string, callback: (event: LiveEvent) => void, providerId?: string): {
|
|
491
|
+
unsubscribe: () => void;
|
|
492
|
+
};
|
|
493
|
+
subscribeAudioLevel(camera: string, callback: (event: LiveEvent) => void, providerId?: string): {
|
|
494
|
+
unsubscribe: () => void;
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Direct client — connects to NVR HTTP API without a proxy.
|
|
500
|
+
* Limited functionality: queries only, no subscriptions, no audio tracking.
|
|
501
|
+
*/
|
|
502
|
+
|
|
503
|
+
declare class DirectClient {
|
|
504
|
+
private baseUrl;
|
|
505
|
+
private type;
|
|
506
|
+
private username?;
|
|
507
|
+
private password?;
|
|
508
|
+
private token;
|
|
509
|
+
constructor(config: DirectClientConfig);
|
|
510
|
+
private authHeaders;
|
|
511
|
+
private request;
|
|
512
|
+
getConfig(): Promise<NvrConfig>;
|
|
513
|
+
getStreams(): Promise<StreamInfo[]>;
|
|
514
|
+
listCameras(): Promise<NvrCamera[]>;
|
|
515
|
+
getSnapshot(camera: string): Promise<string>;
|
|
516
|
+
getEvents(params: EventsQuery): Promise<NvrEvent[]>;
|
|
517
|
+
getRecordings(params: RecordingsQuery): Promise<RecordingSegment[]>;
|
|
518
|
+
getMotionActivity(params: MotionQuery): Promise<MotionBucket[]>;
|
|
519
|
+
/** Audio segments are not available in direct mode (tracked by proxy only). */
|
|
520
|
+
getAudioSegments(_camera: string, _after: number, _before: number): Promise<AudioLevelSegment[]>;
|
|
521
|
+
getEventThumbnail(eventId: string): Promise<string>;
|
|
522
|
+
getEventSnapshot(eventId: string): Promise<string>;
|
|
523
|
+
webrtcOffer(streamName: string, sdpOffer: string): Promise<string>;
|
|
524
|
+
getRecordingHlsUrl(camera: string, startTime: number, endTime: number): string;
|
|
525
|
+
getEventHlsUrl(eventId: string): string;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Detection classes — shared between CamStack app and proxy.
|
|
530
|
+
* Aligned with scrypted-advanced-notifier/src/detectionClasses.ts.
|
|
531
|
+
*/
|
|
532
|
+
declare enum DetectionClass {
|
|
533
|
+
Motion = "motion",
|
|
534
|
+
Person = "person",
|
|
535
|
+
Vehicle = "vehicle",
|
|
536
|
+
Animal = "animal",
|
|
537
|
+
Audio = "audio",
|
|
538
|
+
Face = "face",
|
|
539
|
+
Plate = "plate",
|
|
540
|
+
Package = "package",
|
|
541
|
+
Doorbell = "doorbell",
|
|
542
|
+
Sensor = "sensor"
|
|
543
|
+
}
|
|
544
|
+
declare const animalClasses: string[];
|
|
545
|
+
declare const personClasses: string[];
|
|
546
|
+
declare const vehicleClasses: string[];
|
|
547
|
+
declare const faceClasses: string[];
|
|
548
|
+
declare const licensePlateClasses: string[];
|
|
549
|
+
declare const motionClasses: string[];
|
|
550
|
+
declare const packageClasses: string[];
|
|
551
|
+
declare const audioClasses: string[];
|
|
552
|
+
/** Common YAMNet audio labels (advanced-notifier). Parent: Audio. */
|
|
553
|
+
declare const audioLabelClasses: string[];
|
|
554
|
+
declare const doorbellClasses: string[];
|
|
555
|
+
/** Sensor types (advanced-notifier SupportedSensorType). Parent: Sensor. */
|
|
556
|
+
declare const sensorLabelClasses: string[];
|
|
557
|
+
declare const detectionClassesDefaultMap: Record<string, DetectionClass>;
|
|
558
|
+
declare const isFaceClassname: (c: string) => boolean;
|
|
559
|
+
declare const isPlateClassname: (c: string) => boolean;
|
|
560
|
+
declare const isAnimalClassname: (c: string) => boolean;
|
|
561
|
+
declare const isPersonClassname: (c: string) => boolean;
|
|
562
|
+
declare const isVehicleClassname: (c: string) => boolean;
|
|
563
|
+
declare const isMotionClassname: (c: string) => boolean;
|
|
564
|
+
declare const isDoorbellClassname: (c: string) => boolean;
|
|
565
|
+
declare const isPackageClassname: (c: string) => boolean;
|
|
566
|
+
declare const isAudioClassname: (c: string) => boolean;
|
|
567
|
+
declare const isSensorLabelClassname: (c: string) => boolean;
|
|
568
|
+
declare const isLabelDetection: (c: string) => boolean;
|
|
569
|
+
declare const getParentClass: (className: string) => DetectionClass | undefined;
|
|
570
|
+
declare const getParentDetectionClass: (det: {
|
|
571
|
+
label?: string;
|
|
572
|
+
className: string;
|
|
573
|
+
}) => DetectionClass | undefined;
|
|
574
|
+
declare const defaultDetectionClasses: DetectionClass[];
|
|
575
|
+
/** Default enabled classes: all except Motion. */
|
|
576
|
+
declare const DEFAULT_ENABLED_CLASSES: (DetectionClass.Person | DetectionClass.Vehicle | DetectionClass.Animal | DetectionClass.Audio | DetectionClass.Face | DetectionClass.Plate | DetectionClass.Package | DetectionClass.Doorbell | DetectionClass.Sensor)[];
|
|
577
|
+
type TimelineEventPreset = "all" | "important" | "critical" | "custom";
|
|
578
|
+
/** Classes for "critical" preset: security-relevant only (person, doorbell, package). */
|
|
579
|
+
declare const TIMELINE_PRESET_CRITICAL: string[];
|
|
580
|
+
/** Classes for "important" preset: critical + vehicle, animal, audio, face, plate. */
|
|
581
|
+
declare const TIMELINE_PRESET_IMPORTANT: string[];
|
|
582
|
+
/** Classes for "all" preset: same as DEFAULT_ENABLED_CLASSES. */
|
|
583
|
+
declare const TIMELINE_PRESET_ALL: string[];
|
|
584
|
+
/** Get enabled classes for a preset. For "custom", pass customClasses. */
|
|
585
|
+
declare function getClassesForTimelinePreset(preset: TimelineEventPreset, customClasses?: string[]): string[];
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Device types, mappings, and filtering constants — shared between CamStack app and proxy.
|
|
589
|
+
*
|
|
590
|
+
* Covers:
|
|
591
|
+
* - Canonical device type normalization (Scrypted PascalCase + HA domains → canonical)
|
|
592
|
+
* - Eligible device type lists for Scrypted and Home Assistant
|
|
593
|
+
* - Unified Device / DeviceCommand / CommandResult interfaces
|
|
594
|
+
* - AssociatedDeviceState shape
|
|
595
|
+
*/
|
|
596
|
+
type CanonicalDeviceType = "light" | "switch" | "cover" | "lock" | "alarm" | "button" | "select" | "siren" | "sensor" | "entry" | "media_player" | "script";
|
|
597
|
+
/**
|
|
598
|
+
* Maps raw device types from Scrypted (PascalCase) and Home Assistant (lowercase domains)
|
|
599
|
+
* to canonical CamStack device types.
|
|
600
|
+
*/
|
|
601
|
+
declare const RAW_TO_CANONICAL: Record<string, CanonicalDeviceType>;
|
|
602
|
+
/**
|
|
603
|
+
* Extended HA domain → type map (includes non-eligible domains for display/categorization).
|
|
604
|
+
* Superset of RAW_TO_CANONICAL for HA-specific domains.
|
|
605
|
+
*/
|
|
606
|
+
declare const HA_DOMAIN_TYPE_MAP: Record<string, string>;
|
|
607
|
+
/**
|
|
608
|
+
* Extended Scrypted type → canonical map (includes non-eligible types for display).
|
|
609
|
+
* Superset of RAW_TO_CANONICAL for Scrypted-specific types.
|
|
610
|
+
*/
|
|
611
|
+
declare const SCRYPTED_TYPE_TO_CANONICAL: Record<string, string>;
|
|
612
|
+
/** Normalize raw type (Scrypted or HA) to canonical key for filtering and display. */
|
|
613
|
+
declare function getCanonicalDeviceType(rawType: string): CanonicalDeviceType | null;
|
|
614
|
+
/** Scrypted device types eligible for device control in CamStack. PascalCase. */
|
|
615
|
+
declare const ELIGIBLE_SCRYPTED_DEVICE_TYPES: readonly ["Entry", "Light", "Switch", "Lock", "SecuritySystem", "Buttons", "WindowCovering", "Siren", "Sensor", "Select", "Program"];
|
|
616
|
+
/** Set version for O(1) lookup. */
|
|
617
|
+
declare const ELIGIBLE_SCRYPTED_DEVICE_TYPES_SET: Set<string>;
|
|
618
|
+
/** Home Assistant domains eligible for device control in CamStack. */
|
|
619
|
+
declare const ELIGIBLE_HA_DOMAINS: readonly ["light", "switch", "input_boolean", "cover", "lock", "alarm_control_panel", "input_button", "button", "input_select", "select", "siren", "media_player", "script"];
|
|
620
|
+
/** Set version for O(1) lookup. */
|
|
621
|
+
declare const ELIGIBLE_HA_DOMAINS_SET: Set<string>;
|
|
622
|
+
/** All device command actions supported by CamStack. */
|
|
623
|
+
type DeviceCommandAction = "turnOn" | "turnOff" | "openEntry" | "closeEntry" | "stopEntry" | "lock" | "unlock" | "disarm" | "arm" | "pressButton" | "selectOption" | "volumeUp" | "volumeDown" | "volumeMute";
|
|
624
|
+
/**
|
|
625
|
+
* State snapshot shape for an associated device.
|
|
626
|
+
* Used by both app (AssociatedDeviceStateSnapshot) and proxy (Device.state).
|
|
627
|
+
*/
|
|
628
|
+
interface AssociatedDeviceState {
|
|
629
|
+
name?: string;
|
|
630
|
+
type?: string;
|
|
631
|
+
interfaces?: string[];
|
|
632
|
+
on?: boolean;
|
|
633
|
+
hasBrightnessControl?: boolean;
|
|
634
|
+
brightness?: number;
|
|
635
|
+
lockState?: string;
|
|
636
|
+
securitySystemState?: {
|
|
637
|
+
mode?: string;
|
|
638
|
+
supportedModes?: string[];
|
|
639
|
+
};
|
|
640
|
+
buttons?: string[];
|
|
641
|
+
entryOpen?: boolean | "jammed";
|
|
642
|
+
value?: string;
|
|
643
|
+
options?: string[];
|
|
644
|
+
volumeLevel?: number;
|
|
645
|
+
isVolumeMuted?: boolean;
|
|
646
|
+
coverSupportsStop?: boolean;
|
|
647
|
+
coverPosition?: number;
|
|
648
|
+
}
|
|
649
|
+
/** A device from an automation system (Scrypted, Home Assistant, etc.). */
|
|
650
|
+
interface Device {
|
|
651
|
+
id: string;
|
|
652
|
+
name: string;
|
|
653
|
+
/**
|
|
654
|
+
* Canonical device type (lowercase): "light", "switch", "cover", "lock",
|
|
655
|
+
* "alarm", "button", "select", "siren", "sensor", "entry", "media_player", "script".
|
|
656
|
+
*/
|
|
657
|
+
type: string;
|
|
658
|
+
/** Original type from the source system (e.g. Scrypted "WindowCovering", HA domain "cover"). */
|
|
659
|
+
rawType?: string;
|
|
660
|
+
model?: string;
|
|
661
|
+
manufacturer?: string;
|
|
662
|
+
online: boolean;
|
|
663
|
+
interfaces: string[];
|
|
664
|
+
/** Current state (key-value pairs, device-type-dependent). See AssociatedDeviceState. */
|
|
665
|
+
state: Record<string, unknown>;
|
|
666
|
+
}
|
|
667
|
+
/** A command to send to a device. */
|
|
668
|
+
interface DeviceCommand {
|
|
669
|
+
deviceId: string;
|
|
670
|
+
command: string;
|
|
671
|
+
params?: Record<string, unknown>;
|
|
672
|
+
}
|
|
673
|
+
/** Result of a command execution. */
|
|
674
|
+
interface CommandResult {
|
|
675
|
+
success: boolean;
|
|
676
|
+
error?: string;
|
|
677
|
+
state?: Record<string, unknown>;
|
|
678
|
+
}
|
|
679
|
+
/** Device source type (integration that provides the device). */
|
|
680
|
+
type AssociatedDeviceSource = "scrypted" | "ha";
|
|
681
|
+
/** Eligible device summary (for device picker UI). */
|
|
682
|
+
interface EligibleDevice {
|
|
683
|
+
id: string;
|
|
684
|
+
name: string;
|
|
685
|
+
type: string;
|
|
686
|
+
source: AssociatedDeviceSource;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Camera & PTZ types — shared between CamStack app and proxy.
|
|
691
|
+
*/
|
|
692
|
+
/** Camera source type discriminator. */
|
|
693
|
+
type CameraSourceType = "scrypted" | "frigate" | "onvif" | "rtsp";
|
|
694
|
+
/** Streaming method. */
|
|
695
|
+
type StreamMethod = "webrtc" | "mjpeg";
|
|
696
|
+
/** Scrypted-compatible PTZ command. */
|
|
697
|
+
interface PanTiltZoomCommand {
|
|
698
|
+
movement?: "Relative" | "Absolute" | "Continuous" | "Home" | "Preset";
|
|
699
|
+
pan?: number;
|
|
700
|
+
tilt?: number;
|
|
701
|
+
zoom?: number;
|
|
702
|
+
preset?: string;
|
|
703
|
+
timeout?: number;
|
|
704
|
+
}
|
|
705
|
+
/** PTZ capabilities reported by the device. */
|
|
706
|
+
interface PanTiltZoomCapabilities {
|
|
707
|
+
pan?: boolean;
|
|
708
|
+
tilt?: boolean;
|
|
709
|
+
zoom?: boolean;
|
|
710
|
+
presets?: Record<string, string>;
|
|
711
|
+
}
|
|
712
|
+
/** Accessory switch kinds exposed by the Advanced Notifier plugin. */
|
|
713
|
+
type CameraAccessorySwitchKind = "siren_on_motion" | "siren" | "light_on_motion" | "light" | "pir" | "autotracking";
|
|
714
|
+
/** Extended camera status with accessory switches (from AN plugin). */
|
|
715
|
+
interface CameraStatusEntry {
|
|
716
|
+
notificationsEnabled: boolean;
|
|
717
|
+
isRecording: boolean;
|
|
718
|
+
isSnapshotsEnabled: boolean;
|
|
719
|
+
isRebroadcastEnabled: boolean;
|
|
720
|
+
accessorySwitchStates: Partial<Record<CameraAccessorySwitchKind, boolean>>;
|
|
721
|
+
}
|
|
722
|
+
/** Batch camera status response. */
|
|
723
|
+
interface CamerasStatusResult {
|
|
724
|
+
cameras: Record<string, CameraStatusEntry>;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Feature capability matrix — shared between CamStack app and proxy.
|
|
729
|
+
*
|
|
730
|
+
* Maps each app feature to:
|
|
731
|
+
* - Which source types (Frigate, Scrypted, RTSP) support it
|
|
732
|
+
* - Which platforms (iOS, Android, Web, Scrypted embed) it works on
|
|
733
|
+
* - Whether the camstack-js-proxy backend is required
|
|
734
|
+
* - The ICameraSource method that enables the feature
|
|
735
|
+
*/
|
|
736
|
+
type FeatureId = "liveStream" | "multiResolution" | "motion" | "objectDetection" | "audioVolume" | "audioVolumes" | "ptz" | "intercom" | "deviceStatus" | "timeline" | "clusteredTimeline" | "detectionClasses" | "videoClips" | "nvrPlayback" | "nvrScrub" | "recordingThumbnail" | "nvrSeekToLive";
|
|
737
|
+
type PlatformId = "ios" | "android" | "web" | "scryptedEmbed";
|
|
738
|
+
type SourceType = "frigate" | "scrypted" | "rtsp";
|
|
739
|
+
interface FeatureEntry {
|
|
740
|
+
id: FeatureId;
|
|
741
|
+
label: string;
|
|
742
|
+
/** Which source types support this feature. */
|
|
743
|
+
sources: Partial<Record<SourceType, boolean>>;
|
|
744
|
+
/** Platform availability overrides. When omitted, all platforms are supported. */
|
|
745
|
+
platforms?: Partial<Record<PlatformId, boolean>>;
|
|
746
|
+
/** True when this feature needs the camstack-js-proxy backend. */
|
|
747
|
+
requiresBackend?: boolean;
|
|
748
|
+
/** The ICameraSource method name that enables this feature at runtime. */
|
|
749
|
+
adapterMethod?: string;
|
|
750
|
+
}
|
|
751
|
+
declare const FEATURE_MATRIX: FeatureEntry[];
|
|
752
|
+
/** Check if a feature is available for a given source type and platform. */
|
|
753
|
+
declare function isFeatureAvailable(featureId: FeatureId, source: SourceType, platform: PlatformId): boolean;
|
|
754
|
+
/** Get all features supported by a source type. */
|
|
755
|
+
declare function getSourceFeatures(source: SourceType): FeatureEntry[];
|
|
756
|
+
/** Get all features that require the backend proxy. */
|
|
757
|
+
declare function getBackendRequiredFeatures(): FeatureEntry[];
|
|
758
|
+
|
|
759
|
+
export { type AssociatedDeviceSource, type AssociatedDeviceState, type AudioLevelSegment, CamStackClient, type CamStackClientConfig, type CameraAccessorySwitchKind, type CameraDayData, type CameraDayDataResponse, type CameraSourceType, type CameraStatus, type CameraStatusEntry, type CamerasStatusResult, type CanonicalDeviceType, type ClusterEventsQuery, type ClusterEventsResult, type ClusteredDayDataQuery, type ClusteredDayDataResponse, type CommandResult, DEFAULT_ENABLED_CLASSES, DetectionClass, type DetectionData, type DetectionEvent, type DetectionGroup, type Device, type DeviceCommand, type DeviceCommandAction, DirectClient, type DirectClientConfig, ELIGIBLE_HA_DOMAINS, ELIGIBLE_HA_DOMAINS_SET, ELIGIBLE_SCRYPTED_DEVICE_TYPES, ELIGIBLE_SCRYPTED_DEVICE_TYPES_SET, type EligibleDevice, type EventSnapshotData, type EventsQuery, FEATURE_MATRIX, type FeatureEntry, type FeatureId, type GroupedEventsQuery, type GroupedEventsResult, HA_DOMAIN_TYPE_MAP, type HlsInfo, type LiveEvent, type LiveEventType, type MotionBucket, type MotionFragmentType, type MotionItem, type MotionQuery, type NormalizedBoundingBox, type NvrCamera, type NvrConfig, type NvrEvent, type NvrVideoClip, type PanTiltZoomCapabilities, type PanTiltZoomCommand, type PlatformId, type ProviderConfigs, type ProviderInfo, ProxyClient, type ProxyClientConfig, RAW_TO_CANONICAL, type RecordingSegment, type RecordingsQuery, type ReelEvent, type ReelEventsQuery, type ReelEventsResult, SCRYPTED_TYPE_TO_CANONICAL, type SourceType, type StreamInfo, type StreamMethod, type StreamOption, TIMELINE_PRESET_ALL, TIMELINE_PRESET_CRITICAL, TIMELINE_PRESET_IMPORTANT, type TimelineArtifact, type TimelineArtifactItem, type TimelineCluster, type TimelineClusterFromServer, type TimelineDetectionEvent, type TimelineEventPreset, type TimelineEventSeverity, type TimelineMotionItem, type TimelineRecordingSegment, type VideoClipUrlResult, type VideoClipsQuery, type VideoClipsResult, animalClasses, audioClasses, audioLabelClasses, createCamStackClient, defaultDetectionClasses, detectionClassesDefaultMap, doorbellClasses, faceClasses, getBackendRequiredFeatures, getCanonicalDeviceType, getClassesForTimelinePreset, getParentClass, getParentDetectionClass, getSourceFeatures, isAnimalClassname, isAudioClassname, isDoorbellClassname, isFaceClassname, isFeatureAvailable, isLabelDetection, isMotionClassname, isPackageClassname, isPersonClassname, isPlateClassname, isProxyConfig, isSensorLabelClassname, isVehicleClassname, licensePlateClasses, motionClasses, packageClasses, personClasses, sensorLabelClasses, vehicleClasses };
|