OBD2 Terminal Dashboard
This repository contains a Textual-based terminal dashboard for querying an OBD-II adapter and presenting live vehicle telemetry alongside raw command output.
scanbus.py is intentionally excluded from this README.
What It Does
- Displays live dashboard metrics for speed, RPM, fuel level, oil temperature, and coolant temperature.
- Renders a command table for OBD modes
01,02,03,04,06,07, and09. - Polls commands asynchronously with a global queries-per-second limit.
- Deduplicates concurrent reads of the same OBD command.
- Caches command results using a default TTL plus per-command overrides from
command_ttl.conf. - Supports a simulated mode for UI development without a physical adapter.
- Streams application logs inside the terminal UI.
Repository Layout
obd2_tui.py: main Textual app, CLI entry point, keyboard bindings, command polling, and inline TTL editing.obd2_interface.py: async OBD abstraction, connection lifecycle, rate limiting, caching, TTL config reloads, and simulation support.models.py: Pydantic models for the live telemetry report and scan definitions.ui.css: Textual stylesheet for the dashboard, command table, and log panel.command_ttl.conf: command-specific cache TTL overrides in milliseconds.test.py: unit tests for report normalization, interface behavior, caching, TTL updates, simulation, and UI helpers.requirements.txt: Python dependencies.
Architecture
The application has two main layers:
-
OBD2Appinobd2_tui.py- Builds the terminal UI with Textual.
- Polls a small set of key telemetry commands continuously for the metric cards.
- Polls the currently visible rows in the selected OBD mode table.
- Lets the user edit a selected command's cache TTL from the UI.
-
OBD2Interfaceinobd2_interface.py- Connects to the adapter through
python-OBD. - Serializes outbound queries through a worker queue.
- Enforces a global rate limit.
- Maintains a TTL cache keyed by command name.
- Watches
command_ttl.confand hot-reloads TTL overrides.
- Connects to the adapter through
SimulatedOBD2Interface swaps in a fake connection that generates dynamic values for development and testing.
Requirements
- Python 3.11+ recommended
- An ELM327-compatible OBD-II adapter for real vehicle data
- Terminal support suitable for a Textual application
Install dependencies:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Running
Run against a real OBD adapter:
python3 obd2_tui.py
Run in simulated mode:
python3 obd2_tui.py --simulated
Optional flags:
--qps: maximum OBD queries per second across the app. Default:10.0--ttl-config: path to the TTL override file. Default:command_ttl.conf
Example:
python3 obd2_tui.py --simulated --qps 25 --ttl-config command_ttl.conf
UI Behavior
The dashboard is composed of:
- Metric cards for the five primary telemetry values
- A command table for the selected OBD mode
- A TTL editor bound to the currently highlighted command
- An in-app log panel
The polling strategy is selective:
- Core telemetry commands are always queried so the metric cards stay current.
- The mode table queries only commands that are currently visible in the viewport.
- Duplicate requests for the same command are coalesced through the interface queue and cache.
Keyboard Controls
q: quitb: toggle metric card border stylee: focus the TTL editorescape: return focus to the command tableleft/right: previous or next OBD modeshift+up/shift+down: jump to top or bottom of the table1,2,3,4,6,7,9: switch directly to that OBD mode
TTL Configuration
command_ttl.conf uses the format:
COMMAND_NAME,ttl_ms
Examples:
SPEED,10
RPM,10
FUEL_LEVEL,5000
GET_DTC,86400000
Notes:
- TTL values are stored in milliseconds.
- A value of
0effectively disables caching for that command. - The app reloads the file automatically while running.
- Editing a TTL in the UI writes the updated value back to the configured TTL file.
Testing
Run the unit tests with:
python3 -m unittest -v test.py
The tests cover:
- Report value normalization
- Query rate limiting
- Cache hit and cache expiry behavior
- TTL override parsing and persistence
- Simulated connection behavior
- Display formatting and duration parsing helpers
Current Environment Note
In the current workspace environment, python3 -m unittest -v test.py fails immediately because pydantic is not installed. After installing dependencies from requirements.txt, the test command should be rerun.
Development Notes
- Logging is routed into the Textual
RichLogpanel through a custom logging handler. - The codebase uses
Reportas the shared mutable telemetry state for the UI. command_ttl.confis large because it predefines cache behavior for many standard OBD commands.obd2_interface.pyalso contains a non-TUI__main__path that continuously logs report snapshots, but the primary entry point for normal use isobd2_tui.py.