No description
Find a file
2026-08-17 18:02:56 +02:00
proto Feat: make print relay mode extra state in receiver 2026-05-03 11:50:02 +02:00
tests Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
.gitignore Chore: initial commit 2025-07-02 14:59:18 +02:00
__init__.py Chore: initial commit 2025-07-02 14:59:18 +02:00
AGENTS.md Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
ble_protocol.py Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
ble_replayer.py Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
messages.json Feat: add Bluetooth recordings 2026-08-17 18:02:56 +02:00
README.md Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
receiver.py Feat: add recordings for relay mode 2026-05-03 13:04:02 +02:00
recorder.py Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
recording_store.py Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
replayer.py Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00
requirements.txt Feat: add tooling for Bluetooth recordings 2026-08-17 18:02:51 +02:00

ScStw protocol recorder and replayer

These small Python tools inspect, record, and replay ScStw protobuf traffic over UDP or Bluetooth Low Energy (BLE). Recordings are stored in messages.json and retain their original timing.

Setup

Python 3.12 or newer is recommended. From the repository root, create an environment and install the UDP and BLE dependencies:

python -m venv venv
source venv/bin/activate
python -m pip install -r requirements.txt

protobuf decodes messages, bleak connects to the timer while recording, and bless exposes the timer service while replaying.

UDP

python receiver.py
python recorder.py --transport udp
python replayer.py

receiver.py listens on 0.0.0.0:4999. The UDP recorder captures complete SocketMessage datagrams on that port. The replayer sends a selected UDP recording to 127.0.0.1:4999 with the captured timing. Running python recorder.py without --transport opens an interactive transport prompt.

Record Bluetooth traffic

python recorder.py --transport bluetooth
python recorder.py --transport bluetooth --device IDENTIFIER --scan-timeout 15

The recorder scans for the ScStw timer service, prompts when multiple devices match, and connects as a BLE central. --device selects a known platform identifier or address; --scan-timeout controls device discovery time.

On connection it reads and records the initial RaceFullState, Settings, and optional SystemInfo characteristic values before capturing notifications. Stop with Ctrl+C to save the session.

Replay Bluetooth traffic

Run python replayer.py, select Bluetooth, then choose a mode and recording. The replayer advertises a BLE peripheral named ScStw and waits for a client. Initial reads return the recorded starting values; subsequent characteristic notifications follow the original timeline. This lets the Web Bluetooth client connect and initialize in the same way it does with a timer.

BLE peripheral replay supports Linux through BlueZ and macOS through CoreBluetooth. On Linux, the user must be allowed to register GATT services and advertisements, and the adapter must expose BlueZ's GattManager1 and LEAdvertisingManager1 interfaces. On macOS, enable Bluetooth and grant Bluetooth access to the terminal or IDE running Python in System Settings under Privacy & Security > Bluetooth. Test with a Web Bluetooth-capable browser on a second physical device; a host generally cannot connect to its own advertisement.

Recording format

messages.json separates recordings by transport, then by mode. UDP and Bluetooth can therefore use the same mode and recording names without replacing one another:

{
  "udp": {
    "Single, two lanes": [{
      "name": "Example race",
      "recording": [{"data": "0a081202220012022200", "time": 0.12}]
    }]
  },
  "bluetooth": {
    "Single, two lanes": [{
      "name": "Example race",
      "recording": [{
        "data": "0a03120100",
        "time": 0.12,
        "characteristic_uuid": "97ed638a-690f-4ce4-bb12-d9c4e0010001",
        "source": "initial_read"
      }]
    }]
  }
}

The top-level key is authoritative; canonical recording entries omit redundant transport metadata. Legacy mode-first files (for example, { "Single, two lanes": [...] }) are accepted and automatically partitioned using each entry's old transport value. Unmarked entries default to UDP, and legacy metadata is stripped during normalization, so no manual migration is needed.

For every event, data is lowercase, even-length hexadecimal and time is nonnegative elapsed seconds. BLE events additionally identify their characteristic_uuid; source is initial_read or notification. Race state (...0001) and settings (...0003) have required initial reads; system info (...0004) is optional. Control (...0002) is write-only and is not recorded. This lets replay preload readable values without emitting them as notifications. Keep messages.json changes focused: it is both the fixture collection and the recorder output.