v3.25.6: the PLI dictionary save feedback + the timer wording

The v3.25.5 report's follow-ups: a field the server could not use was
silently dropped, and the PWA also filtered non-hex fields client-side
(never sending them).  The POST now collects them and names them in the
response - `invalid` (a malformed key/value: not hex, an odd number of
digits, a non-string value) and `unknown` (a well-formed code outside the
qualifier list), both omitted when everything was applied - and the PWA's
Save sends every field, composes "Not saved: 0A (invalid value), 18
(unknown qualifier)" (`pysimPliRejectMsg`) into a new #pli-save-msg line
under the button and shows "Not saved" briefly.

The guide's timer exception now says "card-initiated automatic action"
(the timer retries every 5 s; polling/menu are suspended, not retrying).

Docs: api.md's POST contract, help EN/RU - which also said "22 PLI
qualifiers" while the list has 25.  Tests: the rejection lists per case
(odd length, non-hex value/key, non-string, unknown code) and the
valid-save absence, the PWA message helper and the wiring pins.
887 frontend + 875 Python; sw cache simple-v430.
This commit is contained in:
2026-10-11 16:51:30 +03:00
parent f2046d7f0a
commit 9fd8238051
10 changed files with 152 additions and 36 deletions
+31 -15
View File
@@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
VERSION = '3.25.5'
VERSION = '3.25.6'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -9103,25 +9103,41 @@ class PysimHandler(BaseHTTPRequestHandler):
elif self.path == '/api/pli-dict':
body = self._read_body()
self._log_req(body)
invalid, unknown = [], []
if isinstance(body, dict):
for k, v in body.items():
if isinstance(v, str):
if not isinstance(v, str):
invalid.append(k)
continue
try:
code = int(k, 16)
except ValueError:
invalid.append(k)
continue
# a known qualifier - never "an override already exists":
# a new override must stick, and a clear must not poison a
# later set (v3.25.5, live report)
if code not in PLI_QUALIFIER_NAMES:
unknown.append(k)
continue
if v:
try:
code = int(k, 16)
# a known qualifier - never "an override already
# exists": a new override must stick, and a clear
# must not poison a later set (v3.25.5, live report)
if code in PLI_QUALIFIER_NAMES:
if v:
bytes.fromhex(v) # valid hex only
_PLI_DATA[code] = v
else:
# clearing the field restores the default
_PLI_DATA.pop(code, None)
except (ValueError, KeyError):
pass
bytes.fromhex(v) # valid hex only
except ValueError:
invalid.append(k)
continue
_PLI_DATA[code] = v
else:
# clearing the field restores the default
_PLI_DATA.pop(code, None)
resp = {('%02X' % q): (_PLI_DATA.get(q) or PLI_DEFAULTS.get(q, ''))
for q in PLI_QUALIFIER_NAMES}
# a field the server could not use is named in the response, never
# silently dropped (v3.25.6): the PWA shows it under the Save button
if invalid:
resp['invalid'] = invalid
if unknown:
resp['unknown'] = unknown
self._send_json(resp)
self._log_resp(resp)
elif self.path == '/api/poll-toggle':