openvibers/openvibe.bot Commits

LOOI battery: a fresh poll is not provisional; unverified means a read at connect or a stale value (#428)

OP OpenVibers committed 9 h ago · b556201a41 · 2 files, +21 −15

Maticus's P7 list (11-12): on a fresh poll the tray sends battery 0-100 and sensors.p7_battery = { pct, usb, raw, at } with no unverified; unverified comes only with a read at connect or a stale value. The panel keyed "provisional" on source 'poll', which the tray no longer sends. A reading without unverified now reads "updated 7s ago". An unverified one reads "provisional, read at connect" (a minute old or less, or source connect), else "provisional, from 3m ago". Tests: a fresh poll, a stale value, a read at connect with and without a time.

Changes

ddiff --git a/public/panel.js b/public/panel.js
iindex 5ef4a30..df9185d 100644
---- a/public/panel.js
++++ b/public/panel.js
@@@ -1443,8 +1443,10 @@
pill.dataset.state = dataState;
}
// Battery | Latency. A missing reading is a dash, an empty bar and the word unknown, never 0 %; a voltage is
-- // never invented. The tray's reading says how fresh it is: "read at connect" for an unverified FED8 read, or
-- // "updated 12s ago" from its `at`. No `at` and not unverified: no note.
++ // never invented. The tray's reading says how fresh it is (Maticus's P7 list, 2026-11-12): a fresh poll is
++ // { pct, usb, raw, at } with no `unverified`, shown as "updated 12s ago"; `unverified` comes only with a read at
++ // connect or a stale value, shown as "provisional" with its age ("provisional, from 3m ago", or "provisional, read
++ // at connect" when the tray says so). No `at` and not unverified: no note.
function batteryAge(at) {
const then = typeof at === 'number' ? at : Date.parse(at);
if (!Number.isFinite(then)) return '';
@@@ -1474,10 +1476,12 @@
const p7 = sensors && sensors.p7_battery && typeof sensors.p7_battery === 'object' ? sensors.p7_battery : null;
let freshness = '';
if (fraction != null && p7) {
-- // The tray polls it now (source 'poll'): still provisional, but as fresh as its `at` (Maticus, 2026-11-12).
-- if (p7.source === 'poll' && p7.at != null) { const age = batteryAge(p7.at); freshness = age ? `provisional, updated ${age} ago` : 'provisional'; }
-- else if (p7.unverified === true) freshness = 'read at connect';
-- else if (p7.at != null) {
++ if (p7.unverified === true) {
++ // A minute old or less is the read at connect; older is a value gone stale.
++ const then = p7.at == null ? NaN : (typeof p7.at === 'number' ? p7.at : Date.parse(p7.at));
++ const stale = Number.isFinite(then) && Date.now() - then > 60000 && p7.source !== 'connect';
++ freshness = stale ? `provisional, from ${batteryAge(p7.at)} ago` : 'provisional, read at connect';
++ } else if (p7.at != null) {
const age = batteryAge(p7.at);
if (age) freshness = `updated ${age} ago`;
}
ddiff --git a/test/looi-panel-notes.test.js b/test/looi-panel-notes.test.js
iindex c67a6c7..2549122 100644
---- a/test/looi-panel-notes.test.js
++++ b/test/looi-panel-notes.test.js
@@@ -84,12 +84,12 @@ function panelPage() {
}
(async () => {
-- await check('the battery row says read at connect, updated Ns/min ago, or nothing; a missing reading is a dash and unknown', () => {
++ await check('the battery row says provisional at connect, updated Ns/min ago, or nothing; a missing reading is a dash and unknown', () => {
const page = panelPage();
page.state({ battery: 0.42, telemetry: { sensors: { p7_battery: { pct: 42, unverified: true, at: iso(NOW - 500) } } } });
assert.strictEqual(page.batteryText.textContent, '42 %');
assert.strictEqual(page.batteryNote.hidden, true);
-- assert.strictEqual(page.batteryFreshness.textContent, 'read at connect');
++ assert.strictEqual(page.batteryFreshness.textContent, 'provisional, read at connect');
assert.strictEqual(page.batteryFreshness.hidden, false);
page.state({ battery: 0.42, telemetry: { sensors: { p7_battery: { pct: 42, at: iso(NOW - 12300) } } } });
assert.strictEqual(page.batteryFreshness.textContent, 'updated 12s ago');
@@@ -106,14 +106,16 @@ function panelPage() {
assert.strictEqual(page.batteryFreshness.hidden, true, 'a missing reading has no freshness note');
});
-- await check('a polled reading says provisional, updated Ns ago (source poll, Maticus 11-12)', () => {
++ await check('a fresh poll is not provisional; unverified (at connect or stale) is (Maticus\'s P7 list, 11-12)', () => {
const page = panelPage();
-- page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, usb: false, raw: 153, unverified: true, source: 'poll', at: iso(NOW - 7400) } } } });
-- assert.strictEqual(page.batteryFreshness.textContent, 'provisional, updated 7s ago');
-- page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, unverified: true, source: 'poll', at: NOW - 180000 } } } });
-- assert.strictEqual(page.batteryFreshness.textContent, 'provisional, updated 3m ago');
-- page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, unverified: true, at: iso(NOW - 500) } } } });
-- assert.strictEqual(page.batteryFreshness.textContent, 'read at connect', 'a read at connect still says so');
++ page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, usb: false, raw: 153, at: NOW - 7400 } } } });
++ assert.strictEqual(page.batteryFreshness.textContent, 'updated 7s ago', 'a fresh poll: { pct, usb, raw, at }, no unverified');
++ page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, unverified: true, at: NOW - 180000 } } } });
++ assert.strictEqual(page.batteryFreshness.textContent, 'provisional, from 3m ago', 'a stale value');
++ page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, unverified: true, source: 'connect', at: iso(NOW - 500) } } } });
++ assert.strictEqual(page.batteryFreshness.textContent, 'provisional, read at connect');
++ page.state({ battery: 0.6, telemetry: { sensors: { p7_battery: { pct: 60, unverified: true } } } });
++ assert.strictEqual(page.batteryFreshness.textContent, 'provisional, read at connect', 'unverified with no time: a read at connect');
});
await check('the head slider names its mounting: Portrait and its range, or Landscape; nothing without p7_head', () => {