@blueharford/scrypted-spatial-awareness 0.1.8 → 0.1.9
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/package.json +1 -1
- package/src/main.ts +34 -0
- package/src/ui/editor-html.ts +40 -3
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -491,6 +491,10 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
491
491
|
return this.handleAlertsRequest(request, response);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
+
if (path.endsWith('/api/cameras')) {
|
|
495
|
+
return this.handleCamerasRequest(response);
|
|
496
|
+
}
|
|
497
|
+
|
|
494
498
|
if (path.endsWith('/api/floor-plan')) {
|
|
495
499
|
return this.handleFloorPlanRequest(request, response);
|
|
496
500
|
}
|
|
@@ -597,6 +601,36 @@ export class SpatialAwarenessPlugin extends ScryptedDeviceBase
|
|
|
597
601
|
});
|
|
598
602
|
}
|
|
599
603
|
|
|
604
|
+
private handleCamerasRequest(response: HttpResponse): void {
|
|
605
|
+
try {
|
|
606
|
+
// Get all devices with ObjectDetector interface
|
|
607
|
+
const cameras: { id: string; name: string }[] = [];
|
|
608
|
+
|
|
609
|
+
for (const id of Object.keys(systemManager.getSystemState())) {
|
|
610
|
+
try {
|
|
611
|
+
const device = systemManager.getDeviceById(id);
|
|
612
|
+
if (device && device.interfaces?.includes(ScryptedInterface.ObjectDetector)) {
|
|
613
|
+
cameras.push({
|
|
614
|
+
id: id,
|
|
615
|
+
name: device.name || `Camera ${id}`,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
} catch (e) {
|
|
619
|
+
// Skip devices that can't be accessed
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
response.send(JSON.stringify(cameras), {
|
|
624
|
+
headers: { 'Content-Type': 'application/json' },
|
|
625
|
+
});
|
|
626
|
+
} catch (e) {
|
|
627
|
+
this.console.error('Error getting cameras:', e);
|
|
628
|
+
response.send(JSON.stringify([]), {
|
|
629
|
+
headers: { 'Content-Type': 'application/json' },
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
600
634
|
private handleFloorPlanRequest(request: HttpRequest, response: HttpResponse): void {
|
|
601
635
|
if (request.method === 'GET') {
|
|
602
636
|
const imageData = this.storage.getItem('floorPlanImage');
|
package/src/ui/editor-html.ts
CHANGED
|
@@ -260,7 +260,17 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
async function loadAvailableCameras() {
|
|
263
|
-
|
|
263
|
+
try {
|
|
264
|
+
const response = await fetch('../api/cameras');
|
|
265
|
+
if (response.ok) {
|
|
266
|
+
availableCameras = await response.json();
|
|
267
|
+
} else {
|
|
268
|
+
availableCameras = [];
|
|
269
|
+
}
|
|
270
|
+
} catch (e) {
|
|
271
|
+
console.error('Failed to load cameras:', e);
|
|
272
|
+
availableCameras = [];
|
|
273
|
+
}
|
|
264
274
|
updateCameraSelects();
|
|
265
275
|
}
|
|
266
276
|
|
|
@@ -431,14 +441,20 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
431
441
|
|
|
432
442
|
function addCamera() {
|
|
433
443
|
const deviceId = document.getElementById('camera-device-select').value;
|
|
434
|
-
|
|
444
|
+
if (!deviceId) {
|
|
445
|
+
alert('Please select a camera');
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const selectedCam = availableCameras.find(c => c.id === deviceId);
|
|
449
|
+
const customName = document.getElementById('camera-name-input').value;
|
|
450
|
+
const name = customName || (selectedCam ? selectedCam.name : 'New Camera');
|
|
435
451
|
const isEntry = document.getElementById('camera-entry-checkbox').checked;
|
|
436
452
|
const isExit = document.getElementById('camera-exit-checkbox').checked;
|
|
437
453
|
// Use pending position from click, or default to center
|
|
438
454
|
const pos = topology._pendingCameraPos || { x: canvas.width / 2 + Math.random() * 100 - 50, y: canvas.height / 2 + Math.random() * 100 - 50 };
|
|
439
455
|
delete topology._pendingCameraPos;
|
|
440
456
|
const camera = {
|
|
441
|
-
deviceId: deviceId
|
|
457
|
+
deviceId: deviceId,
|
|
442
458
|
nativeId: 'cam-' + Date.now(),
|
|
443
459
|
name,
|
|
444
460
|
isEntryPoint: isEntry,
|
|
@@ -448,6 +464,11 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
448
464
|
};
|
|
449
465
|
topology.cameras.push(camera);
|
|
450
466
|
closeModal('add-camera-modal');
|
|
467
|
+
// Clear form
|
|
468
|
+
document.getElementById('camera-name-input').value = '';
|
|
469
|
+
document.getElementById('camera-entry-checkbox').checked = false;
|
|
470
|
+
document.getElementById('camera-exit-checkbox').checked = false;
|
|
471
|
+
updateCameraSelects();
|
|
451
472
|
updateUI();
|
|
452
473
|
render();
|
|
453
474
|
}
|
|
@@ -459,6 +480,22 @@ export const EDITOR_HTML = `<!DOCTYPE html>
|
|
|
459
480
|
}
|
|
460
481
|
|
|
461
482
|
function updateCameraSelects() {
|
|
483
|
+
// Update camera device select (for adding new cameras)
|
|
484
|
+
const cameraDeviceSelect = document.getElementById('camera-device-select');
|
|
485
|
+
if (availableCameras.length > 0) {
|
|
486
|
+
const existingIds = topology.cameras.map(c => c.deviceId);
|
|
487
|
+
const available = availableCameras.filter(c => !existingIds.includes(c.id));
|
|
488
|
+
if (available.length > 0) {
|
|
489
|
+
cameraDeviceSelect.innerHTML = '<option value="">Select a camera...</option>' +
|
|
490
|
+
available.map(c => '<option value="' + c.id + '">' + c.name + '</option>').join('');
|
|
491
|
+
} else {
|
|
492
|
+
cameraDeviceSelect.innerHTML = '<option value="">All cameras already added</option>';
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
cameraDeviceSelect.innerHTML = '<option value="">No cameras with object detection found</option>';
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Update connection selects (for existing topology cameras)
|
|
462
499
|
const options = topology.cameras.map(c => '<option value="' + c.deviceId + '">' + c.name + '</option>').join('');
|
|
463
500
|
document.getElementById('connection-from-select').innerHTML = options;
|
|
464
501
|
document.getElementById('connection-to-select').innerHTML = options;
|