@blueharford/scrypted-spatial-awareness 0.6.12 → 0.6.13
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/main.nodejs.js +1 -1
- package/dist/main.nodejs.js.map +1 -1
- package/dist/plugin.zip +0 -0
- package/out/main.nodejs.js +48 -32
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/core/tracking-engine.ts +54 -30
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/out/main.nodejs.js
CHANGED
|
@@ -36943,6 +36943,9 @@ class TrackingEngine {
|
|
|
36943
36943
|
trainingConfig = training_1.DEFAULT_TRAINING_CONFIG;
|
|
36944
36944
|
/** Callback for training status updates */
|
|
36945
36945
|
onTrainingStatusUpdate;
|
|
36946
|
+
// ==================== Snapshot Cache ====================
|
|
36947
|
+
/** Cached snapshots for tracked objects (for faster notifications) */
|
|
36948
|
+
snapshotCache = new Map();
|
|
36946
36949
|
constructor(topology, state, alertManager, config, console) {
|
|
36947
36950
|
this.topology = topology;
|
|
36948
36951
|
this.state = state;
|
|
@@ -37166,6 +37169,12 @@ class TrackingEngine {
|
|
|
37166
37169
|
const lastSighting = (0, tracked_object_1.getLastSighting)(tracked);
|
|
37167
37170
|
if (lastSighting && lastSighting.cameraId !== sighting.cameraId) {
|
|
37168
37171
|
const transitDuration = sighting.timestamp - lastSighting.timestamp;
|
|
37172
|
+
// Update cached snapshot from new camera (object is now visible here)
|
|
37173
|
+
if (this.config.useLlmDescriptions) {
|
|
37174
|
+
this.captureAndCacheSnapshot(tracked.globalId, sighting.cameraId).catch(e => {
|
|
37175
|
+
this.console.warn(`[Transition Snapshot] Failed to update snapshot: ${e}`);
|
|
37176
|
+
});
|
|
37177
|
+
}
|
|
37169
37178
|
// Add journey segment
|
|
37170
37179
|
this.state.addJourney(tracked.globalId, {
|
|
37171
37180
|
fromCameraId: lastSighting.cameraId,
|
|
@@ -37232,6 +37241,13 @@ class TrackingEngine {
|
|
|
37232
37241
|
}
|
|
37233
37242
|
/** Schedule an alert after loitering threshold passes */
|
|
37234
37243
|
scheduleLoiteringAlert(globalId, sighting, isEntryPoint) {
|
|
37244
|
+
// Capture snapshot IMMEDIATELY when object is first detected (don't wait for loitering threshold)
|
|
37245
|
+
// This ensures we have a good image while the person/object is still in frame
|
|
37246
|
+
if (this.config.useLlmDescriptions) {
|
|
37247
|
+
this.captureAndCacheSnapshot(globalId, sighting.cameraId).catch(e => {
|
|
37248
|
+
this.console.warn(`[Snapshot] Failed to cache initial snapshot: ${e}`);
|
|
37249
|
+
});
|
|
37250
|
+
}
|
|
37235
37251
|
// Check after loitering threshold if object is still being tracked
|
|
37236
37252
|
setTimeout(async () => {
|
|
37237
37253
|
const tracked = this.state.getObject(globalId);
|
|
@@ -37240,22 +37256,9 @@ class TrackingEngine {
|
|
|
37240
37256
|
// Check if we've already alerted for this object
|
|
37241
37257
|
if (this.isInAlertCooldown(globalId))
|
|
37242
37258
|
return;
|
|
37243
|
-
//
|
|
37244
|
-
let mediaObject;
|
|
37245
|
-
this.console.log(`[Entry Alert]
|
|
37246
|
-
if (this.config.useLlmDescriptions) {
|
|
37247
|
-
try {
|
|
37248
|
-
const camera = systemManager.getDeviceById(sighting.cameraId);
|
|
37249
|
-
this.console.log(`[Entry Alert] Camera ${sighting.cameraId} has Camera interface: ${camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)}`);
|
|
37250
|
-
if (camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)) {
|
|
37251
|
-
mediaObject = await camera.takePicture();
|
|
37252
|
-
this.console.log(`[Entry Alert] Got snapshot: ${!!mediaObject}`);
|
|
37253
|
-
}
|
|
37254
|
-
}
|
|
37255
|
-
catch (e) {
|
|
37256
|
-
this.console.warn('[Entry Alert] Failed to get snapshot:', e);
|
|
37257
|
-
}
|
|
37258
|
-
}
|
|
37259
|
+
// Use cached snapshot (captured when object was first detected)
|
|
37260
|
+
let mediaObject = this.snapshotCache.get(globalId);
|
|
37261
|
+
this.console.log(`[Entry Alert] Using cached snapshot: ${!!mediaObject}`);
|
|
37259
37262
|
// Generate spatial description (now async with LLM support)
|
|
37260
37263
|
this.console.log(`[Entry Alert] Calling generateEntryDescription with mediaObject=${!!mediaObject}`);
|
|
37261
37264
|
const spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId, mediaObject);
|
|
@@ -37290,6 +37293,22 @@ class TrackingEngine {
|
|
|
37290
37293
|
this.recordAlertTime(globalId);
|
|
37291
37294
|
}, this.config.loiteringThreshold);
|
|
37292
37295
|
}
|
|
37296
|
+
/** Capture and cache a snapshot for a tracked object */
|
|
37297
|
+
async captureAndCacheSnapshot(globalId, cameraId) {
|
|
37298
|
+
try {
|
|
37299
|
+
const camera = systemManager.getDeviceById(cameraId);
|
|
37300
|
+
if (camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)) {
|
|
37301
|
+
const mediaObject = await camera.takePicture();
|
|
37302
|
+
if (mediaObject) {
|
|
37303
|
+
this.snapshotCache.set(globalId, mediaObject);
|
|
37304
|
+
this.console.log(`[Snapshot] Cached snapshot for ${globalId.slice(0, 8)} from ${cameraId}`);
|
|
37305
|
+
}
|
|
37306
|
+
}
|
|
37307
|
+
}
|
|
37308
|
+
catch (e) {
|
|
37309
|
+
this.console.warn(`[Snapshot] Failed to capture snapshot: ${e}`);
|
|
37310
|
+
}
|
|
37311
|
+
}
|
|
37293
37312
|
/** Attempt to correlate a sighting with existing tracked objects */
|
|
37294
37313
|
async correlateDetection(sighting) {
|
|
37295
37314
|
const activeObjects = this.state.getActiveObjects();
|
|
@@ -37329,27 +37348,20 @@ class TrackingEngine {
|
|
|
37329
37348
|
handlePotentialExit(tracked, sighting) {
|
|
37330
37349
|
// Mark as pending and set timer
|
|
37331
37350
|
this.state.markPending(tracked.globalId);
|
|
37351
|
+
// Capture a fresh snapshot now while object is still visible (before they leave)
|
|
37352
|
+
if (this.config.useLlmDescriptions) {
|
|
37353
|
+
this.captureAndCacheSnapshot(tracked.globalId, sighting.cameraId).catch(e => {
|
|
37354
|
+
this.console.warn(`[Exit Snapshot] Failed to update snapshot: ${e}`);
|
|
37355
|
+
});
|
|
37356
|
+
}
|
|
37332
37357
|
// Wait for correlation window before marking as exited
|
|
37333
37358
|
const timer = setTimeout(async () => {
|
|
37334
37359
|
const current = this.state.getObject(tracked.globalId);
|
|
37335
37360
|
if (current && current.state === 'pending') {
|
|
37336
37361
|
this.state.markExited(tracked.globalId, sighting.cameraId, sighting.cameraName);
|
|
37337
|
-
//
|
|
37338
|
-
let mediaObject;
|
|
37339
|
-
this.console.log(`[Exit Alert]
|
|
37340
|
-
if (this.config.useLlmDescriptions) {
|
|
37341
|
-
try {
|
|
37342
|
-
const camera = systemManager.getDeviceById(sighting.cameraId);
|
|
37343
|
-
this.console.log(`[Exit Alert] Camera ${sighting.cameraId} has Camera interface: ${camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)}`);
|
|
37344
|
-
if (camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)) {
|
|
37345
|
-
mediaObject = await camera.takePicture();
|
|
37346
|
-
this.console.log(`[Exit Alert] Got snapshot: ${!!mediaObject}`);
|
|
37347
|
-
}
|
|
37348
|
-
}
|
|
37349
|
-
catch (e) {
|
|
37350
|
-
this.console.warn('[Exit Alert] Failed to get snapshot:', e);
|
|
37351
|
-
}
|
|
37352
|
-
}
|
|
37362
|
+
// Use cached snapshot (captured when exit was first detected, while object was still visible)
|
|
37363
|
+
let mediaObject = this.snapshotCache.get(tracked.globalId);
|
|
37364
|
+
this.console.log(`[Exit Alert] Using cached snapshot: ${!!mediaObject}`);
|
|
37353
37365
|
// Generate rich exit description using topology context (now async with LLM support)
|
|
37354
37366
|
this.console.log(`[Exit Alert] Calling generateExitDescription with mediaObject=${!!mediaObject}`);
|
|
37355
37367
|
const spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId, mediaObject);
|
|
@@ -37362,6 +37374,8 @@ class TrackingEngine {
|
|
|
37362
37374
|
involvedLandmarks: spatialResult.involvedLandmarks?.map(l => l.name),
|
|
37363
37375
|
usedLlm: spatialResult.usedLlm,
|
|
37364
37376
|
});
|
|
37377
|
+
// Clean up cached snapshot after exit alert
|
|
37378
|
+
this.snapshotCache.delete(tracked.globalId);
|
|
37365
37379
|
}
|
|
37366
37380
|
this.pendingTimers.delete(tracked.globalId);
|
|
37367
37381
|
}, this.config.correlationWindow);
|
|
@@ -37377,6 +37391,8 @@ class TrackingEngine {
|
|
|
37377
37391
|
this.state.markLost(tracked.globalId);
|
|
37378
37392
|
this.console.log(`Object ${tracked.globalId.slice(0, 8)} marked as lost ` +
|
|
37379
37393
|
`(not seen for ${Math.round(timeSinceSeen / 1000)}s)`);
|
|
37394
|
+
// Clean up cached snapshot
|
|
37395
|
+
this.snapshotCache.delete(tracked.globalId);
|
|
37380
37396
|
this.alertManager.checkAndAlert('lost_tracking', tracked, {
|
|
37381
37397
|
objectClass: tracked.className,
|
|
37382
37398
|
objectLabel: tracked.label,
|