housekeeping: MIT license, region note, refresh-state restore

- LICENSE: MIT (closes #24). Chosen so forks can iterate freely; upstream
  research credits in README should be preserved.
- README: adds a short "Region" callout noting Flock is US-first, so
  users outside North America know why the OUI list may not match
  anything locally (closes #6).
- Dashboard: fix state restoration on page refresh (closes #46).
  loadStatus() now injects the connected port back into the dropdown,
  locks the port controls, hides the refresh button, and for gpsd
  repopulates the host:port inputs from the reported endpoint.
  updateFlockStatus/updateGpsStatus centralize the connected/idle
  toggles (dot + buttons + control lock + extra controls) so all
  entry points converge.
This commit is contained in:
Colonel Panic
2026-07-29 11:50:17 -04:00
parent 75c269ebf6
commit 48d3f1fa9c
3 changed files with 115 additions and 21 deletions
+86 -21
View File
@@ -1889,24 +1889,62 @@
});
}
// Inject a value into a <select> as a fresh option if it isn't
// already present, then select it. Used on page-load status
// restoration so a connected port shows up in the dropdown even
// when the port list refresh hasn't run yet (or the device has
// dropped off `list_ports`).
function ensureSelectHasValue(selectEl, value, labelFallback) {
if (!selectEl || !value) return;
const has = Array.from(selectEl.options).some(o => o.value === value);
if (!has) {
const opt = document.createElement('option');
opt.value = value;
opt.textContent = labelFallback || value;
selectEl.appendChild(opt);
}
selectEl.value = value;
}
// Freeze or unfreeze the Sniffer / GPS port controls so they can't
// be edited while a device is live. Also hides the refresh button
// during connection to keep the auto-refresh loop from clobbering
// the shown value.
function setFlockControlsLocked(locked) {
const sel = document.getElementById('flockDeviceSelect');
const refresh = document.getElementById('refreshFlockPortsBtn');
if (sel) sel.disabled = locked;
if (refresh) refresh.style.display = locked ? 'none' : '';
}
function setGpsControlsLocked(locked) {
const source = document.getElementById('gpsSourceSelect');
const portSel = document.getElementById('gpsPortSelect');
const refresh = document.getElementById('refreshGpsPortsBtn');
const host = document.getElementById('gpsdHostInput');
const port = document.getElementById('gpsdPortInput');
if (source) source.disabled = locked;
if (portSel) portSel.disabled = locked;
if (host) host.disabled = locked;
if (port) port.disabled = locked;
if (refresh) refresh.style.display = locked ? 'none' : '';
}
function updateFlockStatus(connected) {
const indicator = document.getElementById('flockStatus');
if (connected) {
indicator.classList.add('active');
} else {
indicator.classList.remove('active');
}
if (indicator) indicator.classList.toggle('active', !!connected);
document.getElementById('connectFlockBtn').style.display = connected ? 'none' : 'inline-block';
document.getElementById('disconnectFlockBtn').style.display = connected ? 'inline-block' : 'none';
setFlockControlsLocked(!!connected);
setFlockExtraControls(!!connected);
}
function updateGpsStatus(connected) {
const indicator = document.getElementById('gpsStatus');
if (connected) {
indicator.classList.add('active');
} else {
indicator.classList.remove('active');
}
if (indicator) indicator.classList.toggle('active', !!connected);
document.getElementById('connectGpsBtn').style.display = connected ? 'none' : 'inline-block';
document.getElementById('disconnectGpsBtn').style.display = connected ? 'inline-block' : 'none';
setGpsControlsLocked(!!connected);
}
function loadStatus() {
@@ -1914,18 +1952,45 @@
.then(response => response.json())
.then(data => {
console.log('Status loaded:', data);
// Sniffer: reflect the actual connected port in the
// dropdown so the user can see what's connected after
// a refresh, and lock the controls.
if (data.flock_connected && data.flock_port) {
ensureSelectHasValue(
document.getElementById('flockDeviceSelect'),
data.flock_port,
data.flock_port + ' - connected'
);
}
updateFlockStatus(data.flock_connected);
updateGpsStatus(data.gps_connected);
if (data.flock_connected) {
document.getElementById('connectFlockBtn').style.display = 'none';
document.getElementById('disconnectFlockBtn').style.display = 'inline-block';
}
// GPS: source-aware restore. For serial, pin the
// connected serial port; for gpsd, populate host/port
// inputs from the reported endpoint.
if (data.gps_connected) {
document.getElementById('connectGpsBtn').style.display = 'none';
document.getElementById('disconnectGpsBtn').style.display = 'inline-block';
const source = data.gps_source || 'serial';
const srcSel = document.getElementById('gpsSourceSelect');
if (srcSel) {
srcSel.value = source;
if (typeof applyGpsSource === 'function') applyGpsSource(source);
}
if (source === 'serial' && data.gps_port) {
ensureSelectHasValue(
document.getElementById('gpsPortSelect'),
data.gps_port,
data.gps_port + ' - connected'
);
} else if (source === 'gpsd' && data.gpsd_endpoint) {
const [host, port] = String(data.gpsd_endpoint).split(':');
const hostEl = document.getElementById('gpsdHostInput');
const portEl = document.getElementById('gpsdPortInput');
if (hostEl && host) hostEl.value = host;
if (portEl && port) portEl.value = port;
}
gpsConnected = true;
}
updateGpsStatus(data.gps_connected);
})
.catch(error => {
console.error('Error loading status:', error);