feat: polling on by default and an effective-state toggle (v3.5.10)

- Background STATUS polling is enabled by default, per the spec's idle
  polling rule (TS 102 221 14.6.2): `_POLL_ENABLED = True`, __main__ runs
  `_poll_enable()` regardless of card presence (--poll-interval 0 still
  disables) and `_do_status_poll` keeps ticking while enabled even without
  a session, so a cardless start resumes as soon as a card appears.
- A new card session clears a POLLING OFF from the previous card in
  `_apply_equipped_card` before re-enabling polling.
- The Phone-tab toggle now shows the *effective* state: OFF in amber
  ("card disabled polling (POLLING OFF)") while the card suspended
  proactive polling, back to ON on the next POLL INTERVAL.  The 5 s
  background poll now passes `card_disabled` through (it was dropped, so
  an autonomously received POLLING OFF never reached the UI).
- Tests: poll_ui updated (button OFF while suspended, warning path),
  test_poll +2 (cardless ticking, module default via a subprocess check).
- Help EN/RU, docs/api.md, AGENTS; version 3.5.10; sw cache simple-v263.

573 frontend / 441 Python green.
This commit is contained in:
2026-09-26 13:09:44 +03:00
parent 25f001a778
commit 4c3fa64c95
10 changed files with 70 additions and 26 deletions
+10 -5
View File
@@ -1560,7 +1560,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
const SIMPLE_VERSION = '3.5.9';
const SIMPLE_VERSION = '3.5.10';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -9986,7 +9986,12 @@ function pysimUpdatePollUI(enabled, interval, cardDisabled, warning) {
const btn = document.getElementById('pli-pause-btn');
const stat = document.getElementById('pli-poll-stat');
if (!btn) return;
if (enabled) {
// The button shows whether STATUS is actually being polled: while the card
// has disabled proactive polling (POLLING OFF, TS 102 223 6.4.14) it shows
// OFF even though the operator's switch is still on - polling resumes
// automatically on the next POLL INTERVAL.
const effective = !!enabled && !cardDisabled;
if (effective) {
btn.textContent = 'ON';
btn.classList.remove('bg-gray-200', 'dark:bg-slate-700', 'text-gray-700', 'dark:text-slate-300');
btn.classList.add('bg-emerald-600', 'text-white');
@@ -9997,9 +10002,9 @@ function pysimUpdatePollUI(enabled, interval, cardDisabled, warning) {
}
if (stat) {
stat.className = 'text-xs ' + (cardDisabled ? 'text-amber-600 dark:text-amber-400' : 'text-gray-400');
if (cardDisabled) stat.textContent = t('card disabled polling (POLLING OFF)');
if (cardDisabled) stat.textContent = warning || t('card disabled polling (POLLING OFF)');
else if (warning) stat.textContent = warning;
else stat.textContent = enabled ? '(' + interval + 's)' : '';
else stat.textContent = effective ? '(' + interval + 's)' : '';
}
}
@@ -10086,7 +10091,7 @@ function pysimStartBackendPoll() {
pysimFetch('/api/stk-status'),
pysimFetch('/api/poll-status'),
]);
pysimUpdatePollUI(ps.enabled, ps.interval);
pysimUpdatePollUI(ps.enabled, ps.interval, ps.card_disabled, ps.warning);
if (pysimStkStatusChanged(stk) && isViewVisible('tab-phone')) stkCheckMenu();
} catch (e) { /* ignore */ }
}, 5000);