tescmd 0.3.1__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tescmd/__init__.py +1 -1
- tescmd/auth/oauth.py +10 -0
- tescmd/cli/auth.py +212 -145
- tescmd/cli/key.py +6 -7
- tescmd/cli/setup.py +134 -56
- tescmd/deploy/github_pages.py +13 -2
- tescmd/deploy/tailscale_serve.py +96 -8
- tescmd/openclaw/dispatcher.py +9 -2
- tescmd/openclaw/gateway.py +13 -0
- tescmd/telemetry/tailscale.py +78 -16
- tescmd-0.4.0.dist-info/METADATA +300 -0
- {tescmd-0.3.1.dist-info → tescmd-0.4.0.dist-info}/RECORD +15 -15
- tescmd-0.3.1.dist-info/METADATA +0 -543
- {tescmd-0.3.1.dist-info → tescmd-0.4.0.dist-info}/WHEEL +0 -0
- {tescmd-0.3.1.dist-info → tescmd-0.4.0.dist-info}/entry_points.txt +0 -0
- {tescmd-0.3.1.dist-info → tescmd-0.4.0.dist-info}/licenses/LICENSE +0 -0
tescmd/telemetry/tailscale.py
CHANGED
|
@@ -112,27 +112,86 @@ class TailscaleManager:
|
|
|
112
112
|
# Serve management (static file hosting)
|
|
113
113
|
# ------------------------------------------------------------------
|
|
114
114
|
|
|
115
|
-
async def start_serve(
|
|
115
|
+
async def start_serve(
|
|
116
|
+
self,
|
|
117
|
+
path: str,
|
|
118
|
+
target: str | Path,
|
|
119
|
+
*,
|
|
120
|
+
port: int = 443,
|
|
121
|
+
funnel: bool = False,
|
|
122
|
+
) -> None:
|
|
116
123
|
"""Serve a local directory at a URL path prefix.
|
|
117
124
|
|
|
118
|
-
Runs: ``tailscale serve --bg --set-path <path> <target>``
|
|
125
|
+
Runs: ``tailscale serve --bg [--https=<port>] [--funnel] --set-path <path> <target>``
|
|
126
|
+
|
|
127
|
+
When *port* differs from 443 the ``--https=<port>`` flag is added so
|
|
128
|
+
Tailscale listens on the requested HTTPS port.
|
|
119
129
|
|
|
120
130
|
Args:
|
|
121
131
|
path: URL path prefix (e.g. ``/.well-known/``).
|
|
122
132
|
target: Local directory to serve.
|
|
133
|
+
port: HTTPS port to serve on (default ``443``).
|
|
134
|
+
funnel: Also enable Funnel (public access) for this handler.
|
|
123
135
|
"""
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
|
|
128
|
-
"--
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
136
|
+
cmd: list[str] = ["tailscale", "serve", "--bg"]
|
|
137
|
+
if port != 443:
|
|
138
|
+
cmd.append(f"--https={port}")
|
|
139
|
+
if funnel:
|
|
140
|
+
cmd.append("--funnel")
|
|
141
|
+
cmd.extend(["--set-path", path, str(target)])
|
|
142
|
+
|
|
143
|
+
returncode, stdout, stderr = await self._run(*cmd)
|
|
132
144
|
if returncode != 0:
|
|
133
145
|
msg = stderr.strip() or stdout.strip()
|
|
134
146
|
raise TailscaleError(f"Failed to start Tailscale serve: {msg}")
|
|
135
|
-
logger.info("Tailscale serve started: %s -> %s", path, target)
|
|
147
|
+
logger.info("Tailscale serve started: %s -> %s (port %d)", path, target, port)
|
|
148
|
+
|
|
149
|
+
async def start_proxy(self, local_port: int, *, https_port: int = 443) -> None:
|
|
150
|
+
"""Reverse-proxy an HTTPS port to a local HTTP server.
|
|
151
|
+
|
|
152
|
+
Runs: ``tailscale serve --bg [--https=<https_port>] http://127.0.0.1:<local_port>``
|
|
153
|
+
|
|
154
|
+
Unlike :meth:`start_serve` (which serves static files via
|
|
155
|
+
``--set-path``), this sets up a reverse proxy so Tailscale
|
|
156
|
+
forwards traffic to a local HTTP server.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
local_port: Port of the local HTTP server to proxy to.
|
|
160
|
+
https_port: Public-facing HTTPS port (default ``443``).
|
|
161
|
+
"""
|
|
162
|
+
cmd: list[str] = ["tailscale", "serve", "--bg"]
|
|
163
|
+
if https_port != 443:
|
|
164
|
+
cmd.append(f"--https={https_port}")
|
|
165
|
+
cmd.append(f"http://127.0.0.1:{local_port}")
|
|
166
|
+
|
|
167
|
+
returncode, stdout, stderr = await self._run(*cmd)
|
|
168
|
+
if returncode != 0:
|
|
169
|
+
msg = stderr.strip() or stdout.strip()
|
|
170
|
+
raise TailscaleError(f"Failed to start Tailscale proxy: {msg}")
|
|
171
|
+
logger.info(
|
|
172
|
+
"Tailscale proxy started: https port %d -> http://127.0.0.1:%d",
|
|
173
|
+
https_port,
|
|
174
|
+
local_port,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
async def stop_proxy(self, *, https_port: int = 443) -> None:
|
|
178
|
+
"""Remove a reverse-proxy serve configuration for an HTTPS port.
|
|
179
|
+
|
|
180
|
+
Runs: ``tailscale serve --bg [--https=<https_port>] off``
|
|
181
|
+
"""
|
|
182
|
+
cmd: list[str] = ["tailscale", "serve", "--bg"]
|
|
183
|
+
if https_port != 443:
|
|
184
|
+
cmd.append(f"--https={https_port}")
|
|
185
|
+
cmd.append("off")
|
|
186
|
+
|
|
187
|
+
returncode, stdout, stderr = await self._run(*cmd)
|
|
188
|
+
if returncode != 0:
|
|
189
|
+
logger.warning(
|
|
190
|
+
"Failed to stop Tailscale proxy on port %d (may already be stopped): %s",
|
|
191
|
+
https_port,
|
|
192
|
+
stderr.strip() or stdout.strip(),
|
|
193
|
+
)
|
|
194
|
+
logger.info("Tailscale proxy stopped for HTTPS port %d", https_port)
|
|
136
195
|
|
|
137
196
|
async def stop_serve(self, path: str) -> None:
|
|
138
197
|
"""Remove a serve handler for a path.
|
|
@@ -155,21 +214,24 @@ class TailscaleManager:
|
|
|
155
214
|
)
|
|
156
215
|
logger.info("Tailscale serve stopped for path: %s", path)
|
|
157
216
|
|
|
158
|
-
async def enable_funnel(self) -> None:
|
|
159
|
-
"""Enable Funnel on port
|
|
217
|
+
async def enable_funnel(self, port: int = 443) -> None:
|
|
218
|
+
"""Enable Funnel on the given *port* (expose all serve handlers publicly).
|
|
160
219
|
|
|
161
|
-
Runs: ``tailscale funnel --bg
|
|
220
|
+
Runs: ``tailscale funnel --bg <port>``
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
port: HTTPS port to expose via Funnel (default ``443``).
|
|
162
224
|
"""
|
|
163
225
|
returncode, stdout, stderr = await self._run(
|
|
164
226
|
"tailscale",
|
|
165
227
|
"funnel",
|
|
166
228
|
"--bg",
|
|
167
|
-
|
|
229
|
+
str(port),
|
|
168
230
|
)
|
|
169
231
|
if returncode != 0:
|
|
170
232
|
msg = stderr.strip() or stdout.strip()
|
|
171
233
|
raise TailscaleError(f"Failed to enable Tailscale Funnel: {msg}")
|
|
172
|
-
logger.info("Tailscale Funnel enabled on port
|
|
234
|
+
logger.info("Tailscale Funnel enabled on port %d", port)
|
|
173
235
|
|
|
174
236
|
# ------------------------------------------------------------------
|
|
175
237
|
# Funnel management (port proxying for telemetry)
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tescmd
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A Python CLI for querying and controlling Tesla vehicles via the Fleet API
|
|
5
|
+
Project-URL: Homepage, https://github.com/oceanswave/tescmd
|
|
6
|
+
Project-URL: Repository, https://github.com/oceanswave/tescmd
|
|
7
|
+
Project-URL: Issues, https://github.com/oceanswave/tescmd/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/oceanswave/tescmd#readme
|
|
9
|
+
Author: oceanswave
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cli,ev,fleet-api,tesla,vehicle
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: click>=8.1
|
|
26
|
+
Requires-Dist: cryptography>=42.0
|
|
27
|
+
Requires-Dist: httpx>=0.27
|
|
28
|
+
Requires-Dist: keyring>=25.0
|
|
29
|
+
Requires-Dist: mcp>=1.0
|
|
30
|
+
Requires-Dist: protobuf>=5.29
|
|
31
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
32
|
+
Requires-Dist: pydantic>=2.0
|
|
33
|
+
Requires-Dist: python-dotenv>=1.0
|
|
34
|
+
Requires-Dist: rich>=13.0
|
|
35
|
+
Requires-Dist: starlette>=0.37
|
|
36
|
+
Requires-Dist: textual>=1.0
|
|
37
|
+
Requires-Dist: uvicorn>=0.30
|
|
38
|
+
Requires-Dist: websockets>=14.0
|
|
39
|
+
Provides-Extra: ble
|
|
40
|
+
Requires-Dist: bleak>=0.22; extra == 'ble'
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: grpcio-tools>=1.68; extra == 'dev'
|
|
44
|
+
Requires-Dist: mypy-protobuf>=3.6; extra == 'dev'
|
|
45
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest-httpx>=0.34; extra == 'dev'
|
|
49
|
+
Requires-Dist: pytest-xdist>=3.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<img src="images/tescmd_header.jpeg" alt="tescmd — Python CLI for Tesla Fleet API" width="100%">
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
# tescmd
|
|
59
|
+
|
|
60
|
+
<p align="center">
|
|
61
|
+
<a href="https://pypi.org/project/tescmd/"><img src="https://img.shields.io/pypi/v/tescmd" alt="PyPI"></a>
|
|
62
|
+
<a href="https://pypi.org/project/tescmd/"><img src="https://img.shields.io/pypi/pyversions/tescmd" alt="Python"></a>
|
|
63
|
+
<a href="https://github.com/oceanswave/tescmd/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/oceanswave/tescmd/test.yml?branch=main&label=build" alt="Build"></a>
|
|
64
|
+
<a href="LICENSE"><img src="https://img.shields.io/github/license/oceanswave/tescmd" alt="License"></a>
|
|
65
|
+
<a href="https://github.com/oceanswave/tescmd/releases"><img src="https://img.shields.io/github/v/release/oceanswave/tescmd" alt="GitHub Release"></a>
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<strong>The complete Python CLI for Tesla's Fleet API — built for humans and AI agents alike.</strong>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<p align="center">
|
|
73
|
+
Check your battery. Lock your doors. Stream live telemetry. Let Claude control your car.<br>
|
|
74
|
+
Two commands to install. One wizard to set up. Every API endpoint at your fingertips.
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install tescmd
|
|
83
|
+
tescmd setup
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The setup wizard handles everything — Tesla Developer app creation, key generation, public key hosting, Fleet API registration, OAuth2 authentication, and vehicle key enrollment. Then you're ready:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
tescmd charge status # Battery and charging state
|
|
90
|
+
tescmd climate on --wake # Turn on climate (wakes if asleep)
|
|
91
|
+
tescmd security lock --wake # Lock the car
|
|
92
|
+
tescmd nav waypoints "Home" "Work" # Multi-stop navigation
|
|
93
|
+
tescmd serve 5YJ3... # Launch the live dashboard
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## See It in Action
|
|
99
|
+
|
|
100
|
+
### Live TUI Dashboard
|
|
101
|
+
|
|
102
|
+
`tescmd serve` launches a full-screen terminal dashboard with real-time telemetry, MCP server status, tunnel info, and connection metrics — powered by Textual.
|
|
103
|
+
|
|
104
|
+
<p align="center">
|
|
105
|
+
<img src="images/tescmd_serve.png" alt="tescmd serve — live TUI dashboard" width="700">
|
|
106
|
+
</p>
|
|
107
|
+
|
|
108
|
+
### AI Agent Integration
|
|
109
|
+
|
|
110
|
+
Every command doubles as an MCP tool. Claude Desktop, Claude Code, and other agent frameworks can query your vehicle, send commands, and react to telemetry — all through structured JSON with built-in cost protection.
|
|
111
|
+
|
|
112
|
+
<p align="center">
|
|
113
|
+
<img src="images/tescmd_mcp.png" alt="tescmd MCP server — Claude Desktop integration" width="700">
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
### Rich Terminal Output
|
|
117
|
+
|
|
118
|
+
Formatted tables in your terminal, structured JSON when piped — tescmd auto-detects the right output for the context.
|
|
119
|
+
|
|
120
|
+
<p align="center">
|
|
121
|
+
<img src="images/tescmd_waypoints.png" alt="tescmd nav waypoints" width="500">
|
|
122
|
+
</p>
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## What You Get
|
|
127
|
+
|
|
128
|
+
### Query & Control
|
|
129
|
+
|
|
130
|
+
Full read/write access to Tesla's Fleet API: battery, charge, climate, locks, trunks, windows, sentry, navigation, media, speed limits, PINs, Powerwalls, and more. Every read command is cached with smart TTLs — bots can call tescmd as often as they want and only pay for the first request.
|
|
131
|
+
|
|
132
|
+
### Fleet Telemetry Streaming
|
|
133
|
+
|
|
134
|
+
Your vehicle pushes data directly to your machine via Tailscale Funnel — no polling, no per-request charges. Choose from field presets (`driving`, `charging`, `all`) or subscribe to 120+ individual fields. Sessions produce a wide-format CSV log by default.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
tescmd serve 5YJ3... --fields driving # Speed, location, power
|
|
138
|
+
tescmd serve 5YJ3... --fields all # Everything
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### MCP Server for AI Agents
|
|
142
|
+
|
|
143
|
+
`tescmd serve` exposes every command as an MCP tool with OAuth 2.1 authentication. Agents get deterministic JSON output, meaningful exit codes, and a `--wake` opt-in flag so they never trigger billable wake calls by accident.
|
|
144
|
+
|
|
145
|
+
### OpenClaw Bridge
|
|
146
|
+
|
|
147
|
+
Stream filtered telemetry to an [OpenClaw](https://openclaw.ai/) Gateway with per-field delta and throttle filtering. Bots on the gateway can send commands back — lock doors, start charging, set climate — through bidirectional dispatch.
|
|
148
|
+
|
|
149
|
+
### Trigger Subscriptions
|
|
150
|
+
|
|
151
|
+
Register conditions on any telemetry field — battery below 20%, speed above 80, location enters a geofence — and get notified via OpenClaw push events or MCP polling. Supports one-shot and persistent modes with cooldown.
|
|
152
|
+
|
|
153
|
+
### Signed Vehicle Commands
|
|
154
|
+
|
|
155
|
+
tescmd implements the [Vehicle Command Protocol](https://github.com/teslamotors/vehicle-command) with ECDH session management and HMAC-SHA256 signing. Once your key is enrolled, commands are signed transparently — no agent-side crypto needed.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Cost Protection Built In
|
|
160
|
+
|
|
161
|
+
Tesla's Fleet API is pay-per-use. A naive polling script can generate hundreds of dollars in monthly charges from a single vehicle. tescmd implements four layers of defense:
|
|
162
|
+
|
|
163
|
+
| Layer | What it does |
|
|
164
|
+
|---|---|
|
|
165
|
+
| **Tiered caching** | Specs cached 1h, fleet lists 5m, standard queries 1m, location 30s |
|
|
166
|
+
| **Wake confirmation** | Prompts before billable wake calls; `--wake` flag for scripts |
|
|
167
|
+
| **Smart wake state** | Tracks recent wake confirmations, skips redundant attempts |
|
|
168
|
+
| **Write invalidation** | Write commands auto-invalidate the relevant cache scope |
|
|
169
|
+
|
|
170
|
+
Streaming telemetry via `tescmd serve` replaces polling entirely — flat cost regardless of data volume. See [API Costs](docs/api-costs.md) for the full breakdown.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Commands
|
|
175
|
+
|
|
176
|
+
| Group | Description |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `setup` | Interactive first-run wizard |
|
|
179
|
+
| `auth` | OAuth2 login, logout, token management, export/import |
|
|
180
|
+
| `vehicle` | State queries, wake, rename, telemetry streaming, fleet status |
|
|
181
|
+
| `charge` | Charge control, scheduling, departure, fleet management |
|
|
182
|
+
| `climate` | HVAC, seats, steering wheel, bioweapon defense, overheat protection |
|
|
183
|
+
| `security` | Lock/unlock, sentry, valet, PINs, speed limits, remote start |
|
|
184
|
+
| `trunk` | Trunk, frunk, windows, sunroof, tonneau |
|
|
185
|
+
| `media` | Playback control, volume, favorites |
|
|
186
|
+
| `nav` | Send destinations, GPS coordinates, multi-stop waypoints, HomeLink |
|
|
187
|
+
| `software` | Update status, scheduling, cancellation |
|
|
188
|
+
| `energy` | Powerwall status, backup reserve, storm mode, grid config, history |
|
|
189
|
+
| `billing` | Supercharger billing history and invoices |
|
|
190
|
+
| `user` | Account info, region, orders, feature flags |
|
|
191
|
+
| `sharing` | Driver management, vehicle sharing invites |
|
|
192
|
+
| `key` | Key generation, deployment, enrollment, validation |
|
|
193
|
+
| `serve` | Combined MCP + telemetry + OpenClaw TUI dashboard |
|
|
194
|
+
| `mcp` | Standalone MCP server |
|
|
195
|
+
| `openclaw` | Standalone OpenClaw bridge |
|
|
196
|
+
| `cache` | Cache status and management |
|
|
197
|
+
| `raw` | Direct Fleet API endpoint access |
|
|
198
|
+
|
|
199
|
+
Every command supports `--format json` for scripting and `--help` for detailed usage. See the [Command Reference](docs/commands.md) for the full list.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Installation
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
pip install tescmd
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Requirements:** Python 3.11+ and a [Tesla account](https://www.tesla.com) with a linked vehicle or energy product.
|
|
210
|
+
|
|
211
|
+
**Recommended:** [GitHub CLI](https://cli.github.com) (`gh`) for automated key hosting via GitHub Pages, or [Tailscale](https://tailscale.com) for zero-config key hosting and telemetry streaming via Funnel.
|
|
212
|
+
|
|
213
|
+
<details>
|
|
214
|
+
<summary>Install from source</summary>
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
git clone https://github.com/oceanswave/tescmd.git
|
|
218
|
+
cd tescmd
|
|
219
|
+
pip install -e ".[dev]"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
</details>
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Configuration
|
|
227
|
+
|
|
228
|
+
tescmd resolves settings from CLI flags, environment variables (`.env` files loaded automatically), and defaults — in that order.
|
|
229
|
+
|
|
230
|
+
<details>
|
|
231
|
+
<summary>Environment variables</summary>
|
|
232
|
+
|
|
233
|
+
```dotenv
|
|
234
|
+
TESLA_CLIENT_ID=your-client-id
|
|
235
|
+
TESLA_CLIENT_SECRET=your-client-secret
|
|
236
|
+
TESLA_VIN=5YJ3E1EA1NF000000
|
|
237
|
+
TESLA_REGION=na # na, eu, cn
|
|
238
|
+
|
|
239
|
+
# Display units (optional — defaults to US)
|
|
240
|
+
TESLA_TEMP_UNIT=F # F or C
|
|
241
|
+
TESLA_DISTANCE_UNIT=mi # mi or km
|
|
242
|
+
TESLA_PRESSURE_UNIT=psi # psi or bar
|
|
243
|
+
|
|
244
|
+
# Or switch everything at once:
|
|
245
|
+
# tescmd --units metric charge status
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
See [docs/commands.md](docs/commands.md) for the full environment variable reference.
|
|
249
|
+
|
|
250
|
+
</details>
|
|
251
|
+
|
|
252
|
+
<details>
|
|
253
|
+
<summary>Token storage</summary>
|
|
254
|
+
|
|
255
|
+
Tokens are stored in the OS keyring by default (macOS Keychain, GNOME Keyring, Windows Credential Manager). On headless systems, tescmd falls back to a file-based store with restricted permissions. Transfer tokens between machines with `tescmd auth export` and `tescmd auth import`.
|
|
256
|
+
|
|
257
|
+
</details>
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Documentation
|
|
262
|
+
|
|
263
|
+
| | |
|
|
264
|
+
|---|---|
|
|
265
|
+
| [Setup Guide](docs/setup.md) | Step-by-step walkthrough of `tescmd setup` |
|
|
266
|
+
| [Command Reference](docs/commands.md) | Detailed usage for every command |
|
|
267
|
+
| [API Costs](docs/api-costs.md) | Cost breakdown, savings calculations, streaming comparison |
|
|
268
|
+
| [Bot Integration](docs/bot-integration.md) | JSON schema, exit codes, headless auth |
|
|
269
|
+
| [OpenClaw Bridge](docs/openclaw.md) | Gateway protocol, bidirectional commands, triggers, geofencing |
|
|
270
|
+
| [MCP Server](docs/mcp.md) | Tool reference, OAuth 2.1, custom tools, trigger polling |
|
|
271
|
+
| [Vehicle Command Protocol](docs/vehicle-command-protocol.md) | ECDH sessions and signed commands |
|
|
272
|
+
| [Authentication](docs/authentication.md) | OAuth2 PKCE flow, token storage, scopes |
|
|
273
|
+
| [Architecture](docs/architecture.md) | Layered design, module responsibilities |
|
|
274
|
+
| [FAQ](docs/faq.md) | Common questions about costs, hosting, and configuration |
|
|
275
|
+
| [Development](docs/development.md) | Contributing, testing, linting |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Development
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
git clone https://github.com/oceanswave/tescmd.git && cd tescmd
|
|
283
|
+
pip install -e ".[dev]"
|
|
284
|
+
pytest # 1600+ tests
|
|
285
|
+
ruff check src/ tests/ && mypy src/
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Changelog
|
|
291
|
+
|
|
292
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
MIT
|
|
297
|
+
|
|
298
|
+
<p align="center">
|
|
299
|
+
<img src="images/tescmd_logo.jpeg" alt="tescmd logo" width="300">
|
|
300
|
+
</p>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
tescmd/__init__.py,sha256=
|
|
1
|
+
tescmd/__init__.py,sha256=5McRfiCZ4XFvlU9hlnml5SecuYhXarzh3HAVGhcE0ZQ,116
|
|
2
2
|
tescmd/__main__.py,sha256=ecNCDo0sINhjJZiauhAcUMU67U6XUCU23ocf7vQG45E,83
|
|
3
3
|
tescmd/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
tescmd/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -17,7 +17,7 @@ tescmd/api/signed_command.py,sha256=lgmy1LpkWzRT4ExYVnbuSVcHeI6uqBXkBjTC1lF6_8s,
|
|
|
17
17
|
tescmd/api/user.py,sha256=pz4sB9i-9pHukdIBOwGrgfRpWf4zd5gnqAqm3zAJpcA,1411
|
|
18
18
|
tescmd/api/vehicle.py,sha256=esCCnyWyUz7QNGfzmbrGx824YZkDmSI_LsemsA6faDU,7377
|
|
19
19
|
tescmd/auth/__init__.py,sha256=PHsT1-mgm9b-4xft0-adq3i6fwyCSj2of4KTpMAYUhw,76
|
|
20
|
-
tescmd/auth/oauth.py,sha256
|
|
20
|
+
tescmd/auth/oauth.py,sha256=-jWFC0uYYswreiV2WjDBE_-sQlSggYC8ARbpDrfzTv0,10446
|
|
21
21
|
tescmd/auth/server.py,sha256=34j6cXGkp3K7It4m3uCjvMYv82tRXYS80Xd2GQ_DPAk,3751
|
|
22
22
|
tescmd/auth/token_store.py,sha256=jju5IzdeXQK1pFuxX-bMhBuMRAZKcP0m-hM8arARpUU,8906
|
|
23
23
|
tescmd/ble/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -27,13 +27,13 @@ tescmd/cache/response_cache.py,sha256=5M5atqQxv1FQou7iP7eNyTgv54zBVStgO62veetSlh
|
|
|
27
27
|
tescmd/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
tescmd/cli/_client.py,sha256=r4jJ1mnRX96PcKrCH3o3EMxVofCxgMkfBSRe8TwgtTg,25761
|
|
29
29
|
tescmd/cli/_options.py,sha256=Ob-tdg2IftP6skZDGGeFwBXYIA6I_7bnUfag3xyT9VM,4166
|
|
30
|
-
tescmd/cli/auth.py,sha256=
|
|
30
|
+
tescmd/cli/auth.py,sha256=9bp0gRwn2Z4n2jRBzVMqx8ptDEQgLk2xO-plLWnMXfE,29713
|
|
31
31
|
tescmd/cli/billing.py,sha256=BcuAI10Q2Tf6fobejoKsaO4qVN7TcOy95HSdylBn23M,7946
|
|
32
32
|
tescmd/cli/cache.py,sha256=LaOOfHS1ycF8LoCFdcUQfw11PYor2S__-SdWtRpA_pA,2729
|
|
33
33
|
tescmd/cli/charge.py,sha256=TOXRn7YGuFYj5GimBhh2bTOB_5vzU5sc86PODifHXGk,20088
|
|
34
34
|
tescmd/cli/climate.py,sha256=npekNHOcJ63oG1-2o-fZiTRWFmemdZ1Tl_pASWwWBlI,16890
|
|
35
35
|
tescmd/cli/energy.py,sha256=YUv-_UnbUu7u5nko15MSrS5s9bU6Y58IszVfURXRr34,11931
|
|
36
|
-
tescmd/cli/key.py,sha256=
|
|
36
|
+
tescmd/cli/key.py,sha256=bIiBJG3Xu0xhvvg33VpAjLUuQCORQg2u9ajzhApAoAo,26304
|
|
37
37
|
tescmd/cli/main.py,sha256=WmERuesvxcmpI8zlgOAeSYw3KgEApAZMbiCasxJa7z4,22502
|
|
38
38
|
tescmd/cli/mcp_cmd.py,sha256=K6siov7O-dzyZKuHFHFpYraRE9uMcG13obWmdyND6DQ,4644
|
|
39
39
|
tescmd/cli/media.py,sha256=PQ-QJkjkIiM8IJ5CXtDOY0N1yC3DlaC5YLnebxT1nrI,4267
|
|
@@ -43,7 +43,7 @@ tescmd/cli/partner.py,sha256=0GfDCwM_Kfy5g95JwUhrrsjDFn9lSbkuNgfquIQqcSo,3488
|
|
|
43
43
|
tescmd/cli/raw.py,sha256=hj5hZs8tHHHAeXtpf7kHXewciXDvl0RlMJkPK7i3ouY,2238
|
|
44
44
|
tescmd/cli/security.py,sha256=uHowY0b7CH7Ff0LdzqBHL4rjyM6W9AYXC_N9EXvE7pA,15937
|
|
45
45
|
tescmd/cli/serve.py,sha256=dM61NpiON3ZcJ-HT1D_RNUN6d8fpr6OnvVJyvpTT9lU,33358
|
|
46
|
-
tescmd/cli/setup.py,sha256=
|
|
46
|
+
tescmd/cli/setup.py,sha256=Ep1iaUiF9NIUDXGsY6m3gxNFgEjiHsJeK60-tPWzpbA,35979
|
|
47
47
|
tescmd/cli/sharing.py,sha256=9H3MSM1ZvPq4sBPSyvgUXHTvwm1IUAh8PrIu71ZV1GY,6294
|
|
48
48
|
tescmd/cli/software.py,sha256=f3Jl5GRruE_wNHl3jfLbVQHEXQvQxe4y-UaNIY1j76Y,2711
|
|
49
49
|
tescmd/cli/status.py,sha256=_Ql5oMA6j03g16acjtWbFWk6v-pfMJhlTdCNmq7MPh4,3997
|
|
@@ -56,8 +56,8 @@ tescmd/crypto/ecdh.py,sha256=xFe4BR1G5CznwLUJnWvOWr_ZHsWUpdu9IJlL3-LaAk4,1759
|
|
|
56
56
|
tescmd/crypto/keys.py,sha256=jzFZwZKMYFlHGPeTSrw5pVLBwjxvLV57LzTK9d4-caM,4037
|
|
57
57
|
tescmd/crypto/schnorr.py,sha256=qgISnRMDrH-LkinjLoapV8wsoVNdwBnCZvLPMgGIGyo,6626
|
|
58
58
|
tescmd/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
tescmd/deploy/github_pages.py,sha256=
|
|
60
|
-
tescmd/deploy/tailscale_serve.py,sha256=
|
|
59
|
+
tescmd/deploy/github_pages.py,sha256=i5M6U0SxYyasPx-MqG8hW8cJqYqKTrXde-P1cSBI8SQ,8756
|
|
60
|
+
tescmd/deploy/tailscale_serve.py,sha256=IOWgmhRp7fF6oZA-sLihsen-o7aLxDxB5BOFfoDSsO0,7839
|
|
61
61
|
tescmd/mcp/__init__.py,sha256=zHLbMTNRql4hy1jNMc1zD6P0AzX8vFzgjm5QCAZSsQg,172
|
|
62
62
|
tescmd/mcp/server.py,sha256=o0cD1xMNyt8qmGZhN5O7cWTQTdBd9FTaOpQPzpVT3Xw,25414
|
|
63
63
|
tescmd/models/__init__.py,sha256=mo6ngQp9m-244UGykbgY-DoqnunLD4DwEqHdJwP9GLc,1767
|
|
@@ -71,10 +71,10 @@ tescmd/models/vehicle.py,sha256=GXkQOAB406JQI4vI7xhEbWB1hK5H0SLhzwtJEXPEsTw,5733
|
|
|
71
71
|
tescmd/openclaw/__init__.py,sha256=J4Fb8zs69-KQTKgUZVm3MOhvKAafHGhIFF50NxqGNa4,722
|
|
72
72
|
tescmd/openclaw/bridge.py,sha256=_HYO4L5J7aHS4BPIbRumcaHpPHHoIFGzgrVAtOGWT9Y,11665
|
|
73
73
|
tescmd/openclaw/config.py,sha256=73lH7f8hNXJvpUdQcYEdNyz8wTtudTsP1nskw4ybekg,5643
|
|
74
|
-
tescmd/openclaw/dispatcher.py,sha256=
|
|
74
|
+
tescmd/openclaw/dispatcher.py,sha256=LuBsriutiMSOsOIVDCHg0L0RUdXrX_n1n9TTIjZ6CSo,22295
|
|
75
75
|
tescmd/openclaw/emitter.py,sha256=IAY_0tYduXif1JOcWtqhuGeYNN3irDNsnRWrzMdLnXU,6001
|
|
76
76
|
tescmd/openclaw/filters.py,sha256=QJeM8I0Sx_7Hjg4Uy7UjkIU7mAYScMRi_WGzUBnmw0w,4020
|
|
77
|
-
tescmd/openclaw/gateway.py,sha256=
|
|
77
|
+
tescmd/openclaw/gateway.py,sha256=pTQ5ZG3PPjXhwmljU4DlsMCe4By4HwOUkTKnMnsCU38,24301
|
|
78
78
|
tescmd/openclaw/telemetry_store.py,sha256=C3VLUstw1_6nKFKGZJgvuWaaYXdclX0K1rbaDT4Rkls,1696
|
|
79
79
|
tescmd/output/__init__.py,sha256=uFvlXwHmKHkxOIVgP8JryZlQbHLHhbhLGswGo9xuFbI,120
|
|
80
80
|
tescmd/output/formatter.py,sha256=pQLes-gZJbD4S32-fRwKqcLepsiXb9AIWP-Wm_H1ca0,4658
|
|
@@ -100,7 +100,7 @@ tescmd/telemetry/flatbuf.py,sha256=2q4LvlL6SAabTrQg7bS5qrgokO9GylenWQKna363th8,5
|
|
|
100
100
|
tescmd/telemetry/mapper.py,sha256=rBoJojr2s27pOPXcYHo7ASc82SJagpZ-YD8dGFd0pTg,9483
|
|
101
101
|
tescmd/telemetry/server.py,sha256=kcl7zJpjTuRIbhgPwWU4K8vvhXWJ-oUAmSNOdPY3x2k,10647
|
|
102
102
|
tescmd/telemetry/setup.py,sha256=d8owu8a72-4MO7QyFfOa99D6YzOBoCrIGVNuAduTEw0,17008
|
|
103
|
-
tescmd/telemetry/tailscale.py,sha256=
|
|
103
|
+
tescmd/telemetry/tailscale.py,sha256=DmfUXg5OwppE_MnPmX7VJMqJJOZHR2GVz5WY2FMlk00,13014
|
|
104
104
|
tescmd/telemetry/tui.py,sha256=Ka0PuMCcAvDfXMfvNi82k0ESNR0KWEt9hw3hDOG0e8g,64505
|
|
105
105
|
tescmd/telemetry/protos/__init__.py,sha256=VAyp9-euADAEHyi533DRswRK5yEGdL08nXCTiWysgqM,150
|
|
106
106
|
tescmd/telemetry/protos/vehicle_alert.proto,sha256=NZnGi1-PGYBf6JpKD0ptynVOh2AOthRXfsAcvYsLLN4,708
|
|
@@ -121,8 +121,8 @@ tescmd/telemetry/protos/vehicle_metric_pb2.pyi,sha256=hFjmnO_D41FYO39NvCakOczCGL
|
|
|
121
121
|
tescmd/triggers/__init__.py,sha256=kFFbRlhCDbYtE_kZ_A65dcxzpvuDsgYZpYgATNggAgc,432
|
|
122
122
|
tescmd/triggers/manager.py,sha256=xon72FVI37JaqtNroRg_B8pn62zv2cuO3iREcESsnsM,8418
|
|
123
123
|
tescmd/triggers/models.py,sha256=t-Bg7renbVaXKDtxPna2z4bdUhJ1_5wGMs4ahwHPVhM,2812
|
|
124
|
-
tescmd-0.
|
|
125
|
-
tescmd-0.
|
|
126
|
-
tescmd-0.
|
|
127
|
-
tescmd-0.
|
|
128
|
-
tescmd-0.
|
|
124
|
+
tescmd-0.4.0.dist-info/METADATA,sha256=-emZGYAYJ7gWthCwpUguC56PWnFFsJ4nkHeOBD8FAF0,11762
|
|
125
|
+
tescmd-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
126
|
+
tescmd-0.4.0.dist-info/entry_points.txt,sha256=e-Uk81_gfLu4XzJl9bv6-bUIodJbnxAgfR5ugFyeD2E,48
|
|
127
|
+
tescmd-0.4.0.dist-info/licenses/LICENSE,sha256=gFEbRZ5xHSPxkT3OgbLFhDWVUxZv80kFDnv0t3G1E7M,1070
|
|
128
|
+
tescmd-0.4.0.dist-info/RECORD,,
|