Add camera selection feature to dashboard
- Introduced a dropdown for selecting cameras in the dashboard. - Implemented functions to load available cameras and handle camera selection. - Updated polling logic to reflect the currently selected camera. - Enhanced styling for the camera selection dropdown.main
parent
215f5591ea
commit
5fdb9bc29f
|
|
@ -78,6 +78,15 @@
|
||||||
@keyframes pulse { 0%,100%{ opacity:1 } 50%{ opacity:.35 } }
|
@keyframes pulse { 0%,100%{ opacity:1 } 50%{ opacity:.35 } }
|
||||||
|
|
||||||
.clock { font-size: .85rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }
|
.clock { font-size: .85rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }
|
||||||
|
.camera-select {
|
||||||
|
background: var(--surface-2);
|
||||||
|
color: var(--text);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: .85rem;
|
||||||
|
min-width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
/* -------- Layout -------- */
|
/* -------- Layout -------- */
|
||||||
.container {
|
.container {
|
||||||
|
|
@ -292,6 +301,7 @@
|
||||||
Surveillance Dashboard
|
Surveillance Dashboard
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
|
<select id="cameraSelect" class="camera-select"></select>
|
||||||
<div class="status-badge connecting" id="statusBadge">
|
<div class="status-badge connecting" id="statusBadge">
|
||||||
<span class="dot"></span>
|
<span class="dot"></span>
|
||||||
<span id="statusText">Connecting</span>
|
<span id="statusText">Connecting</span>
|
||||||
|
|
@ -368,6 +378,33 @@ updateClock();
|
||||||
|
|
||||||
let prevAlertActive = false;
|
let prevAlertActive = false;
|
||||||
let bannerTimeout = null;
|
let bannerTimeout = null;
|
||||||
|
let camerasLoaded = false;
|
||||||
|
|
||||||
|
async function loadCameras() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/cameras');
|
||||||
|
const data = await res.json();
|
||||||
|
const select = document.getElementById('cameraSelect');
|
||||||
|
select.innerHTML = data.cameras
|
||||||
|
.map(c => `<option value="${c.id}">${c.name} (${c.ip})</option>`)
|
||||||
|
.join('');
|
||||||
|
camerasLoaded = true;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load cameras', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectCamera(cameraId) {
|
||||||
|
try {
|
||||||
|
await fetch('/api/camera/select', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ camera_id: cameraId })
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to switch camera', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function poll() {
|
async function poll() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -387,6 +424,16 @@ async function poll() {
|
||||||
badge.className = 'status-badge ' + d.stream_status;
|
badge.className = 'status-badge ' + d.stream_status;
|
||||||
stext.textContent = d.stream_status === 'live' ? 'Live' :
|
stext.textContent = d.stream_status === 'live' ? 'Live' :
|
||||||
d.stream_status === 'error' ? 'Error' : 'Connecting';
|
d.stream_status === 'error' ? 'Error' : 'Connecting';
|
||||||
|
if (d.active_camera_name) {
|
||||||
|
stext.textContent = `${stext.textContent} - ${d.active_camera_name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (camerasLoaded) {
|
||||||
|
const cameraSelect = document.getElementById('cameraSelect');
|
||||||
|
if (cameraSelect.value !== d.selected_camera_id) {
|
||||||
|
cameraSelect.value = d.selected_camera_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Alert KPI glow
|
// Alert KPI glow
|
||||||
const kpiAlerts = document.getElementById('kpiAlerts');
|
const kpiAlerts = document.getElementById('kpiAlerts');
|
||||||
|
|
@ -446,6 +493,10 @@ function openLightbox(src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(poll, 1000);
|
setInterval(poll, 1000);
|
||||||
|
document.getElementById('cameraSelect').addEventListener('change', (e) => {
|
||||||
|
selectCamera(e.target.value);
|
||||||
|
});
|
||||||
|
loadCameras().then(poll);
|
||||||
poll();
|
poll();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue