Introducing Odometer
Cars are very good at hiding their real cost.
Fuel is obvious. Insurance is obvious when the renewal lands. Tax, MOT, tyres, servicing, parking, repairs, finance, and all the small bits in between are much easier to forget.
That is how the mental number drifts away from the real one.
I wanted something that could answer a simple question:
What does this car actually cost me?
So I built Odometer.
What Odometer is
Odometer is a local-first CLI and TUI application for tracking vehicle ownership costs.
It records vehicles, expenses, and fuel logs. It calculates total spend, fuel spend, non-fuel spend, running cost per mile, ownership cost per mile, UK MPG, monthly summaries, annual summaries, rolling averages, and category breakdowns.
It stores everything in SQLite on your machine.
No cloud account. No subscription. No dashboard trying to become a product strategy. Just your data, locally, in a boring database.
That was deliberate.
Why build this?
I have tried tracking car costs in spreadsheets before. It works for a while. Then the sheet grows just enough logic to become annoying.
Fuel needs mileage. Mileage needs an initial baseline. MPG needs full-to-full fills if you want it to mean anything. Fuel expenses can double count if you are also recording fuel logs. Purchase price changes the ownership cost but should not be mixed into the normal running cost.
None of that is especially complicated, but it is enough to make the spreadsheet feel like a small app pretending not to be one.
Odometer is that small app.
The shape of it
The first release is intentionally practical:
- manage multiple vehicles by registration
- log expenses by category
- log fuel with litres, mileage, station, and full or partial fills
- calculate UK MPG from full-to-full fuel logs
- calculate running and ownership cost per mile
- show monthly, annual, and category summaries
- use a terminal UI when I want to browse the data instead of typing commands
- run locally with Poetry or Docker
The CLI is there because I like direct tools. The TUI is there because tables, summaries, and charts are easier to scan when they have a bit of structure.
Both use the same service layer underneath. That matters because the rules about fuel, mileage, money, and vehicle lifecycle should not depend on which interface I happen to be using.
Local-first by default
This is the part I care about most.
Vehicle ownership data is personal. It says where you spend money, when repairs happen, what mileage you are doing, and sometimes where you buy fuel. I do not need that in someone else’s system for this use case.
SQLite is enough.
The app stores one local database in the normal application data directory for
the operating system. If I want a portable or test database, I can set
ODOMETER_DB_PATH.
That keeps the whole thing understandable. Backing it up is backing up one file. Moving it is moving one file. Debugging it is opening one file.
That is the kind of simplicity I want from a personal tool.
The boring engineering choices
Odometer is built with Python, Typer, Textual, SQLModel, Rich, and SQLite.
That stack is not exotic. It is also the point. The project does not need a web app, a queue, a cloud database, or a login system to answer its main question.
The layers are deliberately plain:
- models describe the stored data
- repositories handle persistence
- services own the business rules
- the CLI and TUI present the same data in different ways
That gives the project enough structure to grow without turning it into a platform.
v0.1.0
The first release is v0.1.0, and it is GitHub-only while I test the release
process and the shape of the tool.
There is no PyPI package yet.
For now, the release workflow builds the distribution artifacts, creates checksums, and publishes them on GitHub. That is enough for the current stage. The next step is using it properly, finding the awkward edges, and deciding what deserves to become stable.
I am treating this as early software. Useful, but not finished.
What I want from it
I do not want Odometer to become another place to fiddle with data for the sake of it.
I want it to make the cost visible.
If a car is costing more than expected, I want to see where. If fuel economy is getting worse, I want to notice. If maintenance is clustering, I want that to be obvious. If the total cost per mile is uncomfortable, I want the number rather than a vague feeling.
That is the whole point.
Better information. Fewer guesses.
The project is on GitHub