Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
215d0a0ba0 | ||
|
|
8f63368008 | ||
|
|
29d762a662 | ||
|
|
eeea7ca509 | ||
|
|
ad4289eabe | ||
|
|
04859051d3 | ||
|
|
2154912d2b | ||
|
|
6750f72cd5 |
No files matched your search
@@ -9,6 +9,18 @@ every change carries a descriptive commit message.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [3.40.3] — 2026-10-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Equipped card not always auto-selected in SCP80/RAM.** The ICCID-based
|
||||||
|
preset auto-selection latched its "already handled" guard even when no
|
||||||
|
matching preset was found, so a card equipped before `/api/presets` had
|
||||||
|
loaded (the connect-time race) never got auto-selected — navigating to
|
||||||
|
SCP80/RAM showed no preset until the card was re-inserted. The guard is
|
||||||
|
now latched only after a successful selection, and `cardsFetch` re-runs
|
||||||
|
the auto-selection once the store arrives.
|
||||||
|
|
||||||
## [3.40.2] — 2026-10-26
|
## [3.40.2] — 2026-10-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -4,20 +4,25 @@
|
|||||||
|
|
||||||
📢 Project news and updates: [website](https://atroshin.ru) · [Telegram channel](https://t.me/youeyeseesee)
|
📢 Project news and updates: [website](https://atroshin.ru) · [Telegram channel](https://t.me/youeyeseesee)
|
||||||
|
|
||||||
SIMple is a workbench for UICC/SIM card specialists: an offline HTML/JS PWA plus a local card server ([`pysim-simple-server`](pysim_simple_server/), a pySim/PC-SC bridge that also serves the PWA). It covers the full lab cycle:
|
SIMple is a workbench for UICC/SIM card specialists: an offline HTML/JS frontend (PWA — a Progressive Web App) plus a local card server ([`pysim-simple-server`](pysim_simple_server/), a pySim/PC-SC bridge that also serves the frontend). It covers the full lab cycle:
|
||||||
|
|
||||||
- **APDU workbench** — SIM/USIM RFM and Expanded Script builders (TS 102 226), GlobalPlatform RAM commands, a C-APDU/R-APDU parser and a response decoder (ISO 7816 / TS 102 221 / GP);
|
- **APDU workbench** — SIM/USIM RFM and Expanded Script builders (TS 102 226), GlobalPlatform RAM commands, a C-APDU/R-APDU parser and a response decoder (ISO 7816 / TS 102 221 / GP);
|
||||||
- **OTA lab** — SCP80 secured packets (TS 102 225) and Java Card RAM installation, plus the terminal side of HTTP OTA (SCP81): BIP channel emulation, a PSK TLS administration server and APDU script execution;
|
- **OTA lab** — SCP80 secured packets (TS 102 225) and Java Card RAM installation, plus the terminal side of HTTP OTA (SCP81): BIP channel emulation, a PSK TLS administration server and APDU script execution;
|
||||||
- **Phone/CAT simulator** — SIM Toolkit menu browser, proactive command / event handling, TERMINAL PROFILE editing and the C-AT dialogue log;
|
- **Phone/CAT simulator** — SIM Toolkit menu browser, proactive command / event handling, TERMINAL PROFILE editing and the C-AT dialogue log;
|
||||||
- **Card profiler** — filesystem capture (profiles and immutable snapshots with timings), exact snapshot comparison and FCP/FCI decoding.
|
- **Card profiler** — filesystem capture (profiles and immutable snapshots with timings), exact snapshot comparison and FCP/FCI decoding.
|
||||||
|
|
||||||
**Demo:** [simple.atroshin.ru](https://simple.atroshin.ru) — the PWA alone, for experimenting. Install the server (below) for card-reader functions.
|
**Demo:** [simple.atroshin.ru](https://simple.atroshin.ru) — the frontend alone, for experimenting. Install the server (below) for card-reader functions.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Python 3.9+** with `pip`, and **Git** — the app runs on 3.9+, but the **SCP81 TLS-PSK listener needs Python 3.13+** (`ssl`'s PSK callbacks).
|
||||||
|
- **Python venv support** — `setup.sh` creates `.venv`; if that fails with `ensurepip is not available` (Debian/Ubuntu ship the venv module separately), install it (`sudo apt install python3-venv`) and run `./setup.sh` again. An incomplete `.venv` left by the failed run is removed automatically.
|
||||||
|
- **Smart card reader** (PC/SC or serial/FTDI) — PC/SC is preferable (`pcsc-lite` + `ccid` on Linux). `pyscard` has no Linux wheels, so it builds from source: install the PC/SC header package too (`libpcsclite-dev` on Debian/Ubuntu, `pcsc-lite-devel` on Fedora; on Arch `pcsclite` includes them).
|
||||||
|
- **Windows** — use **Python 3.13 (strongly recommended)**: `pyscard` ships precompiled wheels for it, so nothing needs compiling; on **3.14** the `pyscard` and some pysim dependency wheels are missing and build from source (needs MSVC C++ Build Tools).
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
**PWA only (client-side tools):** open `frontend/index.html` in any browser, or serve `frontend/` with any static server. No Python required.
|
**Install the server** — two ways to get it:
|
||||||
|
|
||||||
**Full (PWA + card server)** — two ways to get it:
|
|
||||||
|
|
||||||
**1. Download the release package (recommended).** From the
|
**1. Download the release package (recommended).** From the
|
||||||
[Releases](https://github.com/anttro/simple/releases) page take the latest package
|
[Releases](https://github.com/anttro/simple/releases) page take the latest package
|
||||||
@@ -25,7 +30,7 @@ SIMple is a workbench for UICC/SIM card specialists: an offline HTML/JS PWA plus
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
./setup.sh # or setup.bat on Windows — creates .venv, installs pysim + server
|
./setup.sh # or setup.bat on Windows — creates .venv, installs pysim + server
|
||||||
./start.sh # or start.bat — starts the server (it serves the PWA too)
|
./start.sh # or start.bat — starts the server (it serves the frontend too)
|
||||||
```
|
```
|
||||||
|
|
||||||
The package is built from the tagged source — no git history, no development files.
|
The package is built from the tagged source — no git history, no development files.
|
||||||
@@ -47,7 +52,7 @@ cd simple # `git pull` still updates it
|
|||||||
./start.sh # or start.bat
|
./start.sh # or start.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
Then open http://127.0.0.1:8080 — the UI and API share one origin, so no CORS or browser-permission setup is needed. The server starts fine with an empty reader (no card is a normal state): insert a card and it is initialized automatically (auto-equip), or press **Equip card** in the UI.
|
Then open http://127.0.0.1:8080 — the server starts fine with an empty reader (no card is a normal state): insert a card and it is initialized automatically (auto-equip), or press **Equip card** in the UI.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
@@ -557,7 +562,7 @@ probe is refused when SPI1 has no counter check or the named preset does not def
|
|||||||
|
|
||||||
## Cards
|
## Cards
|
||||||
|
|
||||||
Stores saved card configurations (presets) in a **server-side store** — `~/.pysim-simple-server/card_presets.json` (`--card-presets` overrides it). A preset holds the cryptographic keys, SPI settings, TARs and the replay counters for SCP80 operations, the optional **ADM** key, plus the **PSK identity / PSK key** pair used by the SCP81 HTTP OTA listener. A preset carries **several keysets** — a keyset's number is the `b8..b5` nibble of its KIc/KID (TS 102 225 §5.1.2/A.2): KIc and KID of a keyset must carry the same number (`01`–`0F`; A.2 rejects a mismatch), `00` (no security) is not a preset keyset, and each keyset has its own counter on the card (Annex A.1) — the server owns those counters, persists every consumed increment and appends it to an audit trail (`card_presets.json.audit.jsonl`). The list is loaded when the PWA connects; JSON export/import (which also accepts the old flat presets) is the migration path from the browser-local store. Cards is a **top-level tab**. The form groups the fields into two bordered blocks — **SCP80 (GSM 03.48, ETSI TS 102 225)** and **SCP81 (HTTP OTA)** — with the optional **ADM** field in the top row; the keysets are edited as rows (one per number, **Add keyset** / **Remove keyset**). When the card is equipped its EF.ICCID is read and the preset with the same ICCID is selected automatically in all three SCP80 views; when the equipped card matches **no** preset — or the selected preset belongs to another card — all three SCP80 views show an amber warning above the preset selector. The header shows gray **SCP80** / **SCP81** markers and a key glyph on the **ADM** badge when the preset matching the equipped card's ICCID has those settings filled in.
|
Stores saved card configurations (presets) in a **server-side store** — `~/.pysim-simple-server/card_presets.json` (`--card-presets` overrides it). A preset holds the cryptographic keys, SPI settings, TARs and the replay counters for SCP80 operations, the optional **ADM** key, plus the **PSK identity / PSK key** pair used by the SCP81 HTTP OTA listener. A preset carries **several keysets** — a keyset's number is the `b8..b5` nibble of its KIc/KID (TS 102 225 §5.1.2/A.2): KIc and KID of a keyset must carry the same number (`01`–`0F`; A.2 rejects a mismatch), `00` (no security) is not a preset keyset, and each keyset has its own counter on the card (Annex A.1) — the server owns those counters, persists every consumed increment and appends it to an audit trail (`card_presets.json.audit.jsonl`). The list is loaded when the frontend connects; JSON export/import (which also accepts the old flat presets) is the migration path from the browser-local store. Cards is a **top-level tab**. The form groups the fields into two bordered blocks — **SCP80 (GSM 03.48, ETSI TS 102 225)** and **SCP81 (HTTP OTA)** — with the optional **ADM** field in the top row; the keysets are edited as rows (one per number, **Add keyset** / **Remove keyset**). When the card is equipped its EF.ICCID is read and the preset with the same ICCID is selected automatically in all three SCP80 views; when the equipped card matches **no** preset — or the selected preset belongs to another card — all three SCP80 views show an amber warning above the preset selector. The header shows gray **SCP80** / **SCP81** markers and a key glyph on the **ADM** badge when the preset matching the equipped card's ICCID has those settings filled in.
|
||||||
|
|
||||||
| Field | Description |
|
| Field | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -588,7 +593,7 @@ The **SCP81** column shows whether the preset supplies a usable PSK pair: **✓*
|
|||||||
|
|
||||||
Connects to the bundled [`pysim-simple-server`](pysim_simple_server/) for live card operations.
|
Connects to the bundled [`pysim-simple-server`](pysim_simple_server/) for live card operations.
|
||||||
|
|
||||||
> **Browser restriction:** when the PWA is served from a public HTTPS host, reaching the local server (`http://127.0.0.1:8080`) requires two things: the server must send `Access-Control-Allow-Private-Network: true` (pysim-simple-server ≥ 1.6.1 does this automatically), and the browser must be allowed to access the local network — in Chrome/Edge/Vivaldi: Site settings → Local network access → allow the site (or accept the permission prompt). Without the browser permission, the request to `127.0.0.1` is blocked before any preflight is sent.
|
> **Browser restriction:** when the frontend is served from a public HTTPS host, reaching the local server (`http://127.0.0.1:8080`) requires two things: the server must send `Access-Control-Allow-Private-Network: true` (pysim-simple-server ≥ 1.6.1 does this automatically), and the browser must be allowed to access the local network — in Chrome/Edge/Vivaldi: Site settings → Local network access → allow the site (or accept the permission prompt). Without the browser permission, the request to `127.0.0.1` is blocked before any preflight is sent.
|
||||||
|
|
||||||
### File Browser
|
### File Browser
|
||||||
|
|
||||||
@@ -743,21 +748,14 @@ The interface is in English with Russian support. The language is detected from
|
|||||||
|
|
||||||
## Server (pysim-simple-server)
|
## Server (pysim-simple-server)
|
||||||
|
|
||||||
The bundled Python server wraps [pySim](https://osmocom.org/projects/pysim/wiki) and serves both the SIMple PWA (from `frontend/`) and a JSON API under `/api/*`.
|
The bundled Python server wraps [pySim](https://osmocom.org/projects/pysim/wiki) and serves both the SIMple frontend (from `frontend/`) and a JSON API under `/api/*`.
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- **Python 3.9+** with `pip`, and **Git** — the app runs on 3.9+, but the **SCP81 TLS-PSK listener needs Python 3.13+** (`ssl`'s PSK callbacks).
|
|
||||||
- **Python venv support** — `setup.sh` creates `.venv`; if that fails with `ensurepip is not available` (Debian/Ubuntu ship the venv module separately), install it (`sudo apt install python3-venv`) and run `./setup.sh` again. An incomplete `.venv` left by the failed run is removed automatically.
|
|
||||||
- **Smart card reader** (PC/SC or serial/FTDI) — PC/SC is preferable (`pcsc-lite` + `ccid` on Linux). `pyscard` has no Linux wheels, so it builds from source: install the PC/SC header package too (`libpcsclite-dev` on Debian/Ubuntu, `pcsc-lite-devel` on Fedora; on Arch `pcsclite` includes them).
|
|
||||||
- **Windows** — use **Python 3.13 (strongly recommended)**: `pyscard` has precompiled wheels up to 3.13 and the SCP81 TLS-PSK listener needs 3.13+. On 3.14 `pyscard` and some pysim dependency wheels are not available and build from source (needs MSVC C++ Build Tools). 3.9–3.12 run the app but not the SCP81 PSK listener. The SMPP bridge (`smpp.twisted3`) is intentionally skipped on Windows.
|
|
||||||
|
|
||||||
### Scripts
|
### Scripts
|
||||||
|
|
||||||
| Script | What it does |
|
| Script | What it does |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `setup.sh` / `setup.bat` | Creates `.venv/` (or repairs an incomplete one), installs pysim and the server. Run once after cloning or after unzipping the release package. |
|
| `setup.sh` / `setup.bat` | Creates `.venv/` (or repairs an incomplete one), installs pysim and the server. Run once after cloning or after unzipping the release package. |
|
||||||
| `start.sh` / `start.bat` | Starts the server from the venv (serves the PWA + API on `:8080`). |
|
| `start.sh` / `start.bat` | Starts the server from the venv (serves the frontend + API on `:8080`). |
|
||||||
| `update.sh` / `update.bat` | Updates a packaged install in place: fetches the latest release package, replaces the app files and re-installs the app into the existing `.venv` (pysim is kept). `--check`, `--version X.Y.Z`, `--zip FILE`, `--no-pip`, `--force`. |
|
| `update.sh` / `update.bat` | Updates a packaged install in place: fetches the latest release package, replaces the app files and re-installs the app into the existing `.venv` (pysim is kept). `--check`, `--version X.Y.Z`, `--zip FILE`, `--no-pip`, `--force`. |
|
||||||
|
|
||||||
`start.sh` checks PC/SC access before starting: if the user may not use `pcscd` (or the service is not running) it prints the cause with the group/polkit guidance and exits — the server **no longer falls back to a serial reader silently**. With access, the server auto-detects the first PC/SC reader; `start.bat` always passes `-p 0` (PC/SC is built into Windows). An explicit serial/FTDI reader is still possible with `-d <device>` (the pre-flight check is skipped). Extra arguments are passed through to the server, e.g. `./start.sh --gsmtap` or `start.bat --gsmtap 10.0.0.5:4729`. If no PC/SC reader is found the server reports it and exits — connect the reader and restart (a reader present but no card is still a normal cardless start).
|
`start.sh` checks PC/SC access before starting: if the user may not use `pcscd` (or the service is not running) it prints the cause with the group/polkit guidance and exits — the server **no longer falls back to a serial reader silently**. With access, the server auto-detects the first PC/SC reader; `start.bat` always passes `-p 0` (PC/SC is built into Windows). An explicit serial/FTDI reader is still possible with `-d <device>` (the pre-flight check is skipped). Extra arguments are passed through to the server, e.g. `./start.sh --gsmtap` or `start.bat --gsmtap 10.0.0.5:4729`. If no PC/SC reader is found the server reports it and exits — connect the reader and restart (a reader present but no card is still a normal cardless start).
|
||||||
@@ -773,8 +771,8 @@ pysim-simple-server --http-port 8080
|
|||||||
```
|
```
|
||||||
|
|
||||||
On Windows use `setup.bat` instead: it installs pysim's dependencies from
|
On Windows use `setup.bat` instead: it installs pysim's dependencies from
|
||||||
`requirements-pysim.txt` and pysim itself with `--no-deps`, so the SMPP bridge (and
|
`requirements-pysim.txt` and pysim itself with `--no-deps`, so pysim's optional
|
||||||
its MSVC build chain) is not pulled in.
|
dependencies are not pulled in.
|
||||||
|
|
||||||
### CLI options
|
### CLI options
|
||||||
|
|
||||||
@@ -782,7 +780,7 @@ its MSVC build chain) is not pulled in.
|
|||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `--http-host` | Bind address (default: `127.0.0.1`) |
|
| `--http-host` | Bind address (default: `127.0.0.1`) |
|
||||||
| `--http-port` | TCP port (default: `8080`) |
|
| `--http-port` | TCP port (default: `8080`) |
|
||||||
| `--web-dir` | Directory with PWA static files (default: `<repo>/frontend`) |
|
| `--web-dir` | Directory with frontend static files (default: `<repo>/frontend`) |
|
||||||
| `-p` / `--pcsc-device` | PC/SC reader slot number |
|
| `-p` / `--pcsc-device` | PC/SC reader slot number |
|
||||||
| `-d` / `--device` | Serial device path |
|
| `-d` / `--device` | Serial device path |
|
||||||
| `--no-card-init` | Skip card init to preserve the CAT session (no file manager) |
|
| `--no-card-init` | Skip card init to preserve the CAT session (no file manager) |
|
||||||
@@ -824,13 +822,13 @@ See [docs/api.md](docs/api.md) for the full endpoint reference.
|
|||||||
|
|
||||||
## Version compatibility
|
## Version compatibility
|
||||||
|
|
||||||
| PWA (SIMple) | Server | Status |
|
| frontend (SIMple) | Server | Status |
|
||||||
|-------------|--------|--------|
|
|-------------|--------|--------|
|
||||||
| any | same major | ✅ Compatible |
|
| any | same major | ✅ Compatible |
|
||||||
| any | older major | ❌ Outdated — update server |
|
| any | older major | ❌ Outdated — update server |
|
||||||
| any | newer major | ⚠️ Server newer — update PWA |
|
| any | newer major | ⚠️ Server newer — update frontend |
|
||||||
|
|
||||||
The PWA checks the server version on connect via `GET /api/version` and compares the major version (e.g. a 3.x PWA with a 3.x server; a 2.x server is flagged as outdated).
|
The frontend checks the server version on connect via `GET /api/version` and compares the major version (e.g. a 3.x frontend with a 3.x server; a 2.x server is flagged as outdated).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -843,5 +841,5 @@ pySim itself is not included in this repository or in the release artifacts:
|
|||||||
`setup.sh` / `setup.bat` install it from its upstream git repository.
|
`setup.sh` / `setup.bat` install it from its upstream git repository.
|
||||||
|
|
||||||
Bundled third-party components (the MCC/MNC operator list and the AES/DES
|
Bundled third-party components (the MCC/MNC operator list and the AES/DES
|
||||||
bundles in the PWA) keep their own MIT licenses — see
|
bundles in the frontend) keep their own MIT licenses — see
|
||||||
[THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES).
|
[THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES).
|
||||||
+24
-26
@@ -4,20 +4,25 @@
|
|||||||
|
|
||||||
📢 Новости и обновления проекта: [сайт](https://atroshin.ru) · [Telegram-канал](https://t.me/youeyeseesee)
|
📢 Новости и обновления проекта: [сайт](https://atroshin.ru) · [Telegram-канал](https://t.me/youeyeseesee)
|
||||||
|
|
||||||
SIMple — инструментарий для специалистов по UICC/SIM-картам: автономный HTML/JS PWA и локальный сервер карт ([`pysim-simple-server`](pysim_simple_server/) — мост к pySim/PC-SC, он же раздаёт PWA). Закрывает весь цикл лабораторной работы:
|
SIMple — инструментарий для специалистов по UICC/SIM-картам: автономный HTML/JS-фронтенд (PWA — Progressive Web App) и локальный сервер карт ([`pysim-simple-server`](pysim_simple_server/) — мост к pySim/PC-SC, он же раздаёт фронтенд). Закрывает весь цикл лабораторной работы:
|
||||||
|
|
||||||
- **APDU-верстак** — конструкторы SIM/USIM RFM и Expanded Script (TS 102 226), команды GlobalPlatform RAM, парсер C-APDU/R-APDU и декодер ответов (ISO 7816 / TS 102 221 / GP);
|
- **APDU-верстак** — конструкторы SIM/USIM RFM и Expanded Script (TS 102 226), команды GlobalPlatform RAM, парсер C-APDU/R-APDU и декодер ответов (ISO 7816 / TS 102 221 / GP);
|
||||||
- **OTA-лаборатория** — защищённые пакеты SCP80 (TS 102 225) и установка Java Card RAM, а также терминальная сторона HTTP OTA (SCP81): эмуляция BIP-каналов, PSK-TLS-сервер администрирования и выполнение скриптов APDU;
|
- **OTA-лаборатория** — защищённые пакеты SCP80 (TS 102 225) и установка Java Card RAM, а также терминальная сторона HTTP OTA (SCP81): эмуляция BIP-каналов, PSK-TLS-сервер администрирования и выполнение скриптов APDU;
|
||||||
- **Симулятор** — браузер меню SIM Toolkit, обработка proactive-команд и событий, редактирование TERMINAL PROFILE, тест-скрипты карты и журнал C-AT-диалога;
|
- **Симулятор** — браузер меню SIM Toolkit, обработка proactive-команд и событий, редактирование TERMINAL PROFILE, тест-скрипты карты и журнал C-AT-диалога;
|
||||||
- **Профайлер карты** — захват файловой системы (профили и неизменяемые снимки с таймингами), точное сравнение снимков и декодер FCP/FCI.
|
- **Профайлер карты** — захват файловой системы (профили и неизменяемые снимки с таймингами), точное сравнение снимков и декодер FCP/FCI.
|
||||||
|
|
||||||
**Демо:** [simple.atroshin.ru](https://simple.atroshin.ru) — только PWA, для экспериментов. Для функций картридера установите сервер (ниже).
|
**Демо:** [simple.atroshin.ru](https://simple.atroshin.ru) — только фронтенд, для экспериментов. Для функций картридера установите сервер (ниже).
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- **Python 3.9+** с `pip`, и **Git** — приложение работает на 3.9+, но **SCP81 TLS-PSK-слушателю нужен Python 3.13+** (PSK-callback'и `ssl`).
|
||||||
|
- **Поддержка venv в Python** — `setup.sh` создаёт `.venv`; если создание завершается с `ensurepip is not available` (в Debian/Ubuntu модуль venv поставляется отдельно), установите его (`sudo apt install python3-venv`) и запустите `./setup.sh` снова. Неполный `.venv`, оставшийся от неудачного запуска, удаляется автоматически.
|
||||||
|
- **Смарт-картридер** (PC/SC или serial/FTDI) — предпочтителен PC/SC (`pcsc-lite` + `ccid` на Linux). У `pyscard` нет Linux-wheel, поэтому он собирается из исходников: поставьте и пакет с заголовками PC/SC (`libpcsclite-dev` в Debian/Ubuntu, `pcsc-lite-devel` в Fedora; в Arch они входят в `pcsclite`).
|
||||||
|
- **Windows** — используйте **Python 3.13 (настоятельно рекомендуется)**: для него `pyscard` поставляет готовые wheel, так что ничего компилировать не нужно; на **3.14** wheel для `pyscard` и части зависимостей pysim отсутствуют, и они собираются из исходников (нужны MSVC C++ Build Tools).
|
||||||
|
|
||||||
## Быстрый старт
|
## Быстрый старт
|
||||||
|
|
||||||
**Только PWA (клиентские функции):** откройте `frontend/index.html` в любом браузере или раздайте `frontend/` любым статическим сервером. Python не нужен.
|
**Установка сервера** — два способа:
|
||||||
|
|
||||||
**Полная установка (PWA + сервер карт)** — два способа:
|
|
||||||
|
|
||||||
**1. Скачать релизный пакет (рекомендуется).** На странице
|
**1. Скачать релизный пакет (рекомендуется).** На странице
|
||||||
[Releases](https://github.com/anttro/simple/releases) возьмите последний пакет
|
[Releases](https://github.com/anttro/simple/releases) возьмите последний пакет
|
||||||
@@ -25,7 +30,7 @@ SIMple — инструментарий для специалистов по UIC
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
./setup.sh # или setup.bat в Windows — создаёт .venv, ставит pysim + сервер
|
./setup.sh # или setup.bat в Windows — создаёт .venv, ставит pysim + сервер
|
||||||
./start.sh # или start.bat — запускает сервер (он же раздаёт PWA)
|
./start.sh # или start.bat — запускает сервер (он же раздаёт фронтенд)
|
||||||
```
|
```
|
||||||
|
|
||||||
Пакет собран из помеченного тегом исходного кода — без истории git и без файлов для
|
Пакет собран из помеченного тегом исходного кода — без истории git и без файлов для
|
||||||
@@ -47,7 +52,7 @@ cd simple # `git pull` всё равно
|
|||||||
./start.sh # или start.bat
|
./start.sh # или start.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
Затем откройте http://127.0.0.1:8080 — интерфейс и API на одном origin, поэтому CORS и разрешения браузера не нужны. Сервер запускается и с пустым картридером (отсутствие карты — нормальное состояние): вставьте карту, и она инициализируется автоматически (auto-equip), либо нажмите **Equip card** в интерфейсе.
|
Затем откройте http://127.0.0.1:8080 — сервер запускается и с пустым картридером (отсутствие карты — нормальное состояние): вставьте карту, и она инициализируется автоматически (auto-equip), либо нажмите **Equip card** в интерфейсе.
|
||||||
|
|
||||||
## Сборка
|
## Сборка
|
||||||
|
|
||||||
@@ -532,7 +537,7 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
|||||||
|
|
||||||
## Карты
|
## Карты
|
||||||
|
|
||||||
Хранит сохранённые конфигурации карт (пресеты) в **серверном хранилище** — `~/.pysim-simple-server/card_presets.json` (переопределяется `--card-presets`). Пресет содержит криптографические ключи, настройки SPI, TAR-ы и счётчики повторов для SCP80-операций, необязательный ключ **ADM**, а также пару **PSK identity / PSK key** для слушателя SCP81 HTTP OTA. Пресет содержит **несколько keysets** — номер keyset задаётся нибблом `b8..b5` его KIc/KID (TS 102 225 §5.1.2/A.2): KIc и KID одного keyset должны нести один и тот же номер (`01`–`0F`; A.2 отвергает несовпадение), `00` («без защиты») не является keyset пресета, и у каждого keyset на карте свой счётчик (Annex A.1) — счётчиками владеет сервер: он сохраняет каждый израсходованный инкремент и пишет его в журнал аудита (`card_presets.json.audit.jsonl`). Список загружается при подключении PWA; экспорт/импорт JSON (принимает и старые плоские пресеты) — путь миграции из браузерного хранилища. «Карты» — **верхнеуровневая вкладка**. Поля формы сгруппированы в два блока с рамкой — **SCP80 (GSM 03.48, ETSI TS 102 225)** и **SCP81 (HTTP OTA)**; необязательное поле **ADM** находится в верхней строке, а keysets редактируются строками (по одной на номер, **Добавить keyset** / **Удалить keyset**). При подключении карты читается её EF.ICCID, и пресет с тем же ICCID автоматически выбирается во всех трёх видах SCP80; если подключённая карта не совпадает ни с одним пресетом — или выбранный пресет принадлежит другой карте — все три вида SCP80 показывают жёлтое предупреждение над выбором пресета. В шапке отображаются серые маркеры **SCP80** / **SCP81** и значок ключа на бейдже **ADM**, когда в пресете с ICCID подключённой карты эти поля заполнены.
|
Хранит сохранённые конфигурации карт (пресеты) в **серверном хранилище** — `~/.pysim-simple-server/card_presets.json` (переопределяется `--card-presets`). Пресет содержит криптографические ключи, настройки SPI, TAR-ы и счётчики повторов для SCP80-операций, необязательный ключ **ADM**, а также пару **PSK identity / PSK key** для слушателя SCP81 HTTP OTA. Пресет содержит **несколько keysets** — номер keyset задаётся нибблом `b8..b5` его KIc/KID (TS 102 225 §5.1.2/A.2): KIc и KID одного keyset должны нести один и тот же номер (`01`–`0F`; A.2 отвергает несовпадение), `00` («без защиты») не является keyset пресета, и у каждого keyset на карте свой счётчик (Annex A.1) — счётчиками владеет сервер: он сохраняет каждый израсходованный инкремент и пишет его в журнал аудита (`card_presets.json.audit.jsonl`). Список загружается при подключении фронтенда; экспорт/импорт JSON (принимает и старые плоские пресеты) — путь миграции из браузерного хранилища. «Карты» — **верхнеуровневая вкладка**. Поля формы сгруппированы в два блока с рамкой — **SCP80 (GSM 03.48, ETSI TS 102 225)** и **SCP81 (HTTP OTA)**; необязательное поле **ADM** находится в верхней строке, а keysets редактируются строками (по одной на номер, **Добавить keyset** / **Удалить keyset**). При подключении карты читается её EF.ICCID, и пресет с тем же ICCID автоматически выбирается во всех трёх видах SCP80; если подключённая карта не совпадает ни с одним пресетом — или выбранный пресет принадлежит другой карте — все три вида SCP80 показывают жёлтое предупреждение над выбором пресета. В шапке отображаются серые маркеры **SCP80** / **SCP81** и значок ключа на бейдже **ADM**, когда в пресете с ICCID подключённой карты эти поля заполнены.
|
||||||
|
|
||||||
| Поле | Описание |
|
| Поле | Описание |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -563,7 +568,7 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
|||||||
|
|
||||||
Подключение к встроенному [`pysim-simple-server`](pysim_simple_server/) для работы с картой.
|
Подключение к встроенному [`pysim-simple-server`](pysim_simple_server/) для работы с картой.
|
||||||
|
|
||||||
> **Ограничение браузера:** если PWA раздаётся с публичного HTTPS-хоста, для доступа к локальному серверу (`http://127.0.0.1:8080`) нужны два условия: сервер должен отправлять `Access-Control-Allow-Private-Network: true` (pysim-simple-server ≥ 1.6.1 делает это автоматически), и браузеру должно быть разрешено обращаться к локальной сети — в Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → разрешить сайт (или подтвердить запрос). Без разрешения браузера запрос к `127.0.0.1` блокируется ещё до отправки preflight.
|
> **Ограничение браузера:** если фронтенд раздаётся с публичного HTTPS-хоста, для доступа к локальному серверу (`http://127.0.0.1:8080`) нужны два условия: сервер должен отправлять `Access-Control-Allow-Private-Network: true` (pysim-simple-server ≥ 1.6.1 делает это автоматически), и браузеру должно быть разрешено обращаться к локальной сети — в Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → разрешить сайт (или подтвердить запрос). Без разрешения браузера запрос к `127.0.0.1` блокируется ещё до отправки preflight.
|
||||||
|
|
||||||
### Файловый менеджер
|
### Файловый менеджер
|
||||||
|
|
||||||
@@ -715,33 +720,26 @@ SIMple — Progressive Web App. Можно установить для offline-
|
|||||||
|
|
||||||
## Совместимость версий
|
## Совместимость версий
|
||||||
|
|
||||||
| PWA (SIMple) | Сервер | Статус |
|
| фронтенд (SIMple) | Сервер | Статус |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| любая | та же мажорная | ✅ Совместимы |
|
| любая | та же мажорная | ✅ Совместимы |
|
||||||
| любая | старее мажорная | ❌ Сервер устарел — обновите сервер |
|
| любая | старее мажорная | ❌ Сервер устарел — обновите сервер |
|
||||||
| любая | новее мажорная | ⚠️ Сервер новее — обновите PWA |
|
| любая | новее мажорная | ⚠️ Сервер новее — обновите фронтенд |
|
||||||
|
|
||||||
PWA проверяет версию сервера при подключении через `GET /api/version` и сравнивает мажорную версию (например, PWA 3.x с сервером 3.x; сервер 2.x помечается как устаревший).
|
фронтенд проверяет версию сервера при подключении через `GET /api/version` и сравнивает мажорную версию (например, фронтенд 3.x с сервером 3.x; сервер 2.x помечается как устаревший).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Сервер (pysim-simple-server)
|
## Сервер (pysim-simple-server)
|
||||||
|
|
||||||
Встроенный Python-сервер оборачивает [pySim](https://osmocom.org/projects/pysim/wiki) и раздаёт как PWA (из `frontend/`), так и JSON API по `/api/*`.
|
Встроенный Python-сервер оборачивает [pySim](https://osmocom.org/projects/pysim/wiki) и раздаёт как фронтенд (из `frontend/`), так и JSON API по `/api/*`.
|
||||||
|
|
||||||
### Требования
|
|
||||||
|
|
||||||
- **Python 3.9+** с `pip`, и **Git** — приложение работает на 3.9+, но **SCP81 TLS-PSK-слушателю нужен Python 3.13+** (PSK-callback'и `ssl`).
|
|
||||||
- **Поддержка venv в Python** — `setup.sh` создаёт `.venv`; если создание завершается с `ensurepip is not available` (в Debian/Ubuntu модуль venv поставляется отдельно), установите его (`sudo apt install python3-venv`) и запустите `./setup.sh` снова. Неполный `.venv`, оставшийся от неудачного запуска, удаляется автоматически.
|
|
||||||
- **Смарт-картридер** (PC/SC или serial/FTDI) — предпочтителен PC/SC (`pcsc-lite` + `ccid` на Linux). У `pyscard` нет Linux-wheel, поэтому он собирается из исходников: поставьте и пакет с заголовками PC/SC (`libpcsclite-dev` в Debian/Ubuntu, `pcsc-lite-devel` в Fedora; в Arch они входят в `pcsclite`).
|
|
||||||
- **Windows** — используйте **Python 3.13 (настоятельно рекомендуется)**: у `pyscard` есть готовые wheel до 3.13, и SCP81 TLS-PSK-слушателю нужен 3.13+. На 3.14 wheel для `pyscard` и части зависимостей pysim отсутствуют, они собираются из исходников (нужны MSVC C++ Build Tools). 3.9–3.12 запускают приложение, но не SCP81 PSK-слушатель. SMPP-мост (`smpp.twisted3`) в Windows сознательно не ставится.
|
|
||||||
|
|
||||||
### Скрипты
|
### Скрипты
|
||||||
|
|
||||||
| Скрипт | Назначение |
|
| Скрипт | Назначение |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `setup.sh` / `setup.bat` | Создаёт `.venv/` (или исправляет неполный), ставит pysim и сервер. Запускать один раз после клонирования или распаковки релизного пакета. |
|
| `setup.sh` / `setup.bat` | Создаёт `.venv/` (или исправляет неполный), ставит pysim и сервер. Запускать один раз после клонирования или распаковки релизного пакета. |
|
||||||
| `start.sh` / `start.bat` | Запускает сервер из venv (раздаёт PWA + API на `:8080`). |
|
| `start.sh` / `start.bat` | Запускает сервер из venv (раздаёт фронтенд + API на `:8080`). |
|
||||||
| `update.sh` / `update.bat` | Обновляет пакетную установку на месте: скачивает последний релизный пакет, заменяет файлы приложения и переустанавливает приложение в существующий `.venv` (pysim сохраняется). `--check`, `--version X.Y.Z`, `--zip FILE`, `--no-pip`, `--force`. |
|
| `update.sh` / `update.bat` | Обновляет пакетную установку на месте: скачивает последний релизный пакет, заменяет файлы приложения и переустанавливает приложение в существующий `.venv` (pysim сохраняется). `--check`, `--version X.Y.Z`, `--zip FILE`, `--no-pip`, `--force`. |
|
||||||
|
|
||||||
`start.sh` перед запуском проверяет доступ к PC/SC: если пользователь не может использовать `pcscd` (или служба не запущена), он печатает причину и рекомендации (группа/polkit) и завершается — сервер **больше не откатывается на последовательный ридер молча**. При наличии доступа сервер сам находит первый PC/SC-ридер; `start.bat` всегда передаёт `-p 0` (в Windows PC/SC встроен). Явный serial/FTDI-ридер по-прежнему возможен через `-d <устройство>` (проверка пропускается). Дополнительные аргументы передаются серверу, например `./start.sh --gsmtap` или `start.bat --gsmtap 10.0.0.5:4729`. Если PC/SC-ридер не найден, сервер сообщает об этом и завершается — подключите ридер и перезапустите (ридер без карты — по-прежнему нормальный старт без карты).
|
`start.sh` перед запуском проверяет доступ к PC/SC: если пользователь не может использовать `pcscd` (или служба не запущена), он печатает причину и рекомендации (группа/polkit) и завершается — сервер **больше не откатывается на последовательный ридер молча**. При наличии доступа сервер сам находит первый PC/SC-ридер; `start.bat` всегда передаёт `-p 0` (в Windows PC/SC встроен). Явный serial/FTDI-ридер по-прежнему возможен через `-d <устройство>` (проверка пропускается). Дополнительные аргументы передаются серверу, например `./start.sh --gsmtap` или `start.bat --gsmtap 10.0.0.5:4729`. Если PC/SC-ридер не найден, сервер сообщает об этом и завершается — подключите ридер и перезапустите (ридер без карты — по-прежнему нормальный старт без карты).
|
||||||
@@ -757,8 +755,8 @@ pysim-simple-server --http-port 8080
|
|||||||
```
|
```
|
||||||
|
|
||||||
В Windows используйте `setup.bat`: он ставит зависимости pysim из
|
В Windows используйте `setup.bat`: он ставит зависимости pysim из
|
||||||
`requirements-pysim.txt`, а сам pysim — с `--no-deps`, поэтому SMPP-мост (и его сборка
|
`requirements-pysim.txt`, а сам pysim — с `--no-deps`, поэтому необязательные
|
||||||
через MSVC) не подтягивается.
|
зависимости pysim не подтягиваются.
|
||||||
|
|
||||||
### Параметры CLI
|
### Параметры CLI
|
||||||
|
|
||||||
@@ -766,7 +764,7 @@ pysim-simple-server --http-port 8080
|
|||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| `--http-host` | Адрес привязки (по умолчанию `127.0.0.1`) |
|
| `--http-host` | Адрес привязки (по умолчанию `127.0.0.1`) |
|
||||||
| `--http-port` | Порт (по умолчанию `8080`) |
|
| `--http-port` | Порт (по умолчанию `8080`) |
|
||||||
| `--web-dir` | Каталог со статикой PWA (по умолчанию `<repo>/frontend`) |
|
| `--web-dir` | Каталог со статикой фронтенда (по умолчанию `<repo>/frontend`) |
|
||||||
| `-p` / `--pcsc-device` | Номер слота PC/SC |
|
| `-p` / `--pcsc-device` | Номер слота PC/SC |
|
||||||
| `-d` / `--device` | Путь к serial-устройству |
|
| `-d` / `--device` | Путь к serial-устройству |
|
||||||
| `--no-card-init` | Пропустить инициализацию карты (сохранить CAT-сессию) |
|
| `--no-card-init` | Пропустить инициализацию карты (сохранить CAT-сессию) |
|
||||||
@@ -816,5 +814,5 @@ pysim-simple-server --http-port 8080
|
|||||||
Сам pySim не входит ни в этот репозиторий, ни в релизные артефакты: `setup.sh` /
|
Сам pySim не входит ни в этот репозиторий, ни в релизные артефакты: `setup.sh` /
|
||||||
`setup.bat` устанавливают его из его upstream git-репозитория.
|
`setup.bat` устанавливают его из его upstream git-репозитория.
|
||||||
|
|
||||||
Встроенные сторонние компоненты (список операторов MCC/MNC и AES/DES-бандлы в
|
Встроенные сторонние компоненты (список операторов MCC/MNC и AES/DES-бандлы во
|
||||||
PWA) сохраняют свои лицензии MIT — см. [THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES).
|
фронтенде) сохраняют свои лицензии MIT — см. [THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES).
|
||||||
+12
-3
@@ -1104,9 +1104,9 @@
|
|||||||
<div id="pysim-body" class="hidden">
|
<div id="pysim-body" class="hidden">
|
||||||
<div class="flex flex-wrap justify-center gap-1 mb-2">
|
<div class="flex flex-wrap justify-center gap-1 mb-2">
|
||||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-blue-600 text-white" data-pysim-sub="files" data-l10n="File manager">File manager</button>
|
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-blue-600 text-white" data-pysim-sub="files" data-l10n="File manager">File manager</button>
|
||||||
|
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600 disabled:opacity-40 disabled:cursor-not-allowed" data-pysim-sub="services" data-needs="card" data-l10n="Services">Services</button>
|
||||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="cmd" data-l10n="pySim command line">pySim command line</button>
|
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="cmd" data-l10n="pySim command line">pySim command line</button>
|
||||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="apdu" data-l10n="Raw APDU">Raw APDU</button>
|
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600" data-pysim-sub="apdu" data-l10n="Raw APDU">Raw APDU</button>
|
||||||
<button class="pysim-subtab px-3 py-1 text-sm rounded-full bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600 disabled:opacity-40 disabled:cursor-not-allowed" data-pysim-sub="services" data-needs="card" data-l10n="Services">Services</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="pysim-sub-files">
|
<div id="pysim-sub-files">
|
||||||
@@ -1990,7 +1990,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 = '3.40.2';
|
const SIMPLE_VERSION = '3.40.3';
|
||||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||||
|
|
||||||
// ===== view history (the browser Back/Forward buttons) =====
|
// ===== view history (the browser Back/Forward buttons) =====
|
||||||
@@ -14315,6 +14315,11 @@ async function cardsFetch() {
|
|||||||
cardsRebuildSelect();
|
cardsRebuildSelect();
|
||||||
cardsRender();
|
cardsRender();
|
||||||
tarRender();
|
tarRender();
|
||||||
|
// A card may already be equipped when the presets arrive (the server
|
||||||
|
// starts with a card, or the fetch simply raced the first status poll):
|
||||||
|
// run the auto-selection now that the store is loaded. It is a no-op
|
||||||
|
// once the card's preset is already selected (the latched ICCID guard).
|
||||||
|
cardsAutoSelectByIccid(_pysimCardIccid);
|
||||||
scp81PushPskMap();
|
scp81PushPskMap();
|
||||||
spPresetWarningRender();
|
spPresetWarningRender();
|
||||||
return true;
|
return true;
|
||||||
@@ -15209,12 +15214,16 @@ let _cardsAutoIccid = null;
|
|||||||
function cardsAutoSelectByIccid(iccid) {
|
function cardsAutoSelectByIccid(iccid) {
|
||||||
// Act only when the equip reports a *new* ICCID: a manual preset choice
|
// Act only when the equip reports a *new* ICCID: a manual preset choice
|
||||||
// for the same card must not be overridden by later status updates.
|
// for the same card must not be overridden by later status updates.
|
||||||
|
// The guard is latched only once the card's preset is actually found and
|
||||||
|
// selected - a miss (the preset store not loaded yet, or no preset for
|
||||||
|
// this card) is retried when the store arrives (cardsFetch) so a card
|
||||||
|
// equipped before the presets loaded still gets auto-selected.
|
||||||
const norm = cardsNormIccid(iccid);
|
const norm = cardsNormIccid(iccid);
|
||||||
if (!norm) { _cardsAutoIccid = null; return -1; }
|
if (!norm) { _cardsAutoIccid = null; return -1; }
|
||||||
if (norm === _cardsAutoIccid) return -1;
|
if (norm === _cardsAutoIccid) return -1;
|
||||||
_cardsAutoIccid = norm;
|
|
||||||
const idx = cardsFindByIccid(norm);
|
const idx = cardsFindByIccid(norm);
|
||||||
if (idx < 0) return -1;
|
if (idx < 0) return -1;
|
||||||
|
_cardsAutoIccid = norm;
|
||||||
const sp = document.getElementById('sp-card-sel');
|
const sp = document.getElementById('sp-card-sel');
|
||||||
if (sp) sp.value = String(idx);
|
if (sp) sp.value = String(idx);
|
||||||
const ram = document.getElementById('ram-card-sel');
|
const ram = document.getElementById('ram-card-sel');
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simple-v487';
|
const CACHE = 'simple-v488';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
@@ -146,6 +146,30 @@ test('cardsAutoSelectByIccid reacts to a card swap and to unreadable ICCIDs', ()
|
|||||||
assert.strictEqual(cardsAutoSelectByIccid('1234567890123456789'), -1);
|
assert.strictEqual(cardsAutoSelectByIccid('1234567890123456789'), -1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a miss is not latched, so the store arriving later still auto-selects', () => {
|
||||||
|
// The card equips before /api/presets fills cards[] (the connect-time
|
||||||
|
// race): the auto-selection must not latch the ICCID on a miss, or the
|
||||||
|
// later cardsFetch would be a no-op and the preset would stay unselected
|
||||||
|
// until the card is re-inserted.
|
||||||
|
const els = fakeDoc();
|
||||||
|
const applied = [];
|
||||||
|
globalThis.cardsApply = idx => applied.push(idx);
|
||||||
|
globalThis.cards = []; // presets not loaded yet
|
||||||
|
globalThis._ramCardIdx = null;
|
||||||
|
globalThis._cardsAutoIccid = null;
|
||||||
|
assert.strictEqual(cardsAutoSelectByIccid(DIGITS), -1);
|
||||||
|
assert.strictEqual(globalThis._cardsAutoIccid, null,
|
||||||
|
'a missed auto-selection must not latch the ICCID');
|
||||||
|
// the presets arrive: the retry (from cardsFetch) finds and selects the card
|
||||||
|
globalThis.cards = [{ name: 'A', iccid: RAW_HEX }];
|
||||||
|
assert.strictEqual(cardsAutoSelectByIccid(DIGITS), 0);
|
||||||
|
assert.strictEqual(els['sp-card-sel'].value, '0');
|
||||||
|
assert.strictEqual(els['ram-card-sel'].value, '0');
|
||||||
|
assert.strictEqual(els['tar-card-sel'].value, '0');
|
||||||
|
assert.deepStrictEqual(applied, ['0']);
|
||||||
|
assert.strictEqual(globalThis._ramCardIdx, 0);
|
||||||
|
});
|
||||||
|
|
||||||
test('cardsFindDuplicateIccid catches duplicates across stored formats', () => {
|
test('cardsFindDuplicateIccid catches duplicates across stored formats', () => {
|
||||||
globalThis.cards = [
|
globalThis.cards = [
|
||||||
{ name: 'A', iccid: '1111111111111111111' },
|
{ name: 'A', iccid: '1111111111111111111' },
|
||||||
@@ -201,6 +225,13 @@ test('the card-state update wires the ICCID into the preset selection', () => {
|
|||||||
assert.ok(html.includes("(data.iccid ? ' | ICCID: <b>' + esc(data.iccid) + '</b>' : '')"));
|
assert.ok(html.includes("(data.iccid ? ' | ICCID: <b>' + esc(data.iccid) + '</b>' : '')"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cardsFetch retries the auto-selection once the presets load', () => {
|
||||||
|
// The equip may be reported before /api/presets resolves, so the fetch
|
||||||
|
// must re-run the auto-selection (a no-op once the card is selected).
|
||||||
|
assert.ok(html.includes('cardsAutoSelectByIccid(_pysimCardIccid);'),
|
||||||
|
'cardsFetch must re-run the auto-selection after loading the store');
|
||||||
|
});
|
||||||
|
|
||||||
test('cardsRender highlights the equipped card preset', () => {
|
test('cardsRender highlights the equipped card preset', () => {
|
||||||
globalThis.t = s => s;
|
globalThis.t = s => s;
|
||||||
const els = { 'cards-tbody': { innerHTML: '' } };
|
const els = { 'cards-tbody': { innerHTML: '' } };
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pysim-simple-server"
|
name = "pysim-simple-server"
|
||||||
version = "3.40.2"
|
version = "3.40.3"
|
||||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||||
license = { text = "GPL-2.0-or-later" }
|
license = { text = "GPL-2.0-or-later" }
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from osmocom.tlv import BER_TLV_IE
|
|||||||
from osmocom.utils import rpad
|
from osmocom.utils import rpad
|
||||||
|
|
||||||
|
|
||||||
VERSION = '3.40.2'
|
VERSION = '3.40.3'
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user