@blueharford/scrypted-spatial-awareness 0.6.1 → 0.6.3
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 +79 -5
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/main.ts +21 -4
- package/src/ui/editor-html.ts +61 -1
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/out/main.nodejs.js
CHANGED
|
@@ -40227,9 +40227,21 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
40227
40227
|
// Calculate position for the landmark WITHIN the camera's field of view
|
|
40228
40228
|
let position = suggestion.landmark.position;
|
|
40229
40229
|
if (!position || (position.x === 0 && position.y === 0)) {
|
|
40230
|
-
//
|
|
40231
|
-
|
|
40232
|
-
|
|
40230
|
+
// Debug logging
|
|
40231
|
+
this.console.log(`[Discovery] Processing landmark "${suggestion.landmark.name}"`);
|
|
40232
|
+
this.console.log(`[Discovery] visibleFromCameras: ${JSON.stringify(suggestion.landmark.visibleFromCameras)}`);
|
|
40233
|
+
this.console.log(`[Discovery] Available cameras: ${topology.cameras.map(c => `${c.name}(${c.deviceId})`).join(', ')}`);
|
|
40234
|
+
// Find a camera that can see this landmark - use flexible matching (deviceId, name, or case-insensitive)
|
|
40235
|
+
const visibleCameraRef = suggestion.landmark.visibleFromCameras?.[0];
|
|
40236
|
+
const camera = visibleCameraRef ? topology.cameras.find(c => c.deviceId === visibleCameraRef ||
|
|
40237
|
+
c.name === visibleCameraRef ||
|
|
40238
|
+
c.name.toLowerCase() === visibleCameraRef.toLowerCase()) : null;
|
|
40239
|
+
if (camera) {
|
|
40240
|
+
this.console.log(`[Discovery] Matched camera: ${camera.name}, position: ${JSON.stringify(camera.floorPlanPosition)}, fov: ${JSON.stringify(camera.fov)}`);
|
|
40241
|
+
}
|
|
40242
|
+
else {
|
|
40243
|
+
this.console.warn(`[Discovery] No camera matched for "${visibleCameraRef}"`);
|
|
40244
|
+
}
|
|
40233
40245
|
if (camera?.floorPlanPosition) {
|
|
40234
40246
|
// Get camera's FOV direction and range (cast to any for flexible access)
|
|
40235
40247
|
const fov = (camera.fov || { mode: 'simple', angle: 90, direction: 0, range: 80 });
|
|
@@ -40237,7 +40249,9 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
|
|
|
40237
40249
|
const range = fov.range || 80;
|
|
40238
40250
|
const fovAngle = fov.angle || 90;
|
|
40239
40251
|
// Count existing landmarks from this camera to spread them out
|
|
40240
|
-
const
|
|
40252
|
+
const cameraDeviceId = camera.deviceId;
|
|
40253
|
+
const existingFromCamera = (topology.landmarks || []).filter(l => l.visibleFromCameras?.includes(cameraDeviceId) ||
|
|
40254
|
+
l.visibleFromCameras?.includes(camera.name)).length;
|
|
40241
40255
|
// Calculate position in front of camera within its FOV
|
|
40242
40256
|
// Convert direction to radians (0 = up/north, 90 = right/east)
|
|
40243
40257
|
const dirRad = (direction - 90) * Math.PI / 180;
|
|
@@ -41533,6 +41547,19 @@ exports.EDITOR_HTML = `<!DOCTYPE html>
|
|
|
41533
41547
|
<p>Topology Editor</p>
|
|
41534
41548
|
</div>
|
|
41535
41549
|
<div class="sidebar-content">
|
|
41550
|
+
<div class="section" style="background: #1a3a5c; margin: -10px -15px 10px -15px; padding: 15px;">
|
|
41551
|
+
<div class="section-title" style="margin-bottom: 10px;">
|
|
41552
|
+
<span>Floor Plan Scale</span>
|
|
41553
|
+
</div>
|
|
41554
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
41555
|
+
<input type="number" id="scale-input" value="5" min="1" max="50" style="width: 60px; padding: 6px; background: #0f3460; border: 1px solid #1a4a7a; border-radius: 4px; color: #fff;" onchange="updateScale(this.value)">
|
|
41556
|
+
<span style="font-size: 12px; color: #888;">pixels per foot</span>
|
|
41557
|
+
<button class="btn btn-small" onclick="openScaleHelper()" style="margin-left: auto;">Help</button>
|
|
41558
|
+
</div>
|
|
41559
|
+
<div style="font-size: 11px; color: #666; margin-top: 8px;">
|
|
41560
|
+
Tip: If your floor plan is 800px wide and represents 80ft, scale = 10 px/ft
|
|
41561
|
+
</div>
|
|
41562
|
+
</div>
|
|
41536
41563
|
<div class="section">
|
|
41537
41564
|
<div class="section-title">
|
|
41538
41565
|
<span>Cameras</span>
|
|
@@ -41841,6 +41868,14 @@ exports.EDITOR_HTML = `<!DOCTYPE html>
|
|
|
41841
41868
|
let currentDrawing = null;
|
|
41842
41869
|
let blankCanvasMode = false;
|
|
41843
41870
|
|
|
41871
|
+
// Floor plan scale: pixels per foot (default assumes ~5 pixels per foot for a typical floor plan)
|
|
41872
|
+
// User can adjust this by setting the scale
|
|
41873
|
+
let floorPlanScale = 5; // pixels per foot
|
|
41874
|
+
|
|
41875
|
+
// Helper functions for scale conversion
|
|
41876
|
+
function feetToPixels(feet) { return feet * floorPlanScale; }
|
|
41877
|
+
function pixelsToFeet(pixels) { return pixels / floorPlanScale; }
|
|
41878
|
+
|
|
41844
41879
|
// Zone drawing state
|
|
41845
41880
|
let zoneDrawingMode = false;
|
|
41846
41881
|
let currentZonePoints = [];
|
|
@@ -41896,6 +41931,12 @@ exports.EDITOR_HTML = `<!DOCTYPE html>
|
|
|
41896
41931
|
if (response.ok) {
|
|
41897
41932
|
topology = await response.json();
|
|
41898
41933
|
if (!topology.drawings) topology.drawings = [];
|
|
41934
|
+
// Load floor plan scale if saved
|
|
41935
|
+
if (topology.floorPlanScale) {
|
|
41936
|
+
floorPlanScale = topology.floorPlanScale;
|
|
41937
|
+
const scaleInput = document.getElementById('scale-input');
|
|
41938
|
+
if (scaleInput) scaleInput.value = floorPlanScale;
|
|
41939
|
+
}
|
|
41899
41940
|
// Load floor plan from separate storage (handles legacy imageData in topology too)
|
|
41900
41941
|
if (topology.floorPlan?.imageData) {
|
|
41901
41942
|
// Legacy: imageData was stored in topology
|
|
@@ -43065,6 +43106,8 @@ exports.EDITOR_HTML = `<!DOCTYPE html>
|
|
|
43065
43106
|
function showCameraProperties(camera) {
|
|
43066
43107
|
const panel = document.getElementById('properties-panel');
|
|
43067
43108
|
const fov = camera.fov || { mode: 'simple', angle: 90, direction: 0, range: 80 };
|
|
43109
|
+
// Convert stored pixel range to feet for display
|
|
43110
|
+
const rangeInFeet = Math.round(pixelsToFeet(fov.range || 80));
|
|
43068
43111
|
panel.innerHTML = '<h3>Camera Properties</h3>' +
|
|
43069
43112
|
'<div class="form-group"><label>Name</label><input type="text" value="' + camera.name + '" onchange="updateCameraName(\\'' + camera.deviceId + '\\', this.value)"></div>' +
|
|
43070
43113
|
'<div class="form-group"><label class="checkbox-group"><input type="checkbox" ' + (camera.isEntryPoint ? 'checked' : '') + ' onchange="updateCameraEntry(\\'' + camera.deviceId + '\\', this.checked)">Entry Point</label></div>' +
|
|
@@ -43072,7 +43115,8 @@ exports.EDITOR_HTML = `<!DOCTYPE html>
|
|
|
43072
43115
|
'<h4 style="margin-top: 15px; margin-bottom: 10px; color: #888;">Field of View</h4>' +
|
|
43073
43116
|
'<div class="form-group"><label>Direction (0=up, 90=right)</label><input type="number" value="' + Math.round(fov.direction || 0) + '" min="0" max="359" onchange="updateCameraFov(\\'' + camera.deviceId + '\\', \\'direction\\', this.value)"></div>' +
|
|
43074
43117
|
'<div class="form-group"><label>FOV Angle (degrees)</label><input type="number" value="' + (fov.angle || 90) + '" min="30" max="180" onchange="updateCameraFov(\\'' + camera.deviceId + '\\', \\'angle\\', this.value)"></div>' +
|
|
43075
|
-
'<div class="form-group"><label>Range (
|
|
43118
|
+
'<div class="form-group"><label>Range (feet)</label><input type="number" value="' + rangeInFeet + '" min="5" max="200" onchange="updateCameraFovRange(\\'' + camera.deviceId + '\\', this.value)"></div>' +
|
|
43119
|
+
'<div style="font-size: 11px; color: #666; margin-top: -10px; margin-bottom: 15px;">~' + (fov.range || 80) + ' pixels at current scale</div>' +
|
|
43076
43120
|
'<div class="form-group"><button class="btn" style="width: 100%; background: #f44336;" onclick="deleteCamera(\\'' + camera.deviceId + '\\')">Delete Camera</button></div>';
|
|
43077
43121
|
}
|
|
43078
43122
|
|
|
@@ -43091,6 +43135,36 @@ exports.EDITOR_HTML = `<!DOCTYPE html>
|
|
|
43091
43135
|
camera.fov[field] = parseFloat(value);
|
|
43092
43136
|
render();
|
|
43093
43137
|
}
|
|
43138
|
+
function updateCameraFovRange(id, feetValue) {
|
|
43139
|
+
// Convert feet to pixels and store
|
|
43140
|
+
const camera = topology.cameras.find(c => c.deviceId === id);
|
|
43141
|
+
if (!camera) return;
|
|
43142
|
+
if (!camera.fov) camera.fov = { mode: 'simple', angle: 90, direction: 0, range: 80 };
|
|
43143
|
+
camera.fov.range = feetToPixels(parseFloat(feetValue));
|
|
43144
|
+
render();
|
|
43145
|
+
// Update the pixel display
|
|
43146
|
+
showCameraProperties(camera);
|
|
43147
|
+
}
|
|
43148
|
+
function updateScale(value) {
|
|
43149
|
+
floorPlanScale = parseFloat(value) || 5;
|
|
43150
|
+
// Store in topology for persistence
|
|
43151
|
+
topology.floorPlanScale = floorPlanScale;
|
|
43152
|
+
render();
|
|
43153
|
+
setStatus('Scale updated: ' + floorPlanScale + ' pixels per foot', 'success');
|
|
43154
|
+
}
|
|
43155
|
+
function openScaleHelper() {
|
|
43156
|
+
alert('How to determine your floor plan scale:\\n\\n' +
|
|
43157
|
+
'1. Measure a known distance on your floor plan in pixels\\n' +
|
|
43158
|
+
' (e.g., measure a room that you know is 20 feet wide)\\n\\n' +
|
|
43159
|
+
'2. Divide the pixel width by the real width in feet\\n' +
|
|
43160
|
+
' Example: 200 pixels / 20 feet = 10 pixels per foot\\n\\n' +
|
|
43161
|
+
'3. Enter that value in the scale field\\n\\n' +
|
|
43162
|
+
'Common scales:\\n' +
|
|
43163
|
+
'- Small floor plan (fits on screen): 3-5 px/ft\\n' +
|
|
43164
|
+
'- Medium floor plan: 5-10 px/ft\\n' +
|
|
43165
|
+
'- Large/detailed floor plan: 10-20 px/ft\\n\\n' +
|
|
43166
|
+
'Tip: Most outdoor cameras see 30-50 feet, indoor 15-30 feet');
|
|
43167
|
+
}
|
|
43094
43168
|
function updateConnectionName(id, value) { const conn = topology.connections.find(c => c.id === id); if (conn) conn.name = value; updateUI(); }
|
|
43095
43169
|
function updateTransitTime(id, field, value) { const conn = topology.connections.find(c => c.id === id); if (conn) conn.transitTime[field] = parseInt(value) * 1000; }
|
|
43096
43170
|
function updateConnectionBidi(id, value) { const conn = topology.connections.find(c => c.id === id); if (conn) conn.bidirectional = value; render(); }
|