@blueharford/scrypted-spatial-awareness 0.6.30 → 0.6.32
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 +37 -16
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/session-manager-plugin.pkg +0 -0
- package/src/alerts/alert-manager.ts +18 -2
- package/src/core/spatial-reasoning.ts +2 -2
- package/src/core/tracking-engine.ts +21 -10
- package/src/main.ts +1 -1
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/out/main.nodejs.js
CHANGED
|
@@ -34457,6 +34457,12 @@ class AlertManager {
|
|
|
34457
34457
|
const notifierIds = rule.notifiers.length > 0
|
|
34458
34458
|
? rule.notifiers
|
|
34459
34459
|
: this.getDefaultNotifiers();
|
|
34460
|
+
// Debug: log which notifiers we're using
|
|
34461
|
+
this.console.log(`[Notification] Rule ${rule.id} has ${rule.notifiers.length} notifiers, using ${notifierIds.length} notifier(s): ${notifierIds.join(', ') || 'NONE'}`);
|
|
34462
|
+
if (notifierIds.length === 0) {
|
|
34463
|
+
this.console.warn(`[Notification] No notifiers configured! Configure a notifier in plugin settings.`);
|
|
34464
|
+
return;
|
|
34465
|
+
}
|
|
34460
34466
|
// Try to get a thumbnail from the camera
|
|
34461
34467
|
let mediaObject;
|
|
34462
34468
|
const cameraId = alert.details.toCameraId || alert.details.cameraId;
|
|
@@ -34551,27 +34557,34 @@ class AlertManager {
|
|
|
34551
34557
|
try {
|
|
34552
34558
|
// Try new multiple notifiers setting first
|
|
34553
34559
|
const notifiers = this.storage.getItem('defaultNotifiers');
|
|
34560
|
+
this.console.log(`[Notifiers] Raw storage value: ${notifiers}`);
|
|
34554
34561
|
if (notifiers) {
|
|
34555
34562
|
// Could be JSON array or comma-separated string
|
|
34556
34563
|
try {
|
|
34557
34564
|
const parsed = JSON.parse(notifiers);
|
|
34558
34565
|
if (Array.isArray(parsed)) {
|
|
34566
|
+
this.console.log(`[Notifiers] Parsed JSON array: ${parsed.join(', ')}`);
|
|
34559
34567
|
return parsed;
|
|
34560
34568
|
}
|
|
34561
34569
|
}
|
|
34562
34570
|
catch {
|
|
34563
34571
|
// Not JSON, might be comma-separated or single value
|
|
34564
34572
|
if (notifiers.includes(',')) {
|
|
34565
|
-
|
|
34573
|
+
const result = notifiers.split(',').map(s => s.trim()).filter(Boolean);
|
|
34574
|
+
this.console.log(`[Notifiers] Parsed comma-separated: ${result.join(', ')}`);
|
|
34575
|
+
return result;
|
|
34566
34576
|
}
|
|
34577
|
+
this.console.log(`[Notifiers] Single value: ${notifiers}`);
|
|
34567
34578
|
return [notifiers];
|
|
34568
34579
|
}
|
|
34569
34580
|
}
|
|
34570
34581
|
// Fallback to old single notifier setting
|
|
34571
34582
|
const defaultNotifier = this.storage.getItem('defaultNotifier');
|
|
34583
|
+
this.console.log(`[Notifiers] Fallback single notifier: ${defaultNotifier || 'NONE'}`);
|
|
34572
34584
|
return defaultNotifier ? [defaultNotifier] : [];
|
|
34573
34585
|
}
|
|
34574
|
-
catch {
|
|
34586
|
+
catch (e) {
|
|
34587
|
+
this.console.error(`[Notifiers] Error reading notifiers:`, e);
|
|
34575
34588
|
return [];
|
|
34576
34589
|
}
|
|
34577
34590
|
}
|
|
@@ -35427,7 +35440,7 @@ class SpatialReasoningEngine {
|
|
|
35427
35440
|
id: deviceId,
|
|
35428
35441
|
name: device.name || deviceId,
|
|
35429
35442
|
providerType: providerTypeEnum,
|
|
35430
|
-
lastUsed:
|
|
35443
|
+
lastUsed: Date.now() - 60000, // Initialize to 1 minute ago so all LLMs start equal
|
|
35431
35444
|
errorCount: 0,
|
|
35432
35445
|
});
|
|
35433
35446
|
this.console.log(`[LLM] Using configured LLM: ${device.name}`);
|
|
@@ -35455,7 +35468,7 @@ class SpatialReasoningEngine {
|
|
|
35455
35468
|
id,
|
|
35456
35469
|
name: device.name || id,
|
|
35457
35470
|
providerType: providerTypeEnum,
|
|
35458
|
-
lastUsed:
|
|
35471
|
+
lastUsed: Date.now() - 60000, // Initialize to 1 minute ago so all LLMs start equal
|
|
35459
35472
|
errorCount: 0,
|
|
35460
35473
|
});
|
|
35461
35474
|
this.console.log(`[LLM] Auto-discovered: ${device.name}`);
|
|
@@ -37489,14 +37502,22 @@ class TrackingEngine {
|
|
|
37489
37502
|
this.lastLlmCallTime = Date.now();
|
|
37490
37503
|
}
|
|
37491
37504
|
/** Check and record LLM call - returns false if rate limited */
|
|
37492
|
-
tryLlmCall() {
|
|
37505
|
+
tryLlmCall(silent = false) {
|
|
37493
37506
|
if (!this.isLlmCallAllowed()) {
|
|
37494
|
-
|
|
37507
|
+
// Only log once per rate limit window, not every call
|
|
37508
|
+
if (!silent && !this.rateLimitLogged) {
|
|
37509
|
+
const remaining = Math.ceil((this.config.llmDebounceInterval || 30000) - (Date.now() - this.lastLlmCallTime)) / 1000;
|
|
37510
|
+
this.console.log(`[LLM] Rate limited, ${remaining.toFixed(0)}s until next call allowed`);
|
|
37511
|
+
this.rateLimitLogged = true;
|
|
37512
|
+
}
|
|
37495
37513
|
return false;
|
|
37496
37514
|
}
|
|
37515
|
+
this.rateLimitLogged = false;
|
|
37497
37516
|
this.recordLlmCall();
|
|
37498
37517
|
return true;
|
|
37499
37518
|
}
|
|
37519
|
+
/** Track if we've already logged rate limit message */
|
|
37520
|
+
rateLimitLogged = false;
|
|
37500
37521
|
/** Get spatial reasoning result for movement (uses RAG + LLM) with debouncing and fallback */
|
|
37501
37522
|
async getSpatialDescription(tracked, fromCameraId, toCameraId, transitTime, currentCameraId) {
|
|
37502
37523
|
const fallbackEnabled = this.config.llmFallbackEnabled ?? true;
|
|
@@ -37706,31 +37727,31 @@ class TrackingEngine {
|
|
|
37706
37727
|
}
|
|
37707
37728
|
/** Capture and cache a snapshot for a tracked object, and start LLM analysis immediately */
|
|
37708
37729
|
async captureAndCacheSnapshot(globalId, cameraId, eventType = 'entry') {
|
|
37730
|
+
// Skip if we already have a recent snapshot for this object (within 5 seconds)
|
|
37731
|
+
const existingSnapshot = this.snapshotCache.get(globalId);
|
|
37732
|
+
if (existingSnapshot && eventType !== 'exit') {
|
|
37733
|
+
// For entry/movement, we can reuse existing snapshot
|
|
37734
|
+
// For exit, we want a fresh snapshot while they're still visible
|
|
37735
|
+
return;
|
|
37736
|
+
}
|
|
37709
37737
|
try {
|
|
37710
37738
|
const camera = systemManager.getDeviceById(cameraId);
|
|
37711
37739
|
if (camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)) {
|
|
37712
37740
|
const mediaObject = await camera.takePicture();
|
|
37713
37741
|
if (mediaObject) {
|
|
37714
37742
|
this.snapshotCache.set(globalId, mediaObject);
|
|
37715
|
-
this.console.log(`[Snapshot] Cached snapshot for ${globalId.slice(0, 8)} from ${cameraId}`);
|
|
37716
37743
|
// Start LLM analysis immediately in parallel (don't await) - but respect rate limits
|
|
37717
37744
|
const tracked = this.state.getObject(globalId);
|
|
37718
37745
|
if (tracked && this.config.useLlmDescriptions && this.tryLlmCall()) {
|
|
37719
|
-
this.console.log(`[LLM Prefetch] Starting ${eventType} analysis for ${globalId.slice(0, 8)}`);
|
|
37720
37746
|
const descriptionPromise = eventType === 'exit'
|
|
37721
37747
|
? this.spatialReasoning.generateExitDescription(tracked, cameraId, mediaObject)
|
|
37722
37748
|
: this.spatialReasoning.generateEntryDescription(tracked, cameraId, mediaObject);
|
|
37723
37749
|
this.pendingDescriptions.set(globalId, descriptionPromise);
|
|
37724
|
-
// Log when complete (
|
|
37725
|
-
descriptionPromise.
|
|
37726
|
-
this.console.log(`[LLM Prefetch] ${eventType} analysis ready for ${globalId.slice(0, 8)}: "${result.description.substring(0, 40)}..."`);
|
|
37727
|
-
}).catch(e => {
|
|
37750
|
+
// Log when complete (but don't spam logs)
|
|
37751
|
+
descriptionPromise.catch(e => {
|
|
37728
37752
|
this.console.warn(`[LLM Prefetch] Failed for ${globalId.slice(0, 8)}: ${e}`);
|
|
37729
37753
|
});
|
|
37730
37754
|
}
|
|
37731
|
-
else if (tracked && this.config.useLlmDescriptions) {
|
|
37732
|
-
this.console.log(`[LLM Prefetch] Skipped for ${globalId.slice(0, 8)} - rate limited`);
|
|
37733
|
-
}
|
|
37734
37755
|
}
|
|
37735
37756
|
}
|
|
37736
37757
|
}
|
|
@@ -39360,7 +39381,7 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
39360
39381
|
llmDebounceInterval: {
|
|
39361
39382
|
title: 'LLM Rate Limit (seconds)',
|
|
39362
39383
|
type: 'number',
|
|
39363
|
-
defaultValue:
|
|
39384
|
+
defaultValue: 5,
|
|
39364
39385
|
description: 'Minimum time between LLM calls to prevent API rate limiting. Increase if you get rate limit errors. (0 = no limit)',
|
|
39365
39386
|
group: 'AI & Spatial Reasoning',
|
|
39366
39387
|
},
|