@blueharford/scrypted-spatial-awareness 0.4.0 → 0.4.2
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 +20 -1
- package/out/main.nodejs.js.map +1 -1
- package/package.json +1 -1
- package/src/main.ts +27 -1
- package/src/ui/editor-html.ts +24 -2
- package/out/plugin.zip +0 -0
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -432,8 +432,17 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
432
432
|
// ==================== Settings Implementation ====================
|
|
433
433
|
|
|
434
434
|
async getSettings(): Promise<Setting[]> {
|
|
435
|
-
const
|
|
435
|
+
const baseSettings = await this.storageSettings.getSettings();
|
|
436
436
|
|
|
437
|
+
// Build settings in desired order
|
|
438
|
+
const settings: Setting[] = [];
|
|
439
|
+
|
|
440
|
+
// Helper to find and add settings from baseSettings by group
|
|
441
|
+
const addGroup = (group: string) => {
|
|
442
|
+
baseSettings.filter(s => s.group === group).forEach(s => settings.push(s));
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// ==================== 1. Getting Started ====================
|
|
437
446
|
// Training Mode button that opens mobile-friendly training UI in modal
|
|
438
447
|
const trainingOnclickCode = `(function(){var e=document.getElementById('sa-training-modal');if(e)e.remove();var m=document.createElement('div');m.id='sa-training-modal';m.style.cssText='position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.85);z-index:2147483647;display:flex;align-items:center;justify-content:center;';var c=document.createElement('div');c.style.cssText='width:min(420px,95vw);height:92vh;max-height:900px;background:#121212;border-radius:8px;overflow:hidden;position:relative;box-shadow:0 8px 32px rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.1);';var b=document.createElement('button');b.innerHTML='×';b.style.cssText='position:absolute;top:8px;right:8px;z-index:2147483647;background:rgba(255,255,255,0.1);color:white;border:none;width:32px;height:32px;border-radius:4px;font-size:18px;cursor:pointer;line-height:1;';b.onclick=function(){m.remove();};var f=document.createElement('iframe');f.src='/endpoint/@blueharford/scrypted-spatial-awareness/ui/training';f.style.cssText='width:100%;height:100%;border:none;';c.appendChild(b);c.appendChild(f);m.appendChild(c);m.onclick=function(ev){if(ev.target===m)m.remove();};document.body.appendChild(m);})()`;
|
|
439
448
|
|
|
@@ -517,6 +526,7 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
517
526
|
group: 'Getting Started',
|
|
518
527
|
});
|
|
519
528
|
|
|
529
|
+
// ==================== 2. Topology ====================
|
|
520
530
|
// Topology editor button that opens modal overlay (appended to body for proper z-index)
|
|
521
531
|
const onclickCode = `(function(){var e=document.getElementById('sa-topology-modal');if(e)e.remove();var m=document.createElement('div');m.id='sa-topology-modal';m.style.cssText='position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.85);z-index:2147483647;display:flex;align-items:center;justify-content:center;';var c=document.createElement('div');c.style.cssText='width:95vw;height:92vh;max-width:1800px;background:#121212;border-radius:8px;overflow:hidden;position:relative;box-shadow:0 8px 32px rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.1);';var b=document.createElement('button');b.innerHTML='×';b.style.cssText='position:absolute;top:8px;right:8px;z-index:2147483647;background:rgba(255,255,255,0.1);color:white;border:none;width:32px;height:32px;border-radius:4px;font-size:18px;cursor:pointer;line-height:1;';b.onclick=function(){m.remove();};var f=document.createElement('iframe');f.src='/endpoint/@blueharford/scrypted-spatial-awareness/ui/editor';f.style.cssText='width:100%;height:100%;border:none;';c.appendChild(b);c.appendChild(f);m.appendChild(c);m.onclick=function(ev){if(ev.target===m)m.remove();};document.body.appendChild(m);})()`;
|
|
522
532
|
|
|
@@ -568,6 +578,10 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
568
578
|
group: 'Topology',
|
|
569
579
|
});
|
|
570
580
|
|
|
581
|
+
// ==================== 3. Cameras ====================
|
|
582
|
+
addGroup('Cameras');
|
|
583
|
+
|
|
584
|
+
// ==================== 4. Status ====================
|
|
571
585
|
// Add status display
|
|
572
586
|
const activeCount = this.trackingState.getActiveCount();
|
|
573
587
|
const topologyJson = this.storage.getItem('topology');
|
|
@@ -613,6 +627,15 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
613
627
|
});
|
|
614
628
|
}
|
|
615
629
|
|
|
630
|
+
// ==================== 5. Tracking ====================
|
|
631
|
+
addGroup('Tracking');
|
|
632
|
+
|
|
633
|
+
// ==================== 6. AI & Spatial Reasoning ====================
|
|
634
|
+
addGroup('AI & Spatial Reasoning');
|
|
635
|
+
|
|
636
|
+
// ==================== 7. Alerts ====================
|
|
637
|
+
addGroup('Alerts');
|
|
638
|
+
|
|
616
639
|
// Add alert rules configuration UI
|
|
617
640
|
const alertRules = this.alertManager.getRules();
|
|
618
641
|
const rulesHtml = this.generateAlertRulesHtml(alertRules);
|
|
@@ -624,6 +647,9 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
624
647
|
group: 'Alerts',
|
|
625
648
|
});
|
|
626
649
|
|
|
650
|
+
// ==================== 8. MQTT Integration ====================
|
|
651
|
+
addGroup('MQTT Integration');
|
|
652
|
+
|
|
627
653
|
return settings;
|
|
628
654
|
}
|
|
629
655
|
|
package/src/ui/editor-html.ts
CHANGED
|
@@ -342,8 +342,21 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
342
342
|
if (response.ok) {
|
|
343
343
|
topology = await response.json();
|
|
344
344
|
if (!topology.drawings) topology.drawings = [];
|
|
345
|
+
// Load floor plan from separate storage (handles legacy imageData in topology too)
|
|
345
346
|
if (topology.floorPlan?.imageData) {
|
|
347
|
+
// Legacy: imageData was stored in topology
|
|
346
348
|
await loadFloorPlanImage(topology.floorPlan.imageData);
|
|
349
|
+
} else if (topology.floorPlan?.type === 'uploaded') {
|
|
350
|
+
// New: load from separate endpoint
|
|
351
|
+
try {
|
|
352
|
+
const fpResponse = await fetch('../api/floor-plan');
|
|
353
|
+
if (fpResponse.ok) {
|
|
354
|
+
const fpData = await fpResponse.json();
|
|
355
|
+
if (fpData.imageData) {
|
|
356
|
+
await loadFloorPlanImage(fpData.imageData);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} catch (err) { console.error('Failed to load floor plan:', err); }
|
|
347
360
|
} else if (topology.floorPlan?.type === 'blank') {
|
|
348
361
|
blankCanvasMode = true;
|
|
349
362
|
}
|
|
@@ -976,7 +989,16 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
976
989
|
reader.onload = async (e) => {
|
|
977
990
|
const imageData = e.target.result;
|
|
978
991
|
await loadFloorPlanImage(imageData);
|
|
979
|
-
|
|
992
|
+
// Store floor plan separately via API (not in topology JSON to avoid size issues)
|
|
993
|
+
try {
|
|
994
|
+
await fetch('../api/floor-plan', {
|
|
995
|
+
method: 'POST',
|
|
996
|
+
headers: { 'Content-Type': 'application/json' },
|
|
997
|
+
body: JSON.stringify({ imageData })
|
|
998
|
+
});
|
|
999
|
+
} catch (err) { console.error('Failed to save floor plan:', err); }
|
|
1000
|
+
// Store reference in topology (without the large imageData)
|
|
1001
|
+
topology.floorPlan = { type: 'uploaded', width: floorPlanImage.width, height: floorPlanImage.height };
|
|
980
1002
|
closeModal('upload-modal');
|
|
981
1003
|
render();
|
|
982
1004
|
};
|
|
@@ -1138,7 +1160,7 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
1138
1160
|
function updateConnectionName(id, value) { const conn = topology.connections.find(c => c.id === id); if (conn) conn.name = value; updateUI(); }
|
|
1139
1161
|
function updateTransitTime(id, field, value) { const conn = topology.connections.find(c => c.id === id); if (conn) conn.transitTime[field] = parseInt(value) * 1000; }
|
|
1140
1162
|
function updateConnectionBidi(id, value) { const conn = topology.connections.find(c => c.id === id); if (conn) conn.bidirectional = value; render(); }
|
|
1141
|
-
function deleteCamera(id) { if (!confirm('Delete this camera?')) return; topology.cameras = topology.cameras.filter(c => c.deviceId !== id); topology.connections = topology.connections.filter(c => c.fromCameraId !== id && c.toCameraId !== id); selectedItem = null; document.getElementById('properties-panel').innerHTML = '<h3>Properties</h3><p style="color: #666;">Select a camera or connection.</p>'; updateUI(); render(); }
|
|
1163
|
+
function deleteCamera(id) { if (!confirm('Delete this camera?')) return; topology.cameras = topology.cameras.filter(c => c.deviceId !== id); topology.connections = topology.connections.filter(c => c.fromCameraId !== id && c.toCameraId !== id); selectedItem = null; document.getElementById('properties-panel').innerHTML = '<h3>Properties</h3><p style="color: #666;">Select a camera or connection.</p>'; updateCameraSelects(); updateUI(); render(); }
|
|
1142
1164
|
function deleteConnection(id) { if (!confirm('Delete this connection?')) return; topology.connections = topology.connections.filter(c => c.id !== id); selectedItem = null; document.getElementById('properties-panel').innerHTML = '<h3>Properties</h3><p style="color: #666;">Select a camera or connection.</p>'; updateUI(); render(); }
|
|
1143
1165
|
function setTool(tool) {
|
|
1144
1166
|
currentTool = tool;
|
package/out/plugin.zip
DELETED
|
Binary file
|