fix: correct monitoring flag and battery/memory offsets in _decode_monitor_status
section[6] is the monitoring flag (was wrongly section[1] — section[1] is always 0x00 in both states). Battery and memory fields use relative-from-end offsets (section[-11:-9], section[-9:-5], section[-5:-1]) instead of absolute positions, which broke when the payload grew by 3 bytes in monitoring mode. Confirmed from full byte diff of 142 0xE3 frames in 4-8-26/2ndtry capture. SFM start_monitoring now polls /device/monitor/status every 5s for up to 60s instead of a fixed 25s delay (unit runs ~40s on-device sensor check before confirming monitoring state). Also corrects stale 1C→6E response anomaly claim in protocol reference — no exceptions to the 0xFF−SUB rule are known.
This commit is contained in:
+56
-22
@@ -813,13 +813,14 @@ function populateDeviceBar() {
|
||||
|
||||
// ── Monitoring ─────────────────────────────────────────────────────────────────
|
||||
async function refreshMonitorStatus() {
|
||||
if (!devHost()) return;
|
||||
if (!devHost()) return null;
|
||||
try {
|
||||
const r = await fetch(`${api()}/device/monitor/status?${deviceParams()}`);
|
||||
if (!r.ok) return;
|
||||
if (!r.ok) return null;
|
||||
const s = await r.json();
|
||||
updateMonitorPanel(s);
|
||||
} catch (_) {}
|
||||
return s;
|
||||
} catch (_) { return null; }
|
||||
}
|
||||
|
||||
function updateMonitorPanel(s) {
|
||||
@@ -832,24 +833,23 @@ function updateMonitorPanel(s) {
|
||||
const stopB = document.getElementById('mon-stop-btn');
|
||||
|
||||
if (s.is_monitoring) {
|
||||
badge.textContent = 'MONITORING';
|
||||
badge.className = 'mon-status-badge monitoring';
|
||||
panel.className = 'monitoring';
|
||||
startB.disabled = true;
|
||||
stopB.disabled = false;
|
||||
batEl.textContent = '—';
|
||||
memTEl.textContent = '—';
|
||||
memFEl.textContent = '—';
|
||||
badge.textContent = 'MONITORING';
|
||||
badge.className = 'mon-status-badge monitoring';
|
||||
panel.className = 'monitoring';
|
||||
startB.disabled = true;
|
||||
stopB.disabled = false;
|
||||
} else {
|
||||
badge.textContent = 'IDLE';
|
||||
badge.className = 'mon-status-badge idle';
|
||||
panel.className = 'idle';
|
||||
startB.disabled = false;
|
||||
stopB.disabled = true;
|
||||
batEl.textContent = s.battery_v != null ? `${s.battery_v.toFixed(2)} V` : '—';
|
||||
memTEl.textContent = s.memory_total_kb != null ? `${s.memory_total_kb} KB` : '—';
|
||||
memFEl.textContent = s.memory_free_kb != null ? `${s.memory_free_kb} KB` : '—';
|
||||
badge.textContent = 'IDLE';
|
||||
badge.className = 'mon-status-badge idle';
|
||||
panel.className = 'idle';
|
||||
startB.disabled = false;
|
||||
stopB.disabled = true;
|
||||
}
|
||||
// Battery and memory are available in both states — update if present,
|
||||
// keep previous value if this was an optimistic update with no real data.
|
||||
if (s.battery_v != null) batEl.textContent = `${s.battery_v.toFixed(2)} V`;
|
||||
if (s.memory_total_kb != null) memTEl.textContent = `${s.memory_total_kb} KB`;
|
||||
if (s.memory_free_kb != null) memFEl.textContent = `${s.memory_free_kb} KB`;
|
||||
}
|
||||
|
||||
async function startMonitoring() {
|
||||
@@ -860,13 +860,47 @@ async function startMonitoring() {
|
||||
try {
|
||||
const r = await fetch(`${api()}/device/monitor/start?${deviceParams()}`, { method: 'POST' });
|
||||
if (!r.ok) { const e = await r.json().catch(() => ({})); throw new Error(e.detail || r.statusText); }
|
||||
setStatus('Monitoring started.', 'ok');
|
||||
await refreshMonitorStatus();
|
||||
|
||||
// Optimistically show MONITORING immediately. The unit may run a ~40s on-device
|
||||
// sensor check before fully entering monitor mode. We poll status every 5s for
|
||||
// up to 60s, updating the badge when is_monitoring flips to true.
|
||||
updateMonitorPanel({ is_monitoring: true });
|
||||
setStatus('Monitoring started — sensor check in progress (~40s)…', 'loading');
|
||||
btn.textContent = '▶ Start';
|
||||
|
||||
_pollMonitorConfirm(0);
|
||||
|
||||
} catch (e) {
|
||||
setStatus(`Start monitoring failed: ${e.message}`, 'error');
|
||||
btn.disabled = false;
|
||||
btn.textContent = '▶ Start';
|
||||
}
|
||||
}
|
||||
|
||||
async function _pollMonitorConfirm(attempt) {
|
||||
// Poll /device/monitor/status every 5s for up to 60s after startMonitoring().
|
||||
// Updates the panel on each successful poll. Resolves once is_monitoring is true
|
||||
// or after 12 attempts (60s), whichever comes first.
|
||||
const MAX_ATTEMPTS = 12;
|
||||
const INTERVAL_MS = 5000;
|
||||
if (attempt >= MAX_ATTEMPTS) {
|
||||
const s = await refreshMonitorStatus();
|
||||
if (!s || !s.is_monitoring) {
|
||||
setStatus('Warning: unit did not confirm monitoring state after 60s. Check device.', 'error');
|
||||
} else {
|
||||
setStatus('Monitoring active.', 'ok');
|
||||
}
|
||||
return;
|
||||
}
|
||||
await new Promise(res => setTimeout(res, INTERVAL_MS));
|
||||
const s = await refreshMonitorStatus();
|
||||
if (s && s.is_monitoring) {
|
||||
setStatus('Monitoring active.', 'ok');
|
||||
} else {
|
||||
const elapsed = (attempt + 1) * 5;
|
||||
setStatus(`Sensor check in progress… (${elapsed}s elapsed)`, 'loading');
|
||||
_pollMonitorConfirm(attempt + 1);
|
||||
}
|
||||
btn.textContent = '▶ Start';
|
||||
}
|
||||
|
||||
async function stopMonitoring() {
|
||||
|
||||
Reference in New Issue
Block a user