ui: name REFRESH and the rest of the TS 102 223 command types (v2.7.17)

The proactive command log showed 'Cmd 0x01' for REFRESH: 0x01 was missing
from both CMD_NAMES (frontend) and PROACTIVE_TYPE_NAMES (server), so the
log fell back to the generic placeholder.  Both tables now carry the full
TS 102 223 9.4 command type list (REFRESH, MORE TIME, POLLING OFF, SET UP
CALL, SEND SS/USSD/DTMF, GEOGRAPHICAL LOCATION REQUEST, SET UP IDLE MODE
TEXT, PERFORM CARD APDU, POWER ON/OFF CARD, GET READER STATUS, RUN AT
COMMAND, LANGUAGE NOTIFICATION, SERVICE SEARCH/INFORMATION, DECLARE
SERVICE, frames, multimedia, COMMAND CONTAINER, ...).

- tests: event_forms CMD_NAMES cases; new tests/test_proactive_names.py
  (REFRESH + spec spot checks).
This commit is contained in:
2026-09-21 00:24:46 +03:00
parent ef3726441b
commit 87052212b7
6 changed files with 83 additions and 17 deletions
+20 -9
View File
@@ -1413,7 +1413,7 @@
// ===== Version ===== // ===== Version =====
// Single source of truth for the PWA version: shown in the header and used // Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect(). // by the server version check in pysimConnect().
const SIMPLE_VERSION = '2.7.16'; const SIMPLE_VERSION = '2.7.17';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching ===== // ===== Tab switching =====
@@ -8303,15 +8303,26 @@ function pysimProactiveSeqChanged(seq) {
_pysimProactiveSeq = seq; _pysimProactiveSeq = seq;
return true; return true;
} }
// TS 102 223 9.4 command types (values equal the proactive command template
// tags); the log falls back to 'Cmd 0xNN' for anything not listed.
const CMD_NAMES = { const CMD_NAMES = {
'03': 'POLL INTERVAL', '05': 'SET UP EVENT LIST', '01': 'REFRESH', '02': 'MORE TIME', '03': 'POLL INTERVAL', '04': 'POLLING OFF',
'13': 'SEND SHORT MESSAGE', '20': 'PLAY TONE', '05': 'SET UP EVENT LIST', '10': 'SET UP CALL', '11': 'SEND SS', '12': 'SEND USSD',
'21': 'DISPLAY TEXT', '22': 'GET INKEY', '23': 'GET INPUT', '13': 'SEND SHORT MESSAGE', '14': 'SEND DTMF', '15': 'LAUNCH BROWSER',
'24': 'SELECT ITEM', '25': 'SET UP MENU', '16': 'GEOGRAPHICAL LOCATION REQUEST',
'26': 'PROVIDE LOCAL INFORMATION', '27': 'TIMER MANAGEMENT', '20': 'PLAY TONE', '21': 'DISPLAY TEXT', '22': 'GET INKEY', '23': 'GET INPUT',
'15': 'LAUNCH BROWSER', '70': 'ACTIVATE', '24': 'SELECT ITEM', '25': 'SET UP MENU', '26': 'PROVIDE LOCAL INFORMATION',
'40': 'OPEN CHANNEL', '41': 'CLOSE CHANNEL', '27': 'TIMER MANAGEMENT', '28': 'SET UP IDLE MODE TEXT',
'42': 'RECEIVE DATA', '43': 'SEND DATA', '44': 'GET CHANNEL STATUS', '30': 'PERFORM CARD APDU', '31': 'POWER ON CARD', '32': 'POWER OFF CARD',
'33': 'GET READER STATUS', '34': 'RUN AT COMMAND', '35': 'LANGUAGE NOTIFICATION',
'40': 'OPEN CHANNEL', '41': 'CLOSE CHANNEL', '42': 'RECEIVE DATA', '43': 'SEND DATA',
'44': 'GET CHANNEL STATUS', '45': 'SERVICE SEARCH', '46': 'GET SERVICE INFORMATION',
'47': 'DECLARE SERVICE', '50': 'SET FRAMES', '51': 'GET FRAMES STATUS',
'60': 'RETRIEVE MULTIMEDIA MESSAGE', '61': 'SUBMIT MULTIMEDIA MESSAGE',
'62': 'DISPLAY MULTIMEDIA MESSAGE', '70': 'ACTIVATE',
'71': 'CONTACTLESS STATE CHANGED', '72': 'COMMAND CONTAINER',
'73': 'ENCAPSULATED SESSION CONTROL', '79': 'LSI COMMAND',
'81': 'End of the proactive UICC session',
}; };
const CMD_QUALIFIER_SHORT = { const CMD_QUALIFIER_SHORT = {
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v219'; const CACHE = 'simple-v220';
const URLS = [ const URLS = [
'index.html', 'index.html',
'help.html', 'help.html',
+3
View File
@@ -35,7 +35,10 @@ eval(extractBlock('const REJECTION_CAUSES = [', 'const EVENT_FORMS = {').replace
eval(extractBlock('const EVENT_FORMS = {', 'const PLI_QUALIFIERS = [').replace(/^const /gm, 'var ')); eval(extractBlock('const EVENT_FORMS = {', 'const PLI_QUALIFIERS = [').replace(/^const /gm, 'var '));
test('CMD_NAMES decodes timer management and the BIP commands', () => { test('CMD_NAMES decodes timer management and the BIP commands', () => {
assert.strictEqual(CMD_NAMES['01'], 'REFRESH');
assert.strictEqual(CMD_NAMES['10'], 'SET UP CALL');
assert.strictEqual(CMD_NAMES['27'], 'TIMER MANAGEMENT'); assert.strictEqual(CMD_NAMES['27'], 'TIMER MANAGEMENT');
assert.strictEqual(CMD_NAMES['28'], 'SET UP IDLE MODE TEXT');
assert.strictEqual(CMD_NAMES['40'], 'OPEN CHANNEL'); assert.strictEqual(CMD_NAMES['40'], 'OPEN CHANNEL');
assert.strictEqual(CMD_NAMES['41'], 'CLOSE CHANNEL'); assert.strictEqual(CMD_NAMES['41'], 'CLOSE CHANNEL');
assert.strictEqual(CMD_NAMES['42'], 'RECEIVE DATA'); assert.strictEqual(CMD_NAMES['42'], 'RECEIVE DATA');
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "pysim-simple-server" name = "pysim-simple-server"
version = "2.7.16" version = "2.7.17"
description = "HTTP REST server wrapping pysim for the SIMple PWA" description = "HTTP REST server wrapping pysim for the SIMple PWA"
requires-python = ">=3.8" requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
+20 -6
View File
@@ -26,7 +26,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad from osmocom.utils import rpad
VERSION = '2.7.16' VERSION = '2.7.17'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -1086,15 +1086,29 @@ _PROACTIVE_LOG = []
_PROACTIVE_SESSION_START = None _PROACTIVE_SESSION_START = None
_PROACTIVE_ENTRY_ID = 0 _PROACTIVE_ENTRY_ID = 0
# TS 102 223 §9.4 command types (the values are also the proactive command
# template tags). Unknown values fall back to 'UNKNOWN' in the log.
PROACTIVE_TYPE_NAMES = { PROACTIVE_TYPE_NAMES = {
0x03: 'POLL INTERVAL', 0x05: 'SET UP EVENT LIST', 0x01: 'REFRESH', 0x02: 'MORE TIME', 0x03: 'POLL INTERVAL',
0x13: 'SEND SHORT MESSAGE', 0x20: 'PLAY TONE', 0x04: 'POLLING OFF', 0x05: 'SET UP EVENT LIST', 0x10: 'SET UP CALL',
0x11: 'SEND SS', 0x12: 'SEND USSD', 0x13: 'SEND SHORT MESSAGE',
0x14: 'SEND DTMF', 0x15: 'LAUNCH BROWSER',
0x16: 'GEOGRAPHICAL LOCATION REQUEST', 0x20: 'PLAY TONE',
0x21: 'DISPLAY TEXT', 0x22: 'GET INKEY', 0x23: 'GET INPUT', 0x21: 'DISPLAY TEXT', 0x22: 'GET INKEY', 0x23: 'GET INPUT',
0x24: 'SELECT ITEM', 0x25: 'SET UP MENU', 0x24: 'SELECT ITEM', 0x25: 'SET UP MENU',
0x26: 'PROVIDE LOCAL INFORMATION', 0x27: 'TIMER MANAGEMENT', 0x26: 'PROVIDE LOCAL INFORMATION', 0x27: 'TIMER MANAGEMENT',
0x15: 'LAUNCH BROWSER', 0x70: 'ACTIVATE', 0x28: 'SET UP IDLE MODE TEXT', 0x30: 'PERFORM CARD APDU',
0x40: 'OPEN CHANNEL', 0x41: 'CLOSE CHANNEL', 0x31: 'POWER ON CARD', 0x32: 'POWER OFF CARD', 0x33: 'GET READER STATUS',
0x42: 'RECEIVE DATA', 0x43: 'SEND DATA', 0x44: 'GET CHANNEL STATUS', 0x34: 'RUN AT COMMAND', 0x35: 'LANGUAGE NOTIFICATION',
0x40: 'OPEN CHANNEL', 0x41: 'CLOSE CHANNEL', 0x42: 'RECEIVE DATA',
0x43: 'SEND DATA', 0x44: 'GET CHANNEL STATUS', 0x45: 'SERVICE SEARCH',
0x46: 'GET SERVICE INFORMATION', 0x47: 'DECLARE SERVICE',
0x50: 'SET FRAMES', 0x51: 'GET FRAMES STATUS',
0x60: 'RETRIEVE MULTIMEDIA MESSAGE', 0x61: 'SUBMIT MULTIMEDIA MESSAGE',
0x62: 'DISPLAY MULTIMEDIA MESSAGE', 0x70: 'ACTIVATE',
0x71: 'CONTACTLESS STATE CHANGED', 0x72: 'COMMAND CONTAINER',
0x73: 'ENCAPSULATED SESSION CONTROL', 0x79: 'LSI COMMAND',
0x81: 'End of the proactive UICC session',
} }
PLI_QUALIFIER_NAMES = { PLI_QUALIFIER_NAMES = {
+38
View File
@@ -0,0 +1,38 @@
# coding=utf-8
"""Tests for the proactive command type names (TS 102 223 §9.4).
The live card issues REFRESH (0x01) right after a Location status event; a
missing entry makes the proactive log show the generic 'Cmd 0x01' fallback.
"""
import unittest
from pysim_simple_server.server import PROACTIVE_TYPE_NAMES
class ProactiveTypeNamesTests(unittest.TestCase):
def test_refresh_is_named(self):
self.assertEqual(PROACTIVE_TYPE_NAMES[0x01], 'REFRESH')
def test_common_types_match_the_spec(self):
for value, name in [
(0x02, 'MORE TIME'), (0x03, 'POLL INTERVAL'), (0x04, 'POLLING OFF'),
(0x05, 'SET UP EVENT LIST'), (0x10, 'SET UP CALL'),
(0x11, 'SEND SS'), (0x12, 'SEND USSD'),
(0x13, 'SEND SHORT MESSAGE'), (0x14, 'SEND DTMF'),
(0x15, 'LAUNCH BROWSER'), (0x16, 'GEOGRAPHICAL LOCATION REQUEST'),
(0x21, 'DISPLAY TEXT'), (0x24, 'SELECT ITEM'), (0x25, 'SET UP MENU'),
(0x26, 'PROVIDE LOCAL INFORMATION'), (0x27, 'TIMER MANAGEMENT'),
(0x28, 'SET UP IDLE MODE TEXT'), (0x40, 'OPEN CHANNEL'),
(0x44, 'GET CHANNEL STATUS'), (0x70, 'ACTIVATE'),
]:
self.assertEqual(PROACTIVE_TYPE_NAMES[value], name, hex(value))
def test_names_are_nonempty_strings(self):
for value, name in PROACTIVE_TYPE_NAMES.items():
self.assertIsInstance(value, int)
self.assertTrue(isinstance(name, str) and name, hex(value))
if __name__ == '__main__':
unittest.main()