Shot timers
Architecture
apps/tablet/src/timer/TimerAdapter.ts defines a small interface: connect/disconnect/subscribe/source, with events start, shot {index, time, split}, final {time, shots}, battery, error. The scoring screen only knows that interface. Every timer is an adapter:
| Adapter | Status |
|---|---|
ManualTimer | done โ the RO types in the time |
SgTimer (Shooters Global, BLE) | scaffold โ connecting, notifications, frame decoder; UUIDs and frame format are placeholders |
| CED7000 | roadmap (see below) |
All shot times are stored with the score (StageScore.shotTimes) and timerSource records which timer the time came from (sg_timer:<id> or manual).
Shooters Global SG Timer
Approach to finish the adapter:
- Request the official BLE API documentation from Shooters Global (they provide it to integrators of scoring apps).
- Otherwise: read the GATT services of an SG Timer with nRF Connect (Android) and record a run (start, a few shots, stop) to work out the notification format.
- Only change
SG_SERVICE_UUID,SG_NOTIFY_CHAR_UUIDandparseFrame()insgTimer.ts; the rest of the app stays unchanged. - Test on a real Android tablet with a development build (
pnpm --filter @makeready/tablet android); BLE does not work in Expo Go or in the emulator.
Behaviour in the app: on start a new run begins (shots cleared), every shot appears live on the tablet and on the dashboards (via the live channel), final sets the stage time and switches to hit entry. The RO can always override the time manually.
CED7000
The classic CED7000 has no Bluetooth; the CED7000 Pro / BT variants do (serial over BLE). Once the SG adapter is stable, a CED adapter is the same exercise: service/characteristic + frame decoder.
Android permissions
app.json requests BLUETOOTH_SCAN, BLUETOOTH_CONNECT and ACCESS_FINE_LOCATION (required for BLE scans on older Android versions). The react-native-ble-plx config plugin is set to central.