@blueharford/scrypted-spatial-awareness 0.6.22 → 0.6.24

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/out/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueharford/scrypted-spatial-awareness",
3
- "version": "0.6.22",
3
+ "version": "0.6.24",
4
4
  "description": "Cross-camera object tracking for Scrypted NVR with spatial awareness",
5
5
  "author": "Joshua Seidel <blueharford>",
6
6
  "license": "Apache-2.0",
@@ -155,6 +155,7 @@ export class AlertManager {
155
155
 
156
156
  /**
157
157
  * Get notification title based on alert type
158
+ * For movement alerts with LLM descriptions, use the smart description as title
158
159
  */
159
160
  private getNotificationTitle(alert: Alert): string {
160
161
  const prefix = alert.severity === 'critical' ? '🚨 ' :
@@ -167,11 +168,26 @@ export class AlertManager {
167
168
 
168
169
  switch (alert.type) {
169
170
  case 'property_entry':
171
+ // Legacy - use simple title
170
172
  return `${prefix}${objectType} Arrived`;
171
173
  case 'property_exit':
174
+ // Legacy - use simple title
172
175
  return `${prefix}${objectType} Left`;
173
176
  case 'movement':
174
- // Include destination in title
177
+ // For smart activity alerts, use the LLM description as title if available
178
+ // This gives us rich context like "Person walking toward front door"
179
+ if (alert.details.objectLabel && alert.details.usedLlm) {
180
+ // Truncate to reasonable title length (first sentence or 60 chars)
181
+ let smartTitle = alert.details.objectLabel;
182
+ const firstPeriod = smartTitle.indexOf('.');
183
+ if (firstPeriod > 0 && firstPeriod < 60) {
184
+ smartTitle = smartTitle.substring(0, firstPeriod);
185
+ } else if (smartTitle.length > 60) {
186
+ smartTitle = smartTitle.substring(0, 57) + '...';
187
+ }
188
+ return `${prefix}${smartTitle}`;
189
+ }
190
+ // Fallback: include destination in title
175
191
  const dest = alert.details.toCameraName || 'area';
176
192
  return `${prefix}${objectType} → ${dest}`;
177
193
  case 'unusual_path':
@@ -748,16 +748,20 @@ Use the mount height to help estimate distances - objects at ground level will a
748
748
  return null;
749
749
  }
750
750
 
751
- // Correlate if we have multiple cameras
751
+ // ALWAYS generate suggestions from each camera's analysis first
752
+ // This ensures landmarks and zones from individual cameras are captured
753
+ for (const analysis of analyses) {
754
+ this.generateSuggestionsFromAnalysis(analysis);
755
+ }
756
+ this.console.log(`[Discovery] Generated suggestions from ${analyses.length} camera analyses`);
757
+
758
+ // Then correlate if we have multiple cameras (adds shared landmarks and connections)
752
759
  let correlation: TopologyCorrelation | null = null;
753
760
  if (analyses.length >= 2) {
754
761
  correlation = await this.correlateScenes(analyses);
755
762
  if (correlation) {
756
763
  this.generateSuggestionsFromCorrelation(correlation);
757
764
  }
758
- } else if (analyses.length === 1) {
759
- // Single camera - generate suggestions from its analysis
760
- this.generateSuggestionsFromAnalysis(analyses[0]);
761
765
  }
762
766
 
763
767
  this.status.lastScanTime = Date.now();
@@ -579,32 +579,19 @@ export class TrackingEngine {
579
579
  this.console.log(`[Entry Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
580
580
  }
581
581
 
582
- if (isEntryPoint) {
583
- // Entry point - generate property entry alert
584
- await this.alertManager.checkAndAlert('property_entry', tracked, {
585
- cameraId: sighting.cameraId,
586
- cameraName: sighting.cameraName,
587
- objectClass: sighting.detection.className,
588
- objectLabel: spatialResult.description,
589
- detectionId: sighting.detectionId,
590
- involvedLandmarks: spatialResult.involvedLandmarks?.map(l => l.name),
591
- usedLlm: spatialResult.usedLlm,
592
- });
593
- } else {
594
- // Non-entry point - still alert about activity using movement alert type
595
- // This notifies about any activity around the property using topology context
596
- await this.alertManager.checkAndAlert('movement', tracked, {
597
- cameraId: sighting.cameraId,
598
- cameraName: sighting.cameraName,
599
- toCameraId: sighting.cameraId,
600
- toCameraName: sighting.cameraName,
601
- objectClass: sighting.detection.className,
602
- objectLabel: spatialResult.description, // Use spatial reasoning description (topology-based)
603
- detectionId: sighting.detectionId,
604
- involvedLandmarks: spatialResult.involvedLandmarks?.map(l => l.name),
605
- usedLlm: spatialResult.usedLlm,
606
- });
607
- }
582
+ // Always use movement alert type for smart notifications with LLM descriptions
583
+ // The property_entry/property_exit types are legacy and disabled by default
584
+ await this.alertManager.checkAndAlert('movement', tracked, {
585
+ cameraId: sighting.cameraId,
586
+ cameraName: sighting.cameraName,
587
+ toCameraId: sighting.cameraId,
588
+ toCameraName: sighting.cameraName,
589
+ objectClass: sighting.detection.className,
590
+ objectLabel: spatialResult.description, // Smart LLM-generated description
591
+ detectionId: sighting.detectionId,
592
+ involvedLandmarks: spatialResult.involvedLandmarks?.map(l => l.name),
593
+ usedLlm: spatialResult.usedLlm,
594
+ });
608
595
 
609
596
  this.recordAlertTime(globalId);
610
597
  }, this.config.loiteringThreshold);
@@ -731,9 +718,12 @@ export class TrackingEngine {
731
718
  this.console.log(`[Exit Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
732
719
  }
733
720
 
734
- await this.alertManager.checkAndAlert('property_exit', current, {
721
+ // Use movement alert for exit too - smart notifications with LLM descriptions
722
+ await this.alertManager.checkAndAlert('movement', current, {
735
723
  cameraId: sighting.cameraId,
736
724
  cameraName: sighting.cameraName,
725
+ toCameraId: sighting.cameraId,
726
+ toCameraName: sighting.cameraName,
737
727
  objectClass: current.className,
738
728
  objectLabel: spatialResult.description,
739
729
  involvedLandmarks: spatialResult.involvedLandmarks?.map(l => l.name),
package/src/main.ts CHANGED
@@ -1919,6 +1919,9 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
1919
1919
  const distanceFeet = landmarkData.distanceFeet || 50; // Default 50ft if not set
1920
1920
  const distanceInPixels = distanceFeet * floorPlanScale;
1921
1921
 
1922
+ // Debug: log the distance data to verify it's being used correctly
1923
+ this.console.log(`[Discovery] Landmark "${suggestion.landmark.name}" distance data: distanceFeet=${distanceFeet}, distance="${landmarkData.distance || 'not set'}", floorPlanScale=${floorPlanScale}, distanceInPixels=${distanceInPixels}`);
1924
+
1922
1925
  // Use bounding box for horizontal positioning within the FOV
1923
1926
  const bbox = landmarkData.boundingBox as [number, number, number, number] | undefined;
1924
1927
 
@@ -128,8 +128,8 @@ export function createDefaultRules(): AlertRule[] {
128
128
  return [
129
129
  {
130
130
  id: 'property-entry',
131
- name: 'Property Entry',
132
- enabled: true,
131
+ name: 'Property Entry (Legacy)',
132
+ enabled: false, // Disabled - use movement alerts with LLM descriptions instead
133
133
  type: 'property_entry',
134
134
  conditions: [],
135
135
  severity: 'info',
@@ -138,8 +138,8 @@ export function createDefaultRules(): AlertRule[] {
138
138
  },
139
139
  {
140
140
  id: 'property-exit',
141
- name: 'Property Exit',
142
- enabled: true,
141
+ name: 'Property Exit (Legacy)',
142
+ enabled: false, // Disabled - use movement alerts with LLM descriptions instead
143
143
  type: 'property_exit',
144
144
  conditions: [],
145
145
  severity: 'info',
@@ -148,7 +148,7 @@ export function createDefaultRules(): AlertRule[] {
148
148
  },
149
149
  {
150
150
  id: 'movement',
151
- name: 'Movement Between Cameras',
151
+ name: 'Smart Activity Alerts',
152
152
  enabled: true,
153
153
  type: 'movement',
154
154
  conditions: [],