The web interface and API for configuring and monitoring trackIT Systems sensor stations. It runs on every station of the tsOS-base image, behind Caddy and a login with the pi account's password, and is the one place to set up a station, see how it is doing and watch its signals live. A Bluetooth Low Energy gateway offers the same API to a phone in the field.
Features · Installation · Configuration · Usage · API · BLE gateway · Development · License
| Page | What it does |
|---|---|
| Status | CPU, memory, disk, temperature, load, network, power and location at a glance; start, stop and restart the station's systemd services and stream their logs; reboot |
| Live | Signals of the radiotracking pipeline as they arrive, in the browser, with no server-side component (see below) |
| System Settings | Network (Wi-Fi, LTE, WireGuard), time and location, users and SSH keys, MQTT, updates, and the other groups of the station's own configuration |
| Radio Tracking | RTL-SDR devices, signal detection and matching, and where the results are published (MQTT, CSV); an expert mode shows the advanced options |
| Schedule | Operation schedules from fixed times or astronomical events (sunrise, sunset, dawn, dusk, noon, golden and blue hour) with offsets, several overlapping entries, a map to pick the location and a force mode for continuous operation |
| Soundscape pipeline | Configuration of the acoustic pipeline |
| Web shell | A terminal in the browser, for those who need one |
The Live page reads the station's own MQTT broker over WebSockets at /mqtt (tsOS-base serves it behind the login) and draws what arrives. tsconfig itself subscribes to nothing and stores nothing: the last five minutes are held in the browser and cleared when the page or tab is left. There is a tab for each service that is available, and a time window selector (30 seconds to 5 minutes). Radiotracking has three panels:
- Signal Power: matched signals in dBW, or calibrated in dBm
- Frequency Usage: the frequency of the matched signals over time
- Signal Detail: the raw signals of every SDR, with the value on the vertical axis to choose (started with a button, as they are many)
Dots are drawn at the signal's own Time on a sliding 24-hour axis, so a station clock that is off or detections that arrive late show up as signals away from "now". A dot's size is the signal's duration, its colour the SDR's compass direction (0 blue, 1 yellow, 2 red, 3 green), and the axes follow the radiotracking configuration. Details: docs/frontend.md.
A separate service (tsconfig-ble) that exposes the API as GATT services, so smartphones and tablets can configure a station without any network: encrypted link plus the operator login, standard tools work (nRF Connect, LightBlue, gatttool), and the station advertises under its hostname. See below and docs/ble-protocol.md.
A second separate service, tstelemetry (app/telemetry), publishes the station's health: system, temperatures, disks, PMIC, throttling, clocks, voltages, WittyPi, network interfaces, modem, config files and release. Each reading goes out over MQTT as CSV, JSON and CBOR (<host>/tsconfig/<collector>/<format>) and into one CSV file per collector and day under /data. Each kind of data is an explicit collector module; /etc/tsconfig/tstelemetry.yml (installed with the image from configs/tstelemetry.yml, not operator-editable) only switches them on and off and sets intervals. Readings taken before the clock is synced are re-timed before they reach the files. tstelemetry --once -v prints one round. Topics and columns: tsOS-base docs/telemetry.md.
In tracker mode (on a station) the login is the pi password, checked through PAM. In server mode it runs centrally, keeps configuration per group of stations to deploy to them, and logs in through OIDC.
- Python 3.12 or higher (tsOS runs 3.13)
- Linux-based system (tested on Raspberry Pi OS / tsOS)
- Appropriate system permissions for system control features
-
Install PDM (Python Dependency Manager):
curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 - -
Install dependencies:
pdm install -G tracker # on a station (or for development) pdm install -G server # central server mode
The core dependencies are what the web service needs in both modes. Extras:
trackeradds system status, the web shell and the telemetry daemon (psutil, websockets, cbor2, paho-mqtt),serverthe OIDC login (authlib, itsdangerous),blethe BLE gateway (dbus-python, pygobject, cbor2). The tests needtrackerandserverplus thedevgroup. Without PDM:pip install -e ".[tracker]"(or".[server]",".[ble]") andpip install --group dev. On a tsOS image everything comes from Debian packages (seetsOS-base.Pifile). -
Run the application:
# Using uvicorn directly (recommended) python3 -m uvicorn app.main:app --reload # Or using pdm pdm run uvicorn app.main:app --reload
The application will be available at http://localhost:8000
The application supports configurable logging levels optimized for journald consumption:
# Default (WARNING level - only warnings and errors)
python3 -m uvicorn app.main:app --reload
# INFO level (shows operational events)
LOG_LEVEL=INFO python3 -m uvicorn app.main:app --reload
# DEBUG level (shows detailed diagnostics)
LOG_LEVEL=DEBUG python3 -m uvicorn app.main:app --reloadLog Levels:
- ERROR: an operation failed and needs attention
- WARNING (default): unexpected but handled
- INFO: one-off events only: startup, login/logout, config written, service restarted, deployment, shell/BLE session lifecycle. Never per-request or periodic.
- DEBUG: everything chatty: request tracing, token refresh, polling, D-Bus communication, HTTP access logs
Note: HTTP access logs (uvicorn.access) and HTTP client logs (httpx, httpcore) are only emitted at DEBUG, so INFO stays readable while the web UI polls the API. Set ACCESS_LOG_LEVEL (e.g. INFO) to override this independently of LOG_LEVEL. Other libraries (uvicorn, fastapi, dbus) follow LOG_LEVEL.
If you need to run tsconfig at a subpath (e.g., http://example.com/tsconfig/), you can configure the base URL using the TSCONFIG_BASE_URL environment variable:
# Run at a subpath
export TSCONFIG_BASE_URL="/tsconfig"
python3 -m uvicorn app.main:app --reload
# Or inline
TSCONFIG_BASE_URL="/tsconfig" python3 -m uvicorn app.main:app --reloadThe default value is / (root path). When configured, all URLs (API endpoints, static files, and navigation) will respect the base path.
Nginx reverse proxy example:
location /tsconfig/ {
proxy_pass http://localhost:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}The application can control systemd services through the web interface. The default monitored services include:
- chrony - Time synchronization service
- tsschedule - tsschedule daemon for power management
- wg-quick@wireguard - WireGuard VPN connection
- mosquitto - MQTT broker for data communication
- tstelemetry - device health over MQTT and into daily CSV files (
app/telemetry, see below)
Services are configured in configs/systemd_services.yml:
# Systemd services configuration
services:
- name: chrony
expert: false # Always visible
- name: mosquitto
expert: true # Only visible in expert modeSystem control features require appropriate permissions. The application must be run with sufficient privileges to execute system control operations such as:
- Starting, stopping, and restarting systemd services
- Initiating system reboots
- Modifying network configurations
- Managing system files
- Navigate to the Schedule tab
- Set your location using the interactive map or coordinates
- Create schedule entries with start/stop times
- Use astronomical events or specific times
- Download or save configuration
- Go to the Radio Tracking tab
- Configure RTL-SDR devices and frequency settings
- Set signal analysis parameters
- Configure data publishing options
- Enable expert mode for advanced settings
- Visit the Status tab (default page)
- View real-time system information
- Monitor service status and control services
- Stream service logs for troubleshooting
- Use the reboot button for system restarts
The application provides REST APIs for all functionality:
GET /api/system-status- System monitoring dataGET/PUT /api/schedule- Schedule configurationGET/PUT /api/radiotracking- Radio tracking configurationGET /api/systemd/services- Service statusPOST /api/systemd/action- Service controlPOST /api/systemd/reboot- System rebootGET /api/systemd/logs/{service}- Service log streaming
The BLE gateway provides wireless access to the tsconfig API via Bluetooth GATT, enabling mobile devices and other BLE clients to configure and monitor sensor stations without network infrastructure.
- Wireless Access: All system, systemd, and config endpoints accessible via BLE
- Mobile Ready: Perfect for field configuration from smartphones/tablets
- Standalone: Runs independently from the main web application
- Secure: Encrypted link and operator login (
pipassword) required for writes, logs and config - Auto-discovery: Advertises using system hostname
- Standard Compliance: Works with any BLE GATT client
-
Install BLE dependencies:
pdm install -G ble
-
Verify Bluetooth adapter:
bluetoothctl show
-
Run the BLE gateway:
# Basic usage (requires appropriate permissions) python3 -m app.ble_gateway # With custom API URL python3 -m app.ble_gateway --api-url http://192.168.1.100:8000 # Disable link encryption and the operator login (insecure) python3 -m app.ble_gateway --no-auth # Enable debug logging python3 -m app.ble_gateway --verbose
-
Install as systemd service (recommended):
cp tsconfig-ble.service /etc/systemd/system/ systemctl daemon-reload systemctl enable tsconfig-ble.service systemctl start tsconfig-ble.service
The BLE gateway exposes three GATT services, each with multiple characteristics that map 1:1 to REST API endpoints.
Base UUID: 0000XXXX-7473-4f53-636f-6e6669672121
7473= "ts" (ASCII hex)4f53= "OS" (ASCII hex)636f6e666967= "config" (ASCII hex)
Read-only characteristics for system information (open: no login needed, so devices can be identified), plus the operator login:
| Characteristic | UUID | REST Endpoint |
|---|---|---|
| System Status | 00001001-... |
GET /api/system-status |
| Server Mode Info | 00001002-... |
GET /api/server-mode |
| Timedatectl Status | 00001003-... |
GET /api/timedatectl-status |
| Available Services | 00001004-... |
GET /api/available-services |
| Authenticate (write) | 00001005-... |
operator login, see Security |
Service management (operator login required):
| Characteristic | UUID | REST Endpoint |
|---|---|---|
| Services List | 00002001-... |
GET /api/systemd/services |
| Service Action | 00002002-... |
POST /api/systemd/action |
| System Reboot | 00002003-... |
POST /api/systemd/reboot |
| Service Logs | 00002004-... |
GET /api/systemd/logs/{service}?follow=false |
Configuration file upload and download (operator login required):
| Characteristic | UUID | REST Endpoint |
|---|---|---|
| Config Upload | 00003001-... |
POST /api/configs/update |
| Zip Upload | 00003002-... |
POST /api/configs.zip |
Note: Download endpoints (GET /api/configs/{filename} and GET /api/configs.zip) are available via the REST API but not exposed as BLE characteristics.
Reads return a small CBOR metadata record; the data itself follows as notifications of at most 512 bytes. Writes carry a JSON request (split over several writes when it is longer than the MTU) and are answered by notifications. The full reference for client developers, including request and response formats, framing, limits and error codes, is in docs/ble-protocol.md. test_ble_client.py is a working example client (bleak).
A redesigned protocol (framed, encrypted, password-authenticated key exchange, no pairing) is described in docs/ble-protocol-v2-design.md. It is a proposal and not implemented.
Everything except the System Service's status characteristics requires two things:
- An encrypted link. The gated characteristics (and the login itself) are exposed with BlueZ's
encrypt-*flags, so your OS starts LE pairing the first time you touch one. The gateway registers aNoInputNoOutputpairing agent: pairing is "Just Works" - no PIN or confirmation on the device. This keeps the login from being sniffable; it does not protect against an active man-in-the-middle during pairing (the device has no display to compare a passkey on). - The operator login, per connection. Write
{"password": "<pi's password>"}to the Authenticate characteristic (00001005-...). The gateway checks it against PAM (the same check as the web login) and notifies{"authenticated": true}, or an error: code401for a wrong password,429with aretry_after(seconds) while it is backing off after repeated failures. Until a connection has logged in, gated characteristics answer{"error": "Authentication required", "code": 401}and do nothing. The login ends on disconnect, after 8 hours, or when the gateway restarts.
Instead of the password, {"hash": "$6$..."} logs in as pi with pi's password hash, i.e. the line in the userconf.txt of the config bundle applied to the device; it works while the password has not been changed since (see docs/ble-protocol.md). To read /etc/shadow the gateway runs as its own user tsconfig-ble in the shadow group.
The default pi password is natur until a config bundle (userconf.txt) changes it. Staff can log in as root instead: add "username": "root" to the login request (works only if the device has a root password).
- Disable authentication:
--no-auth(alias--no-pairing) turns off both the encryption requirement and the login (not recommended for production) test_ble_client.pylogs in with--password(defaultnatur), or first with pi's hash from--userconf FILE/--hash HASH
- Linux with BlueZ 5.50+
- Bluetooth hardware adapter
- Root or CAP_NET_ADMIN capability
- Running tsconfig HTTP API
- D-Bus system bus
Device not appearing in scans:
- Ensure Bluetooth is powered:
bluetoothctl power on - Check gateway is running:
systemctl status tsconfig-ble - Try making adapter discoverable:
bluetoothctl discoverable on
Intermittent device detection (test_ble_client.py):
- BLE advertising is intermittent - the test client now includes automatic retry logic
- Use
--retries 5for more reliable detection in difficult environments - Use
--timeout 15to increase scan time per attempt - See BLE_TROUBLESHOOTING.md for detailed troubleshooting guide
Permission denied errors:
- BLE operations require appropriate privileges (root or CAP_NET_ADMIN capability)
- Grant capabilities:
setcap cap_net_admin+eip $(which python3)
Cannot write to characteristics:
- Pair the device first (your OS normally starts this on first access) and log in through the Authenticate characteristic
- Or disable authentication:
--no-authflag
API connection errors:
- Verify tsconfig is running:
curl http://localhost:8000/api/server-mode - Check API URL:
--api-url http://correct-ip:8000
- Backend: FastAPI with async/await support
- Frontend: Bootstrap 5 with Alpine.js for reactivity
- Configuration: YAML and INI file formats
- System Integration: systemd, psutil for system monitoring
- Mapping: Leaflet with Mapbox satellite imagery
tsconfig/
├── app/
│ ├── main.py # FastAPI application
│ ├── routers/ # API endpoints (schedule, radiotracking, systemd, network, ...)
│ ├── configs/ # Configuration schemas
│ ├── ble_gateway.py # BLE gateway
│ ├── telemetry/ # tstelemetry daemon and its collectors
│ ├── system/ # Readouts shared by the API and tstelemetry (modem)
│ ├── templates/ # Jinja pages and partials
│ └── static/ # Alpine.js frontend (see docs/frontend.md), vendored libraries
├── configs/ # Default configuration files
├── docs/ # Frontend structure, BLE protocol
└── tests/ # pytest (backend) and node:test (frontend, tests/js)
ruff check . # lint (pinned in CI)
python -m pytest -q # backend
node --test 'tests/js/*.test.mjs' # frontend (Node 24)CI (Lint and test) runs all three, and builds the Docker image.
This project is part of the trackIT Systems ecosystem. For development:
- Follow PEP 8 coding standards
- Use type hints for all functions
- Add docstrings for public APIs
- Test on target hardware (Raspberry Pi/tsOS)
© 2025 trackIT Systems. All rights reserved.
For support and documentation:
- Website: https://trackit.systems
- Email: info@trackit.systems
Compatibility: tsOS, Raspberry Pi OS, Linux systems with systemd