diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d2d6db --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +# 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`, and `09`. +- 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: + +1. `OBD2App` in `obd2_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. + +2. `OBD2Interface` in `obd2_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.conf` and hot-reloads TTL overrides. + +`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: + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +## Running + +Run against a real OBD adapter: + +```bash +python3 obd2_tui.py +``` + +Run in simulated mode: + +```bash +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: + +```bash +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`: quit +- `b`: toggle metric card border style +- `e`: focus the TTL editor +- `escape`: return focus to the command table +- `left` / `right`: previous or next OBD mode +- `shift+up` / `shift+down`: jump to top or bottom of the table +- `1`, `2`, `3`, `4`, `6`, `7`, `9`: switch directly to that OBD mode + +## TTL Configuration + +`command_ttl.conf` uses the format: + +```text +COMMAND_NAME,ttl_ms +``` + +Examples: + +```text +SPEED,10 +RPM,10 +FUEL_LEVEL,5000 +GET_DTC,86400000 +``` + +Notes: + +- TTL values are stored in milliseconds. +- A value of `0` effectively 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: + +```bash +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 `RichLog` panel through a custom logging handler. +- The codebase uses `Report` as the shared mutable telemetry state for the UI. +- `command_ttl.conf` is large because it predefines cache behavior for many standard OBD commands. +- `obd2_interface.py` also contains a non-TUI `__main__` path that continuously logs report snapshots, but the primary entry point for normal use is `obd2_tui.py`.