@blueharford/scrypted-spatial-awareness 0.6.26 → 0.6.27
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 +66 -119
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/core/spatial-reasoning.ts +47 -28
- package/src/core/tracking-engine.ts +3 -0
- package/src/main.ts +18 -96
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/out/main.nodejs.js
CHANGED
|
@@ -35410,46 +35410,39 @@ class SpatialReasoningEngine {
|
|
|
35410
35410
|
// Load balancing for multiple LLMs
|
|
35411
35411
|
llmDevices = [];
|
|
35412
35412
|
llmIndex = 0;
|
|
35413
|
-
/** Find
|
|
35413
|
+
/** Find LLM devices - uses configured device or auto-discovers all for load balancing */
|
|
35414
35414
|
async findAllLlmDevices() {
|
|
35415
35415
|
if (this.llmSearched)
|
|
35416
35416
|
return;
|
|
35417
35417
|
this.llmSearched = true;
|
|
35418
35418
|
try {
|
|
35419
|
+
// If a specific LLM device is configured, use only that one
|
|
35420
|
+
if (this.config.llmDeviceId) {
|
|
35421
|
+
const device = systemManager.getDeviceById(this.config.llmDeviceId);
|
|
35422
|
+
if (device?.interfaces?.includes('ChatCompletion')) {
|
|
35423
|
+
const providerTypeEnum = this.detectProviderType(device);
|
|
35424
|
+
this.llmDevices.push({
|
|
35425
|
+
device: device,
|
|
35426
|
+
id: this.config.llmDeviceId,
|
|
35427
|
+
name: device.name || this.config.llmDeviceId,
|
|
35428
|
+
providerType: providerTypeEnum,
|
|
35429
|
+
lastUsed: 0,
|
|
35430
|
+
errorCount: 0,
|
|
35431
|
+
});
|
|
35432
|
+
this.console.log(`[LLM] Using configured LLM: ${device.name}`);
|
|
35433
|
+
return;
|
|
35434
|
+
}
|
|
35435
|
+
else {
|
|
35436
|
+
this.console.warn(`[LLM] Configured device ${this.config.llmDeviceId} not found or doesn't support ChatCompletion`);
|
|
35437
|
+
}
|
|
35438
|
+
}
|
|
35439
|
+
// Auto-discover all LLM devices for load balancing
|
|
35419
35440
|
for (const id of Object.keys(systemManager.getSystemState())) {
|
|
35420
35441
|
const device = systemManager.getDeviceById(id);
|
|
35421
35442
|
if (!device)
|
|
35422
35443
|
continue;
|
|
35423
35444
|
if (device.interfaces?.includes('ChatCompletion')) {
|
|
35424
|
-
const
|
|
35425
|
-
const pluginId = device.pluginId?.toLowerCase() || '';
|
|
35426
|
-
// Identify the provider type for image format selection
|
|
35427
|
-
let providerType = 'Unknown';
|
|
35428
|
-
let providerTypeEnum = 'unknown';
|
|
35429
|
-
if (deviceName.includes('openai') || deviceName.includes('gpt')) {
|
|
35430
|
-
providerType = 'OpenAI';
|
|
35431
|
-
providerTypeEnum = 'openai';
|
|
35432
|
-
}
|
|
35433
|
-
else if (deviceName.includes('anthropic') || deviceName.includes('claude')) {
|
|
35434
|
-
providerType = 'Anthropic';
|
|
35435
|
-
providerTypeEnum = 'anthropic';
|
|
35436
|
-
}
|
|
35437
|
-
else if (deviceName.includes('ollama')) {
|
|
35438
|
-
providerType = 'Ollama';
|
|
35439
|
-
providerTypeEnum = 'openai';
|
|
35440
|
-
}
|
|
35441
|
-
else if (deviceName.includes('gemini') || deviceName.includes('google')) {
|
|
35442
|
-
providerType = 'Google';
|
|
35443
|
-
providerTypeEnum = 'openai';
|
|
35444
|
-
}
|
|
35445
|
-
else if (deviceName.includes('llama')) {
|
|
35446
|
-
providerType = 'llama.cpp';
|
|
35447
|
-
providerTypeEnum = 'openai';
|
|
35448
|
-
}
|
|
35449
|
-
else if (pluginId.includes('@scrypted/llm') || pluginId.includes('llm')) {
|
|
35450
|
-
providerType = 'Scrypted LLM';
|
|
35451
|
-
providerTypeEnum = 'unknown';
|
|
35452
|
-
}
|
|
35445
|
+
const providerTypeEnum = this.detectProviderType(device);
|
|
35453
35446
|
this.llmDevices.push({
|
|
35454
35447
|
device: device,
|
|
35455
35448
|
id,
|
|
@@ -35458,7 +35451,7 @@ class SpatialReasoningEngine {
|
|
|
35458
35451
|
lastUsed: 0,
|
|
35459
35452
|
errorCount: 0,
|
|
35460
35453
|
});
|
|
35461
|
-
this.console.log(`[LLM] Found
|
|
35454
|
+
this.console.log(`[LLM] Found: ${device.name}`);
|
|
35462
35455
|
}
|
|
35463
35456
|
}
|
|
35464
35457
|
if (this.llmDevices.length === 0) {
|
|
@@ -35472,6 +35465,30 @@ class SpatialReasoningEngine {
|
|
|
35472
35465
|
this.console.error('[LLM] Error searching for LLM devices:', e);
|
|
35473
35466
|
}
|
|
35474
35467
|
}
|
|
35468
|
+
/** Detect the provider type from device name */
|
|
35469
|
+
detectProviderType(device) {
|
|
35470
|
+
const deviceName = device.name?.toLowerCase() || '';
|
|
35471
|
+
const pluginId = device.pluginId?.toLowerCase() || '';
|
|
35472
|
+
if (deviceName.includes('openai') || deviceName.includes('gpt')) {
|
|
35473
|
+
return 'openai';
|
|
35474
|
+
}
|
|
35475
|
+
else if (deviceName.includes('anthropic') || deviceName.includes('claude')) {
|
|
35476
|
+
return 'anthropic';
|
|
35477
|
+
}
|
|
35478
|
+
else if (deviceName.includes('ollama')) {
|
|
35479
|
+
return 'openai'; // Ollama uses OpenAI-compatible format
|
|
35480
|
+
}
|
|
35481
|
+
else if (deviceName.includes('gemini') || deviceName.includes('google')) {
|
|
35482
|
+
return 'openai'; // Google uses OpenAI-compatible format
|
|
35483
|
+
}
|
|
35484
|
+
else if (deviceName.includes('llama')) {
|
|
35485
|
+
return 'openai'; // llama.cpp uses OpenAI-compatible format
|
|
35486
|
+
}
|
|
35487
|
+
else if (pluginId.includes('@scrypted/llm') || pluginId.includes('llm')) {
|
|
35488
|
+
return 'unknown';
|
|
35489
|
+
}
|
|
35490
|
+
return 'unknown';
|
|
35491
|
+
}
|
|
35475
35492
|
/** Get the next available LLM using round-robin with least-recently-used preference */
|
|
35476
35493
|
async findLlmDevice() {
|
|
35477
35494
|
await this.findAllLlmDevices();
|
|
@@ -37326,6 +37343,7 @@ class TrackingEngine {
|
|
|
37326
37343
|
// Initialize spatial reasoning engine
|
|
37327
37344
|
const spatialConfig = {
|
|
37328
37345
|
enableLlm: config.useLlmDescriptions,
|
|
37346
|
+
llmDeviceId: config.llmDeviceId,
|
|
37329
37347
|
enableLandmarkLearning: config.enableLandmarkLearning ?? true,
|
|
37330
37348
|
landmarkConfidenceThreshold: config.landmarkConfidenceThreshold ?? 0.7,
|
|
37331
37349
|
contextCacheTtl: 60000, // 1 minute cache
|
|
@@ -39439,20 +39457,21 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
39439
39457
|
defaultValue: 'scrypted/spatial-awareness',
|
|
39440
39458
|
group: 'MQTT Integration',
|
|
39441
39459
|
},
|
|
39442
|
-
//
|
|
39443
|
-
|
|
39444
|
-
title: '
|
|
39445
|
-
type: '
|
|
39446
|
-
|
|
39447
|
-
|
|
39460
|
+
// Integrations
|
|
39461
|
+
llmDevice: {
|
|
39462
|
+
title: 'LLM Provider',
|
|
39463
|
+
type: 'device',
|
|
39464
|
+
deviceFilter: `interfaces.includes('ChatCompletion')`,
|
|
39465
|
+
description: 'Select the LLM plugin to use for smart descriptions (e.g., OpenAI, Anthropic, Ollama)',
|
|
39466
|
+
group: 'Integrations',
|
|
39448
39467
|
},
|
|
39449
39468
|
defaultNotifiers: {
|
|
39450
|
-
title: '
|
|
39469
|
+
title: 'Notification Service',
|
|
39451
39470
|
type: 'device',
|
|
39452
39471
|
multiple: true,
|
|
39453
39472
|
deviceFilter: `interfaces.includes('${sdk_1.ScryptedInterface.Notifier}')`,
|
|
39454
|
-
description: 'Select one or more notifiers to receive alerts',
|
|
39455
|
-
group: '
|
|
39473
|
+
description: 'Select one or more notifiers to receive alerts (e.g., Pushover, Home Assistant)',
|
|
39474
|
+
group: 'Integrations',
|
|
39456
39475
|
},
|
|
39457
39476
|
// Tracked Cameras
|
|
39458
39477
|
trackedCameras: {
|
|
@@ -39463,12 +39482,6 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
39463
39482
|
group: 'Cameras',
|
|
39464
39483
|
description: 'Select cameras with object detection to track',
|
|
39465
39484
|
},
|
|
39466
|
-
// Alert Rules (stored as JSON)
|
|
39467
|
-
alertRules: {
|
|
39468
|
-
title: 'Alert Rules',
|
|
39469
|
-
type: 'string',
|
|
39470
|
-
hide: true,
|
|
39471
|
-
},
|
|
39472
39485
|
});
|
|
39473
39486
|
constructor(nativeId) {
|
|
39474
39487
|
super(nativeId);
|
|
@@ -39555,7 +39568,8 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
39555
39568
|
loiteringThreshold: (this.storageSettings.values.loiteringThreshold || 3) * 1000,
|
|
39556
39569
|
objectAlertCooldown: (this.storageSettings.values.objectAlertCooldown || 30) * 1000,
|
|
39557
39570
|
useLlmDescriptions: this.storageSettings.values.useLlmDescriptions ?? true,
|
|
39558
|
-
|
|
39571
|
+
llmDeviceId: this.storageSettings.values.llmDevice || undefined,
|
|
39572
|
+
llmDebounceInterval: (this.storageSettings.values.llmDebounceInterval || 30) * 1000,
|
|
39559
39573
|
llmFallbackEnabled: this.storageSettings.values.llmFallbackEnabled ?? true,
|
|
39560
39574
|
llmFallbackTimeout: (this.storageSettings.values.llmFallbackTimeout || 3) * 1000,
|
|
39561
39575
|
enableTransitTimeLearning: this.storageSettings.values.enableTransitTimeLearning ?? true,
|
|
@@ -39848,84 +39862,16 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
39848
39862
|
}
|
|
39849
39863
|
// ==================== 5. Tracking ====================
|
|
39850
39864
|
addGroup('Tracking');
|
|
39851
|
-
// ==================== 6.
|
|
39865
|
+
// ==================== 6. Integrations ====================
|
|
39866
|
+
addGroup('Integrations');
|
|
39867
|
+
// ==================== 7. AI & Spatial Reasoning ====================
|
|
39852
39868
|
addGroup('AI & Spatial Reasoning');
|
|
39853
|
-
// ====================
|
|
39869
|
+
// ==================== 8. Auto-Topology Discovery ====================
|
|
39854
39870
|
addGroup('Auto-Topology Discovery');
|
|
39855
|
-
// ==================== 8. Alerts ====================
|
|
39856
|
-
addGroup('Alerts');
|
|
39857
|
-
// Add alert rules configuration UI
|
|
39858
|
-
const alertRules = this.alertManager.getRules();
|
|
39859
|
-
const rulesHtml = this.generateAlertRulesHtml(alertRules);
|
|
39860
|
-
settings.push({
|
|
39861
|
-
key: 'alertRulesEditor',
|
|
39862
|
-
title: 'Alert Rules',
|
|
39863
|
-
type: 'html',
|
|
39864
|
-
value: rulesHtml,
|
|
39865
|
-
group: 'Alerts',
|
|
39866
|
-
});
|
|
39867
39871
|
// ==================== 9. MQTT Integration ====================
|
|
39868
39872
|
addGroup('MQTT Integration');
|
|
39869
39873
|
return settings;
|
|
39870
39874
|
}
|
|
39871
|
-
generateAlertRulesHtml(rules) {
|
|
39872
|
-
const ruleRows = rules.map(rule => `
|
|
39873
|
-
<tr data-rule-id="${rule.id}">
|
|
39874
|
-
<td style="padding:8px;border-bottom:1px solid #333;">
|
|
39875
|
-
<input type="checkbox" ${rule.enabled ? 'checked' : ''}
|
|
39876
|
-
onchange="(function(el){var rules=JSON.parse(localStorage.getItem('sa-temp-rules')||'[]');var r=rules.find(x=>x.id==='${rule.id}');if(r)r.enabled=el.checked;localStorage.setItem('sa-temp-rules',JSON.stringify(rules));})(this)" />
|
|
39877
|
-
</td>
|
|
39878
|
-
<td style="padding:8px;border-bottom:1px solid #333;color:#fff;">${rule.name}</td>
|
|
39879
|
-
<td style="padding:8px;border-bottom:1px solid #333;color:#888;">${rule.type}</td>
|
|
39880
|
-
<td style="padding:8px;border-bottom:1px solid #333;">
|
|
39881
|
-
<span style="padding:2px 8px;border-radius:4px;font-size:12px;background:${rule.severity === 'critical' ? '#e94560' :
|
|
39882
|
-
rule.severity === 'warning' ? '#f39c12' : '#3498db'};color:white;">${rule.severity}</span>
|
|
39883
|
-
</td>
|
|
39884
|
-
<td style="padding:8px;border-bottom:1px solid #333;color:#888;">${Math.round(rule.cooldown / 1000)}s</td>
|
|
39885
|
-
</tr>
|
|
39886
|
-
`).join('');
|
|
39887
|
-
const initCode = `localStorage.setItem('sa-temp-rules',JSON.stringify(${JSON.stringify(rules)}))`;
|
|
39888
|
-
const saveCode = `(function(){var rules=JSON.parse(localStorage.getItem('sa-temp-rules')||'[]');fetch('/endpoint/@blueharford/scrypted-spatial-awareness/api/alert-rules',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(rules)}).then(r=>r.json()).then(d=>{if(d.success)alert('Alert rules saved!');else alert('Error: '+d.error);}).catch(e=>alert('Error: '+e));})()`;
|
|
39889
|
-
return `
|
|
39890
|
-
<style>
|
|
39891
|
-
.sa-rules-table { width:100%; border-collapse:collapse; margin-top:10px; }
|
|
39892
|
-
.sa-rules-table th { text-align:left; padding:10px 8px; border-bottom:2px solid #e94560; color:#e94560; font-size:13px; }
|
|
39893
|
-
.sa-save-rules-btn {
|
|
39894
|
-
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
|
|
39895
|
-
color: white;
|
|
39896
|
-
border: none;
|
|
39897
|
-
padding: 10px 20px;
|
|
39898
|
-
border-radius: 6px;
|
|
39899
|
-
font-size: 14px;
|
|
39900
|
-
font-weight: 600;
|
|
39901
|
-
cursor: pointer;
|
|
39902
|
-
margin-top: 15px;
|
|
39903
|
-
}
|
|
39904
|
-
.sa-save-rules-btn:hover { opacity: 0.9; }
|
|
39905
|
-
.sa-rules-container { background:#16213e; border-radius:8px; padding:15px; }
|
|
39906
|
-
.sa-rules-desc { color:#888; font-size:13px; margin-bottom:10px; }
|
|
39907
|
-
</style>
|
|
39908
|
-
<div class="sa-rules-container">
|
|
39909
|
-
<p class="sa-rules-desc">Enable or disable alert types. Movement alerts notify you when someone moves between cameras.</p>
|
|
39910
|
-
<table class="sa-rules-table">
|
|
39911
|
-
<thead>
|
|
39912
|
-
<tr>
|
|
39913
|
-
<th style="width:40px;">On</th>
|
|
39914
|
-
<th>Alert Type</th>
|
|
39915
|
-
<th>Event</th>
|
|
39916
|
-
<th>Severity</th>
|
|
39917
|
-
<th>Cooldown</th>
|
|
39918
|
-
</tr>
|
|
39919
|
-
</thead>
|
|
39920
|
-
<tbody>
|
|
39921
|
-
${ruleRows}
|
|
39922
|
-
</tbody>
|
|
39923
|
-
</table>
|
|
39924
|
-
<button class="sa-save-rules-btn" onclick="${saveCode}">Save Alert Rules</button>
|
|
39925
|
-
<script>(function(){${initCode}})();</script>
|
|
39926
|
-
</div>
|
|
39927
|
-
`;
|
|
39928
|
-
}
|
|
39929
39875
|
async putSetting(key, value) {
|
|
39930
39876
|
await this.storageSettings.putSetting(key, value);
|
|
39931
39877
|
// Handle setting changes that require engine restart
|
|
@@ -39940,6 +39886,7 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
39940
39886
|
key === 'llmDebounceInterval' ||
|
|
39941
39887
|
key === 'llmFallbackEnabled' ||
|
|
39942
39888
|
key === 'llmFallbackTimeout' ||
|
|
39889
|
+
key === 'llmDevice' ||
|
|
39943
39890
|
key === 'enableTransitTimeLearning' ||
|
|
39944
39891
|
key === 'enableConnectionSuggestions' ||
|
|
39945
39892
|
key === 'enableLandmarkLearning' ||
|