tescmd 0.1.2__tar.gz

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.
Files changed (186) hide show
  1. tescmd-0.1.2/.claude/settings.local.json +47 -0
  2. tescmd-0.1.2/.gitignore +12 -0
  3. tescmd-0.1.2/CHANGELOG.md +69 -0
  4. tescmd-0.1.2/CLAUDE.md +401 -0
  5. tescmd-0.1.2/LICENSE +21 -0
  6. tescmd-0.1.2/PKG-INFO +458 -0
  7. tescmd-0.1.2/README.md +411 -0
  8. tescmd-0.1.2/docs/api-costs.md +220 -0
  9. tescmd-0.1.2/docs/architecture.md +251 -0
  10. tescmd-0.1.2/docs/authentication.md +263 -0
  11. tescmd-0.1.2/docs/bot-integration.md +375 -0
  12. tescmd-0.1.2/docs/commands.md +1192 -0
  13. tescmd-0.1.2/docs/development.md +464 -0
  14. tescmd-0.1.2/docs/plans/2025-01-29-mvp-design.md +152 -0
  15. tescmd-0.1.2/docs/plans/2025-01-29-mvp-implementation.md +3073 -0
  16. tescmd-0.1.2/docs/setup-enrollment-audit.md +171 -0
  17. tescmd-0.1.2/docs/vehicle-command-protocol.md +187 -0
  18. tescmd-0.1.2/pyproject.toml +96 -0
  19. tescmd-0.1.2/scripts/e2e_test.py +307 -0
  20. tescmd-0.1.2/scripts/validate_fleet_api.py +671 -0
  21. tescmd-0.1.2/spec/fleet_api_spec.json +250 -0
  22. tescmd-0.1.2/src/tescmd/__init__.py +3 -0
  23. tescmd-0.1.2/src/tescmd/__main__.py +5 -0
  24. tescmd-0.1.2/src/tescmd/_internal/__init__.py +0 -0
  25. tescmd-0.1.2/src/tescmd/_internal/async_utils.py +25 -0
  26. tescmd-0.1.2/src/tescmd/_internal/permissions.py +43 -0
  27. tescmd-0.1.2/src/tescmd/_internal/vin.py +44 -0
  28. tescmd-0.1.2/src/tescmd/api/__init__.py +1 -0
  29. tescmd-0.1.2/src/tescmd/api/charging.py +102 -0
  30. tescmd-0.1.2/src/tescmd/api/client.py +189 -0
  31. tescmd-0.1.2/src/tescmd/api/command.py +540 -0
  32. tescmd-0.1.2/src/tescmd/api/energy.py +146 -0
  33. tescmd-0.1.2/src/tescmd/api/errors.py +76 -0
  34. tescmd-0.1.2/src/tescmd/api/partner.py +40 -0
  35. tescmd-0.1.2/src/tescmd/api/sharing.py +65 -0
  36. tescmd-0.1.2/src/tescmd/api/signed_command.py +277 -0
  37. tescmd-0.1.2/src/tescmd/api/user.py +38 -0
  38. tescmd-0.1.2/src/tescmd/api/vehicle.py +150 -0
  39. tescmd-0.1.2/src/tescmd/auth/__init__.py +1 -0
  40. tescmd-0.1.2/src/tescmd/auth/oauth.py +312 -0
  41. tescmd-0.1.2/src/tescmd/auth/server.py +108 -0
  42. tescmd-0.1.2/src/tescmd/auth/token_store.py +273 -0
  43. tescmd-0.1.2/src/tescmd/ble/__init__.py +0 -0
  44. tescmd-0.1.2/src/tescmd/cache/__init__.py +6 -0
  45. tescmd-0.1.2/src/tescmd/cache/keys.py +51 -0
  46. tescmd-0.1.2/src/tescmd/cache/response_cache.py +213 -0
  47. tescmd-0.1.2/src/tescmd/cli/__init__.py +0 -0
  48. tescmd-0.1.2/src/tescmd/cli/_client.py +603 -0
  49. tescmd-0.1.2/src/tescmd/cli/_options.py +126 -0
  50. tescmd-0.1.2/src/tescmd/cli/auth.py +682 -0
  51. tescmd-0.1.2/src/tescmd/cli/billing.py +240 -0
  52. tescmd-0.1.2/src/tescmd/cli/cache.py +85 -0
  53. tescmd-0.1.2/src/tescmd/cli/charge.py +610 -0
  54. tescmd-0.1.2/src/tescmd/cli/climate.py +501 -0
  55. tescmd-0.1.2/src/tescmd/cli/energy.py +385 -0
  56. tescmd-0.1.2/src/tescmd/cli/key.py +611 -0
  57. tescmd-0.1.2/src/tescmd/cli/main.py +601 -0
  58. tescmd-0.1.2/src/tescmd/cli/media.py +146 -0
  59. tescmd-0.1.2/src/tescmd/cli/nav.py +242 -0
  60. tescmd-0.1.2/src/tescmd/cli/partner.py +112 -0
  61. tescmd-0.1.2/src/tescmd/cli/raw.py +75 -0
  62. tescmd-0.1.2/src/tescmd/cli/security.py +495 -0
  63. tescmd-0.1.2/src/tescmd/cli/setup.py +786 -0
  64. tescmd-0.1.2/src/tescmd/cli/sharing.py +188 -0
  65. tescmd-0.1.2/src/tescmd/cli/software.py +81 -0
  66. tescmd-0.1.2/src/tescmd/cli/status.py +106 -0
  67. tescmd-0.1.2/src/tescmd/cli/trunk.py +240 -0
  68. tescmd-0.1.2/src/tescmd/cli/user.py +145 -0
  69. tescmd-0.1.2/src/tescmd/cli/vehicle.py +837 -0
  70. tescmd-0.1.2/src/tescmd/config/__init__.py +0 -0
  71. tescmd-0.1.2/src/tescmd/crypto/__init__.py +19 -0
  72. tescmd-0.1.2/src/tescmd/crypto/ecdh.py +46 -0
  73. tescmd-0.1.2/src/tescmd/crypto/keys.py +122 -0
  74. tescmd-0.1.2/src/tescmd/deploy/__init__.py +0 -0
  75. tescmd-0.1.2/src/tescmd/deploy/github_pages.py +268 -0
  76. tescmd-0.1.2/src/tescmd/models/__init__.py +85 -0
  77. tescmd-0.1.2/src/tescmd/models/auth.py +108 -0
  78. tescmd-0.1.2/src/tescmd/models/command.py +18 -0
  79. tescmd-0.1.2/src/tescmd/models/config.py +63 -0
  80. tescmd-0.1.2/src/tescmd/models/energy.py +56 -0
  81. tescmd-0.1.2/src/tescmd/models/sharing.py +26 -0
  82. tescmd-0.1.2/src/tescmd/models/user.py +37 -0
  83. tescmd-0.1.2/src/tescmd/models/vehicle.py +185 -0
  84. tescmd-0.1.2/src/tescmd/output/__init__.py +5 -0
  85. tescmd-0.1.2/src/tescmd/output/formatter.py +132 -0
  86. tescmd-0.1.2/src/tescmd/output/json_output.py +83 -0
  87. tescmd-0.1.2/src/tescmd/output/rich_output.py +809 -0
  88. tescmd-0.1.2/src/tescmd/protocol/__init__.py +23 -0
  89. tescmd-0.1.2/src/tescmd/protocol/commands.py +175 -0
  90. tescmd-0.1.2/src/tescmd/protocol/encoder.py +122 -0
  91. tescmd-0.1.2/src/tescmd/protocol/metadata.py +116 -0
  92. tescmd-0.1.2/src/tescmd/protocol/payloads.py +621 -0
  93. tescmd-0.1.2/src/tescmd/protocol/protobuf/__init__.py +6 -0
  94. tescmd-0.1.2/src/tescmd/protocol/protobuf/messages.py +564 -0
  95. tescmd-0.1.2/src/tescmd/protocol/session.py +318 -0
  96. tescmd-0.1.2/src/tescmd/protocol/signer.py +84 -0
  97. tescmd-0.1.2/src/tescmd/py.typed +0 -0
  98. tescmd-0.1.2/tests/__init__.py +0 -0
  99. tescmd-0.1.2/tests/_internal/__init__.py +0 -0
  100. tescmd-0.1.2/tests/_internal/test_async_utils.py +32 -0
  101. tescmd-0.1.2/tests/_internal/test_vin.py +65 -0
  102. tescmd-0.1.2/tests/api/__init__.py +0 -0
  103. tescmd-0.1.2/tests/api/test_client.py +221 -0
  104. tescmd-0.1.2/tests/api/test_command_api.py +719 -0
  105. tescmd-0.1.2/tests/api/test_energy_api.py +441 -0
  106. tescmd-0.1.2/tests/api/test_partner_api.py +139 -0
  107. tescmd-0.1.2/tests/api/test_sharing_api.py +270 -0
  108. tescmd-0.1.2/tests/api/test_signed_command.py +299 -0
  109. tescmd-0.1.2/tests/api/test_user_api.py +252 -0
  110. tescmd-0.1.2/tests/api/test_vehicle_api.py +611 -0
  111. tescmd-0.1.2/tests/auth/__init__.py +0 -0
  112. tescmd-0.1.2/tests/auth/test_oauth.py +44 -0
  113. tescmd-0.1.2/tests/auth/test_oauth_extended.py +125 -0
  114. tescmd-0.1.2/tests/auth/test_server.py +52 -0
  115. tescmd-0.1.2/tests/auth/test_token_store.py +115 -0
  116. tescmd-0.1.2/tests/auth/test_token_store_fallback.py +138 -0
  117. tescmd-0.1.2/tests/auth/test_token_store_file.py +156 -0
  118. tescmd-0.1.2/tests/cache/__init__.py +0 -0
  119. tescmd-0.1.2/tests/cache/test_generic_cache.py +168 -0
  120. tescmd-0.1.2/tests/cache/test_keys.py +38 -0
  121. tescmd-0.1.2/tests/cache/test_response_cache.py +211 -0
  122. tescmd-0.1.2/tests/cli/__init__.py +0 -0
  123. tescmd-0.1.2/tests/cli/_helpers.py +27 -0
  124. tescmd-0.1.2/tests/cli/conftest.py +26 -0
  125. tescmd-0.1.2/tests/cli/test_auth.py +74 -0
  126. tescmd-0.1.2/tests/cli/test_auth_exec.py +188 -0
  127. tescmd-0.1.2/tests/cli/test_cache.py +100 -0
  128. tescmd-0.1.2/tests/cli/test_cached_api_call.py +351 -0
  129. tescmd-0.1.2/tests/cli/test_charge_exec.py +434 -0
  130. tescmd-0.1.2/tests/cli/test_cli_integration.py +189 -0
  131. tescmd-0.1.2/tests/cli/test_climate_exec.py +463 -0
  132. tescmd-0.1.2/tests/cli/test_energy.py +173 -0
  133. tescmd-0.1.2/tests/cli/test_energy_exec.py +831 -0
  134. tescmd-0.1.2/tests/cli/test_error_handlers.py +114 -0
  135. tescmd-0.1.2/tests/cli/test_key.py +255 -0
  136. tescmd-0.1.2/tests/cli/test_key_enroll.py +202 -0
  137. tescmd-0.1.2/tests/cli/test_key_unenroll.py +136 -0
  138. tescmd-0.1.2/tests/cli/test_main_errors.py +71 -0
  139. tescmd-0.1.2/tests/cli/test_media.py +93 -0
  140. tescmd-0.1.2/tests/cli/test_media_exec.py +346 -0
  141. tescmd-0.1.2/tests/cli/test_nav.py +101 -0
  142. tescmd-0.1.2/tests/cli/test_nav_exec.py +503 -0
  143. tescmd-0.1.2/tests/cli/test_partner.py +44 -0
  144. tescmd-0.1.2/tests/cli/test_raw.py +63 -0
  145. tescmd-0.1.2/tests/cli/test_raw_exec.py +225 -0
  146. tescmd-0.1.2/tests/cli/test_security_exec.py +679 -0
  147. tescmd-0.1.2/tests/cli/test_setup.py +561 -0
  148. tescmd-0.1.2/tests/cli/test_setup_scope_check.py +158 -0
  149. tescmd-0.1.2/tests/cli/test_sharing.py +98 -0
  150. tescmd-0.1.2/tests/cli/test_sharing_exec.py +484 -0
  151. tescmd-0.1.2/tests/cli/test_software.py +63 -0
  152. tescmd-0.1.2/tests/cli/test_software_exec.py +327 -0
  153. tescmd-0.1.2/tests/cli/test_status_exec.py +247 -0
  154. tescmd-0.1.2/tests/cli/test_tier_enforcement.py +143 -0
  155. tescmd-0.1.2/tests/cli/test_trunk_exec.py +409 -0
  156. tescmd-0.1.2/tests/cli/test_user.py +49 -0
  157. tescmd-0.1.2/tests/cli/test_user_exec.py +210 -0
  158. tescmd-0.1.2/tests/cli/test_vcsec_guard.py +218 -0
  159. tescmd-0.1.2/tests/cli/test_vehicle_exec.py +542 -0
  160. tescmd-0.1.2/tests/cli/test_vehicle_power_exec.py +108 -0
  161. tescmd-0.1.2/tests/cli/test_verbose.py +78 -0
  162. tescmd-0.1.2/tests/cli/test_wake_confirmation.py +157 -0
  163. tescmd-0.1.2/tests/conftest.py +70 -0
  164. tescmd-0.1.2/tests/crypto/__init__.py +0 -0
  165. tescmd-0.1.2/tests/crypto/test_ecdh.py +94 -0
  166. tescmd-0.1.2/tests/crypto/test_keys.py +132 -0
  167. tescmd-0.1.2/tests/deploy/__init__.py +0 -0
  168. tescmd-0.1.2/tests/deploy/test_github_pages.py +304 -0
  169. tescmd-0.1.2/tests/models/__init__.py +0 -0
  170. tescmd-0.1.2/tests/models/test_auth.py +89 -0
  171. tescmd-0.1.2/tests/models/test_config.py +87 -0
  172. tescmd-0.1.2/tests/models/test_energy.py +94 -0
  173. tescmd-0.1.2/tests/models/test_sharing.py +70 -0
  174. tescmd-0.1.2/tests/models/test_user_models.py +57 -0
  175. tescmd-0.1.2/tests/models/test_vehicle.py +155 -0
  176. tescmd-0.1.2/tests/output/__init__.py +0 -0
  177. tescmd-0.1.2/tests/output/test_formatter.py +62 -0
  178. tescmd-0.1.2/tests/output/test_json_output.py +138 -0
  179. tescmd-0.1.2/tests/output/test_rich_output.py +471 -0
  180. tescmd-0.1.2/tests/protocol/__init__.py +0 -0
  181. tescmd-0.1.2/tests/protocol/conftest.py +44 -0
  182. tescmd-0.1.2/tests/protocol/test_commands.py +162 -0
  183. tescmd-0.1.2/tests/protocol/test_encoder.py +293 -0
  184. tescmd-0.1.2/tests/protocol/test_metadata.py +222 -0
  185. tescmd-0.1.2/tests/protocol/test_session.py +332 -0
  186. tescmd-0.1.2/tests/protocol/test_signer.py +124 -0
@@ -0,0 +1,47 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:developer.tesla.com)",
5
+ "WebFetch(domain:github.com)",
6
+ "WebFetch(domain:raw.githubusercontent.com)",
7
+ "WebFetch(domain:api.github.com)",
8
+ "Bash(pip install:*)",
9
+ "Bash(git -C /Users/oceanswave/Projects/tescmd init:*)",
10
+ "Bash(git -C /Users/oceanswave/Projects/tescmd add pyproject.toml src/ tests/ .gitignore CLAUDE.md README.md docs/)",
11
+ "Bash(git -C /Users/oceanswave/Projects/tescmd commit -m \"feat: project skeleton with pyproject.toml and package structure\")",
12
+ "Bash(git -C /Users/oceanswave/Projects/tescmd log --oneline)",
13
+ "Bash(python:*)",
14
+ "Bash(git -C /Users/oceanswave/Projects/tescmd log --oneline -5)",
15
+ "Bash(git -C /Users/oceanswave/Projects/tescmd add src/tescmd/_internal/async_utils.py src/tescmd/_internal/vin.py)",
16
+ "Bash(git -C /Users/oceanswave/Projects/tescmd commit -m \"$\\(cat <<''EOF''\nfeat: add async utils and VIN resolution\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
17
+ "Bash(git -C /Users/oceanswave/Projects/tescmd status)",
18
+ "Bash(pytest:*)",
19
+ "Bash(git add:*)",
20
+ "Bash(git commit:*)",
21
+ "Bash(ruff check:*)",
22
+ "Bash(ruff format:*)",
23
+ "Bash(mypy:*)",
24
+ "Bash(tescmd --help:*)",
25
+ "Bash(tree:*)",
26
+ "mcp__plugin_context7_context7__resolve-library-id",
27
+ "mcp__plugin_context7_context7__query-docs",
28
+ "WebSearch",
29
+ "WebFetch(domain:pypi.org)",
30
+ "WebFetch(domain:developer.tessie.com)",
31
+ "Bash(ls:*)",
32
+ "Bash(wc:*)",
33
+ "Bash(grep:*)",
34
+ "Bash(while read file)",
35
+ "Bash(do grep -h \"^from\\\\|^import\" \"$file\")",
36
+ "Bash(done)",
37
+ "Bash(test:*)",
38
+ "Bash(xargs:*)",
39
+ "Bash(curl:*)",
40
+ "WebFetch(domain:www.tesla.com)",
41
+ "Bash(find:*)",
42
+ "Bash(gh api:*)",
43
+ "mcp__plugin_playwright_playwright__browser_navigate",
44
+ "mcp__plugin_playwright_playwright__browser_close"
45
+ ]
46
+ }
47
+ }
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .env
8
+ *.pem
9
+ .mypy_cache/
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .coverage
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.2] - 2025-01-31
9
+
10
+ ### Added
11
+
12
+ - **Universal read-command caching** — every read command is now transparently cached with tiered TTLs (STATIC 1h, SLOW 5m, DEFAULT 1m, FAST 30s); bots can call tescmd as often as needed — within the TTL window, responses are instant and free
13
+ - **Generic cache key scheme** — `generic_cache_key(scope, identifier, endpoint, params)` generates scope-aware keys (`vin`, `site`, `account`, `partner`) for any API endpoint
14
+ - **`cached_api_call()` helper** — unified async helper that handles cache lookup, fetch, serialisation (Pydantic/dict/list/scalar), and storage for all non-vehicle-state read commands
15
+ - **Site-scoped cache invalidation** — `invalidate_cache_for_site()` clears energy site entries after write commands; `invalidate_cache_for_vin()` now also clears generic vin-scoped keys
16
+ - **`cache clear` options** — `--site SITE_ID` and `--scope {account,partner}` flags for targeted cache clearing alongside existing `--vin`
17
+ - **Partner endpoints** — `partner public-key`, `partner telemetry-error-vins`, `partner telemetry-errors` for partner account data (require client credentials)
18
+ - **Billing endpoints** — `billing history`, `billing sessions`, `billing invoice` for Supercharger charging data
19
+ - **Cross-platform file permissions** — `_internal/permissions.py` provides `secure_file()` using `chmod 0600` on Unix and `icacls` on Windows
20
+ - **Token store file backend** — `_FileBackend` with atomic writes and restricted permissions as fallback when keyring is unavailable
21
+ - **Spec-driven Fleet API validation** — `scripts/validate_fleet_api.py` validates implementation against `spec/fleet_api_spec.json` using AST introspection
22
+ - **6 missing Fleet API commands** — added `managed_charging_set_amps`, `managed_charging_set_location`, `managed_charging_set_schedule`, `add_charge_schedule`, `remove_charge_schedule`, `clear_charge_schedules`
23
+ - **Configurable display units** — `--units metric` flag switches all display values to °C/km/bar; individual env vars (`TESLA_TEMP_UNIT`, `TESLA_DISTANCE_UNIT`, `TESLA_PRESSURE_UNIT`) for granular control
24
+
25
+ ### Fixed
26
+
27
+ - Aligned schedule/departure command parameters with Tesla Go SDK (correct param names and types)
28
+ - Fixed energy endpoint paths to match Fleet API spec
29
+ - Fixed Rich markup escaping bug in command output
30
+ - Aligned command parameters (3 param gaps) with Go SDK specs
31
+
32
+ ### Changed
33
+
34
+ - Response cache documentation in CLAUDE.md expanded to cover universal caching, TTL tiers, and generic cache key scheme
35
+
36
+ ## [0.1.1]
37
+
38
+ ### Added
39
+
40
+ - **`status` command** — `tescmd status` shows current configuration, auth, cache, and key status at a glance
41
+ - **Retry option in wake prompt** — when a vehicle is asleep, the interactive prompt now offers `[R] Retry` alongside `[W] Wake via API` and `[C] Cancel`, allowing users to wake the vehicle for free via the Tesla app and retry without restarting the command
42
+ - **Key enrollment** — `tescmd key enroll <VIN>` sends the public key to the vehicle and guides the user through Tesla app approval with interactive [C]heck/[R]esend/[Q]uit prompt, `--wait` auto-polling, and JSON mode support
43
+ - **Tier enforcement** — readonly tier now blocks write commands with a clear error and upgrade guidance (`tescmd setup`)
44
+ - **Vehicle Command Protocol** — ECDH session management, HMAC-SHA256 command signing, and protobuf RoutableMessage encoding for the `signed_command` endpoint; commands are automatically signed when keys are available (`command_protocol=auto`)
45
+ - **SignedCommandAPI** — composition wrapper that transparently routes signed commands through the Vehicle Command Protocol while falling back to unsigned REST for `wake_up` and unknown commands
46
+ - **`command_protocol` setting** — `auto` (default), `signed`, or `unsigned` to control command routing; configurable via `TESLA_COMMAND_PROTOCOL` env var
47
+ - **Enrollment step in setup wizard** — full-tier setup now offers to enroll the key on a vehicle after key generation
48
+ - **Friendly command output** — all vehicle commands now display descriptive success messages (e.g. "Climate control turned on.", "Doors locked.") instead of bare "OK"
49
+ - **E2E test script** — `scripts/e2e_test.py` provides interactive end-to-end command testing against a live vehicle with per-command confirmation, category filtering, and destructive-command skipping
50
+
51
+ ## [0.1.0]
52
+
53
+ ### Added
54
+
55
+ - OAuth2 PKCE authentication with browser-based login flow
56
+ - Vehicle state queries: battery, charge, climate, drive, location, doors, windows, trunks, tire pressure
57
+ - Vehicle commands: charge start/stop/limit/schedule, climate on/off/set/seats/wheel, lock/unlock, sentry, trunk/frunk, windows, media, navigation, software updates, HomeLink, speed limits, PIN management
58
+ - Energy products: Powerwall live status, site info, backup reserve, operation mode, storm mode, TOU settings, charging history, calendar history, grid config
59
+ - User account: profile info, region, orders, feature config
60
+ - Vehicle sharing: add/remove drivers, create/redeem/revoke invites
61
+ - Rich terminal output with tables, panels, and status indicators
62
+ - JSON output mode for scripting and agent integration
63
+ - Configurable display units (F/C, mi/km, PSI/bar)
64
+ - Response caching with configurable TTL for API cost reduction
65
+ - Cost-aware wake confirmation (interactive prompt or `--wake` flag)
66
+ - Multi-profile configuration support
67
+ - EC key generation and Tesla Developer Portal registration
68
+ - Raw API access (`raw get`, `raw post`) for uncovered endpoints
69
+ - First-run setup wizard with Fleet Telemetry cost guidance
tescmd-0.1.2/CLAUDE.md ADDED
@@ -0,0 +1,401 @@
1
+ # CLAUDE.md — Project Context for Claude Code
2
+
3
+ ## Project Overview
4
+
5
+ **tescmd** is a Python CLI application that queries data from and sends commands to Tesla vehicles via the [Tesla Fleet API](https://developer.tesla.com/docs/fleet-api). The current implementation covers OAuth2 authentication, key management, vehicle state queries (battery, location, climate, drive state, tire pressure, trunks, and more), and both human-friendly (Rich TUI) and machine-friendly (JSON) output with configurable display units.
6
+
7
+ **Current scope:** auth, vehicle queries, vehicle commands (charge, climate, security, trunk, media, nav, software), energy product management (including telemetry), Supercharger charging history and invoices, user account info, vehicle sharing, partner account endpoints (public key, fleet telemetry errors), key management with enrollment and unenrollment, Vehicle Command Protocol (ECDH sessions + HMAC-signed protobuf commands), tier enforcement, initial setup, response caching with cost-aware wake confirmation, Fleet Telemetry configuration management (create/delete/errors), configuration status dashboard, and GitHub Pages key deployment.
8
+
9
+ ## Tech Stack
10
+
11
+ - **Python 3.11+** (required for `tomllib`, `StrEnum`, modern typing)
12
+ - **pydantic v2** — request/response models, settings management
13
+ - **rich** — terminal tables, panels, spinners, progress bars
14
+ - **click** — CLI argument parsing and command routing
15
+ - **httpx** — async HTTP client for Fleet API calls
16
+ - **cryptography** — EC key generation, PEM handling, ECDH key exchange
17
+ - **protobuf** — protobuf serialization for Vehicle Command Protocol messages
18
+ - **keyring** — OS-level credential storage for tokens
19
+ - **python-dotenv** — `.env` file loading
20
+ - **bleak** — BLE communication for key enrollment (optional; portal enrollment is primary)
21
+
22
+ ## Project Structure
23
+
24
+ ```
25
+ src/tescmd/
26
+ ├── __init__.py # Package version
27
+ ├── __main__.py # Entry point (python -m tescmd)
28
+ ├── cli/ # CLI layer (click-based)
29
+ │ ├── __init__.py
30
+ │ ├── main.py # Root Click group, dispatch, AppContext
31
+ │ ├── _options.py # Shared Click options/decorators
32
+ │ ├── _client.py # API client builders, auto_wake, cached_vehicle_data, cached_api_call, TTL tiers
33
+ │ ├── auth.py # auth login, logout, status, refresh, export, register, import
34
+ │ ├── cache.py # cache clear, cache status
35
+ │ ├── charge.py # charge status, start, stop, limit, limit-max, limit-std, amps, port-open, port-close, schedule, departure, precondition-add/remove, add/remove-schedule, clear-schedules, clear-preconditions, managed-amps/location/schedule
36
+ │ ├── billing.py # billing history, sessions, invoice (Supercharger billing data)
37
+ │ ├── climate.py # climate status, on, off, set, precondition, seat, seat-cool, wheel-heater, overheat, bioweapon, keeper, cop-temp, auto-seat, auto-wheel, wheel-level
38
+ │ ├── security.py # security status, lock, auto-secure, unlock, sentry, valet, valet-reset, remote-start, flash, honk, boombox, pin-to-drive, pin-reset, pin-clear-admin, speed-limit, speed-clear, speed-clear-admin, guest-mode, erase-data
39
+ │ ├── status.py # status (show config, auth, and cache status)
40
+ │ ├── trunk.py # trunk open, close, frunk, window, sunroof, tonneau-open/close/stop
41
+ │ ├── vehicle.py # vehicle list, get, info, data, location, wake, rename, mobile-access, nearby-chargers, alerts, release-notes, service, drivers, calendar, subscriptions, upgrades, options, specs, warranty, fleet-status, low-power, accessory-power; telemetry subgroup: config, create, delete, errors
42
+ │ ├── media.py # media play-pause, next/prev track, next/prev fav, volume
43
+ │ ├── nav.py # nav send, gps, supercharger, homelink, waypoints
44
+ │ ├── partner.py # partner public-key, telemetry-error-vins, telemetry-errors
45
+ │ ├── software.py # software status, schedule, cancel
46
+ │ ├── energy.py # energy list, status, live, backup, mode, storm, tou, history, off-grid, grid-config, telemetry, calendar
47
+ │ ├── user.py # user me, region, orders, features
48
+ │ ├── sharing.py # sharing add/remove driver, create/redeem/revoke/list invites
49
+ │ ├── raw.py # raw get, raw post (arbitrary Fleet API access)
50
+ │ ├── key.py # key generate, deploy, validate, show, enroll, unenroll
51
+ │ └── setup.py # setup (interactive first-run wizard, key enrollment, Fleet Telemetry awareness)
52
+ ├── api/ # API client layer
53
+ │ ├── __init__.py
54
+ │ ├── client.py # TeslaFleetClient (base HTTP client)
55
+ │ ├── vehicle.py # VehicleAPI (vehicle data, nearby chargers, alerts, drivers, fleet telemetry, subscriptions, specs)
56
+ │ ├── command.py # CommandAPI (~78 vehicle commands, unsigned REST)
57
+ │ ├── signed_command.py # SignedCommandAPI (Vehicle Command Protocol routing)
58
+ │ ├── energy.py # EnergyAPI (Powerwall/energy product endpoints, telemetry)
59
+ │ ├── charging.py # ChargingAPI (Supercharger charging history, sessions, invoices)
60
+ │ ├── partner.py # PartnerAPI (public key, fleet telemetry errors — requires partner token)
61
+ │ ├── sharing.py # SharingAPI (driver and invite management)
62
+ │ ├── user.py # UserAPI (account info, region, orders, features)
63
+ │ └── errors.py # API error types (incl. TierError, SessionError, KeyNotEnrolledError)
64
+ ├── cache/ # Response caching
65
+ │ ├── __init__.py # Re-exports ResponseCache, generic_cache_key
66
+ │ ├── response_cache.py # ResponseCache (file-based JSON with TTL, generic cache)
67
+ │ └── keys.py # Cache key generation (VIN + endpoint hash, generic_cache_key)
68
+ ├── models/ # Pydantic models
69
+ │ ├── __init__.py # Re-exports all models (40 symbols)
70
+ │ ├── vehicle.py # Vehicle, VehicleData, DriveState, ChargeState, ClimateState, VehicleState, VehicleConfig, GuiSettings, SoftwareUpdateInfo, SuperchargerInfo, DestChargerInfo, NearbyChargingSites
71
+ │ ├── energy.py # LiveStatus, SiteInfo, CalendarHistory, GridImportExportConfig
72
+ │ ├── user.py # UserInfo, UserRegion, VehicleOrder, FeatureConfig
73
+ │ ├── sharing.py # ShareDriverInfo, ShareInvite
74
+ │ ├── auth.py # TokenData, TokenMeta, AuthConfig
75
+ │ ├── command.py # CommandResponse, CommandResult
76
+ │ └── config.py # AppSettings (pydantic-settings, incl. cache settings)
77
+ ├── auth/ # Authentication
78
+ │ ├── __init__.py
79
+ │ ├── oauth.py # OAuth2 PKCE flow, token refresh, partner registration
80
+ │ ├── token_store.py # Token persistence (keyring backend + file-based fallback)
81
+ │ └── server.py # Local callback server for OAuth redirect
82
+ ├── protocol/ # Vehicle Command Protocol
83
+ │ ├── __init__.py # Re-exports: Session, SessionManager, CommandSpec, etc.
84
+ │ ├── protobuf/ # Protobuf message definitions
85
+ │ │ ├── __init__.py
86
+ │ │ └── messages.py # RoutableMessage, SessionInfo, Domain, SignatureData, etc.
87
+ │ ├── session.py # ECDH session management (SessionManager)
88
+ │ ├── signer.py # HMAC-SHA256 command signing
89
+ │ ├── metadata.py # TLV serialization for command metadata
90
+ │ ├── commands.py # Command registry (name → domain + signing requirement)
91
+ │ ├── payloads.py # Protobuf payload builders for Vehicle Command Protocol
92
+ │ └── encoder.py # RoutableMessage assembly + base64 encoding
93
+ ├── crypto/ # Key management and ECDH
94
+ │ ├── __init__.py
95
+ │ ├── keys.py # EC key generation, loading, PEM export
96
+ │ └── ecdh.py # ECDH key exchange, session key derivation
97
+ ├── output/ # Output formatting
98
+ │ ├── __init__.py
99
+ │ ├── formatter.py # OutputFormatter (auto-detect TTY vs pipe)
100
+ │ ├── rich_output.py # Rich tables, panels, status displays, DisplayUnits
101
+ │ └── json_output.py # Structured JSON output
102
+ ├── ble/ # BLE communication (stub — enrollment not yet wired)
103
+ │ └── __init__.py
104
+ ├── deploy/ # Key deployment helpers
105
+ │ ├── __init__.py
106
+ │ └── github_pages.py # GitHub Pages deployment for public key hosting
107
+ ├── config/ # Configuration (stub — settings in models/config.py)
108
+ │ └── __init__.py
109
+ └── _internal/ # Shared utilities
110
+ ├── __init__.py
111
+ ├── vin.py # Smart VIN resolution
112
+ ├── async_utils.py # asyncio helpers (run_async)
113
+ └── permissions.py # Cross-platform file permissions (chmod 0600 / icacls)
114
+
115
+ spec/
116
+ └── fleet_api_spec.json # Canonical Fleet API specification (endpoints, params, types)
117
+
118
+ scripts/
119
+ └── validate_fleet_api.py # Spec-driven API coverage validator (AST-based)
120
+ ```
121
+
122
+ ### Key Models (`models/vehicle.py`)
123
+
124
+ The Pydantic vehicle models cover an extensive set of Tesla Fleet API fields:
125
+
126
+ - **ChargeState** — battery %, range (rated/ideal/estimated), usable %, charge limit, rate, voltage, current, charger power, charger type, energy added, cable type, port latch, scheduled charging, battery heater, preconditioning
127
+ - **ClimateState** — inside/outside temp, driver/passenger setting, HVAC on/off, fan speed, defrost, front+rear seat heaters, steering wheel heater, cabin overheat protection, bioweapon defense mode, auto conditioning, preconditioning
128
+ - **VehicleState** — locked, odometer, sentry mode, firmware version, doors (4), windows (4), frunk/trunk (ft/rt), center display, dashcam, remote start, user present, homelink, TPMS tire pressure (4 wheels)
129
+ - **VehicleConfig** — car type, trim, color, wheels, roof color, navigation, trunk actuation, seat cooling, motorized charge port, power liftgate, EU vehicle
130
+ - **GuiSettings** — distance units, temperature units, charge rate units
131
+
132
+ Additional typed models:
133
+
134
+ - **SoftwareUpdateInfo** — status, version, install_perc, expected_duration_sec, scheduled_time_ms, download_perc
135
+ - **NearbyChargingSites** — superchargers (list of SuperchargerInfo), destination_charging (list of DestChargerInfo)
136
+ - **SiteInfo** — energy_site_id, site_name, resource_type, backup_reserve_percent, default_real_mode, storm_mode_enabled
137
+ - **CalendarHistory** — serial_number, time_series
138
+ - **GridImportExportConfig** — disallow_charge_from_grid_with_solar_installed, customer_preferred_export_rule
139
+ - **VehicleOrder** — order_id, vin, model, status
140
+ - **FeatureConfig** — signaling dict
141
+ - **ShareDriverInfo** — share_user_id, email, status, public_key
142
+ - **ShareInvite** — id, code, created_at, expires_at, status
143
+
144
+ All models use `extra="allow"` so unknown fields from the API are captured without validation errors.
145
+
146
+ ### Display Units (`output/rich_output.py`)
147
+
148
+ Rich output supports configurable display units via `DisplayUnits`:
149
+
150
+ - **Pressure:** PSI (default) or bar
151
+ - **Temperature:** °F (default) or °C
152
+ - **Distance:** mi (default) or km
153
+
154
+ The Tesla API returns temperatures in Celsius, distances in miles, and tire pressures in bar. Conversions happen in the display layer only — models retain raw API values.
155
+
156
+ ```python
157
+ from tescmd.output.rich_output import DisplayUnits, DistanceUnit, PressureUnit, TempUnit
158
+
159
+ # US defaults (no argument needed)
160
+ ro = RichOutput(console)
161
+
162
+ # Metric
163
+ ro = RichOutput(console, units=DisplayUnits(
164
+ pressure=PressureUnit.BAR,
165
+ temp=TempUnit.C,
166
+ distance=DistanceUnit.KM,
167
+ ))
168
+ ```
169
+
170
+ ### Response Cache (`cache/response_cache.py`)
171
+
172
+ The Tesla Fleet API is pay-per-use — every call with status < 500 is billable, wake requests are the most expensive category (3/min limit), and their docs explicitly say `vehicle_data` should never be polled regularly. **All read commands** are transparently cached — bots can call tescmd as often as needed; within the TTL window, responses are instant and free.
173
+
174
+ The cache reduces API costs through four mechanisms:
175
+
176
+ 1. **Universal read-command cache** — Every read command goes through `cached_api_call()` with a scope-aware TTL. Vehicle state queries additionally use `cached_vehicle_data()` with smart wake logic.
177
+ 2. **Disk cache** — `ResponseCache` stores API responses as JSON files under `~/.cache/tescmd/`. Each entry has a TTL. Expired entries are cleaned up lazily on read.
178
+ 3. **Wake state cache** — Tracks whether the vehicle was recently confirmed online (default 30s TTL). Skips redundant wake attempts when the vehicle is known to be awake.
179
+ 4. **Wake confirmation prompt** — Before sending a billable wake API call, users are prompted interactively (TTY) or receive a structured error (JSON/piped) with guidance to wake via the Tesla app for free.
180
+
181
+ **TTL tiers** (defined in `cli/_client.py`):
182
+
183
+ | Tier | TTL | Use case | Example endpoints |
184
+ |------|-----|----------|-------------------|
185
+ | `TTL_STATIC` | 3600s (1h) | Rarely changes | specs, warranty, options, user.me, user.region, user.features, partner.public-key |
186
+ | `TTL_SLOW` | 300s (5m) | Changes infrequently | vehicle.list, fleet-status, drivers, subscriptions, energy.list, energy.status, sharing.list-invites |
187
+ | `TTL_DEFAULT` | 60s (1m) | Standard | vehicle.get, mobile-access, alerts, billing.history, user.orders |
188
+ | `TTL_FAST` | 30s | Location-dependent | nearby-chargers |
189
+
190
+ **Generic cache key scheme** (`cache/keys.py`):
191
+
192
+ `generic_cache_key(scope, identifier, endpoint, params)` generates keys in the format `{scope}_{identifier}_{sha256(endpoint+params)[:12]}`. Scopes: `vin`, `site`, `account`, `partner`. Params are sorted and hashed so different query parameters produce different cache entries.
193
+
194
+ **Cache file naming**:
195
+ - Legacy: `{vin}_{endpoint_hash}.json` (vehicle state data), `{vin}_wake.json`
196
+ - Generic: `{scope}_{identifier}_{hash}.json` (all other cached commands)
197
+
198
+ **Data flow for vehicle-state commands** (`cached_vehicle_data()` in `_client.py`):
199
+
200
+ 1. Check disk cache → on hit, return `VehicleData.model_validate(cached)`
201
+ 2. Check wake state cache → if recently online, try direct API fetch (skip wake)
202
+ 3. If direct fetch raises `VehicleAsleepError`, fall back to `auto_wake()`
203
+ 4. If no cached wake state, use `auto_wake()` directly
204
+ 5. On success, cache the response and update wake state
205
+
206
+ **Data flow for all other read commands** (`cached_api_call()` in `_client.py`):
207
+
208
+ 1. Compute `generic_cache_key(scope, identifier, endpoint, params)`
209
+ 2. Check `cache.get_generic(key)` → on hit, emit cache metadata, return cached dict
210
+ 3. On miss → call `fetch()`, serialise (Pydantic → dict), `cache.put_generic(key, data, ttl)`, return result
211
+
212
+ **Write-commands** (POST operations) do not cache responses but call `invalidate_cache_for_vin()` or `invalidate_cache_for_site()` after success to prevent stale reads.
213
+
214
+ **Intentionally NOT cached:**
215
+ - `energy live` — real-time power flow, stale in seconds
216
+ - `energy history/calendar/telemetry` — time-range parameterized, complex key management
217
+ - `billing invoice` — one-off document retrieval
218
+ - `raw get` / `raw post` — escape hatch, user controls caching
219
+ - `vehicle wake` — write operation
220
+ - Auth/key/setup commands — infrastructure, not data reads
221
+
222
+ ```python
223
+ from tescmd.cache import ResponseCache, generic_cache_key
224
+
225
+ cache = ResponseCache(cache_dir=Path("~/.cache/tescmd"), default_ttl=60, enabled=True)
226
+
227
+ # Legacy VIN-scoped cache (vehicle state data)
228
+ cache.put("VIN123", {"charge_state": {"battery_level": 72}}, endpoints=["charge_state"])
229
+ data = cache.get("VIN123", endpoints=["charge_state"]) # CacheResult or None
230
+
231
+ # Generic cache (any scope)
232
+ key = generic_cache_key("account", "global", "vehicle.list")
233
+ cache.put_generic(key, [{"vin": "VIN123"}], ttl=300)
234
+ result = cache.get_generic(key) # CacheResult or None
235
+
236
+ # Wake state
237
+ cache.put_wake_state("VIN123", "online", ttl=30)
238
+ is_online = cache.get_wake_state("VIN123") # True/False
239
+
240
+ # Clearing
241
+ cache.clear("VIN123") # per-VIN (legacy keys)
242
+ cache.clear_by_prefix("vin_VIN123_") # per-VIN (generic keys)
243
+ cache.clear_by_prefix("site_12345_") # per energy site
244
+ cache.clear_by_prefix("account_") # all account-level entries
245
+ cache.clear() # everything
246
+ ```
247
+
248
+ ### Wake Confirmation (`cli/_client.py`)
249
+
250
+ When a vehicle is asleep and a command needs to wake it, `auto_wake()` behaves differently based on context:
251
+
252
+ | Mode | `--wake` flag | Behavior |
253
+ |---|---|---|
254
+ | TTY (Rich) | Not set | Interactive prompt: `[W] Wake via API [R] Retry [C] Cancel` |
255
+ | TTY (Rich) | Set | Auto-wake without prompting |
256
+ | JSON / piped | Not set | Raise `VehicleAsleepError` with `--wake` guidance |
257
+ | JSON / piped | Set | Auto-wake without prompting |
258
+
259
+ The `[R] Retry` option allows users to wake the vehicle for free via the Tesla mobile app and then retry the command without a billable API call. If the vehicle is still asleep after retry, the prompt re-appears. The `vehicle wake` command is an explicit wake request and uses its own logic (no prompt needed). The wake state cache means the prompt only triggers when the vehicle is actually asleep — if it was recently confirmed online, the prompt is skipped entirely.
260
+
261
+ ## Coding Conventions
262
+
263
+ - **Type hints everywhere** — all function signatures, all variables where non-obvious
264
+ - **async/await** — all API calls are async; CLI entry points use `run_async()` helper
265
+ - **Pydantic models** — all API request/response payloads; all configuration
266
+ - **src layout** — code lives under `src/tescmd/`, tests under `tests/`
267
+ - **No star imports** — explicit imports only
268
+ - **Single responsibility** — CLI modules handle args + output, API modules handle HTTP
269
+ - **Composition over inheritance** — `VehicleAPI` wraps `TeslaFleetClient`, doesn't extend it
270
+
271
+ ## Build System
272
+
273
+ - **hatchling** via `pyproject.toml`
274
+ - Entry point: `tescmd = "tescmd.cli.main:main"`
275
+ - No `setup.py` or `setup.cfg`
276
+
277
+ ## Testing
278
+
279
+ - **pytest** + **pytest-asyncio** + **pytest-httpx**
280
+ - Test files mirror source: `tests/cli/test_auth.py`, `tests/api/test_client.py`, etc.
281
+ - Use `pytest-httpx` to mock HTTP responses (no live API calls in tests)
282
+ - Async tests use `@pytest.mark.asyncio`
283
+ - Current count: ~910 tests
284
+
285
+ ## Linting & Formatting
286
+
287
+ - **ruff** — linting and formatting (replaces flake8, isort, black)
288
+ - **mypy** — strict mode, all code fully typed
289
+ - Config in `pyproject.toml`
290
+
291
+ ## Key Architectural Decisions
292
+
293
+ 1. **Composition over inheritance** — API classes wrap `TeslaFleetClient` via constructor injection
294
+ 2. **REST-first with portal key enrollment** — all commands go over REST; key enrollment uses Tesla Developer Portal (remote, confirmed via Tesla app); BLE enrollment is an optional alternative requiring physical proximity
295
+ 3. **Output auto-detection** — TTY → Rich panels/tables; piped → JSON; `--quiet` → minimal stderr only
296
+ 4. **Error output stream routing** — JSON/piped mode writes errors to **stderr** so stdout stays clean for machine-parseable data (scripts can safely `| jq`). TTY/Rich mode keeps errors on **stdout** because the user is looking at the terminal directly — splitting streams would be worse UX there. This split lives in `OutputFormatter.output_error()` and the error handlers in `cli/main.py`. Interactive prompts (wake confirmation spinner, enrollment approval) always use stdout via Rich since they are inherently TTY-only.
297
+ 5. **Smart VIN resolution** — positional arg > `--vin` flag > profile default > interactive picker
298
+ 6. **Settings resolution** — CLI args > env vars (`.env` loaded via python-dotenv) > `config.toml` profile > defaults
299
+ 7. **click** — works well with command structure, async patterns
300
+ 8. **httpx async** — clean async API, good type stubs, easily testable with pytest-httpx
301
+ 9. **Browser-based auth** — `tescmd auth login` opens system browser for OAuth2 PKCE flow with local callback server
302
+ 10. **Display-layer unit conversion** — models retain raw API values (Celsius, miles, bar); conversion to user-preferred units happens in `RichOutput` via `DisplayUnits`
303
+ 11. **Universal response caching** — file-based JSON cache under `~/.cache/tescmd/` with tiered TTLs (STATIC 1h / SLOW 5m / DEFAULT 1m / FAST 30s). **All** read commands are cached via `cached_api_call()` with scope-aware keys (`vin`, `site`, `account`, `partner`). Vehicle state queries use the dedicated `cached_vehicle_data()` with smart wake logic. Write-commands invalidate via `invalidate_cache_for_vin()` / `invalidate_cache_for_site()`. No new dependencies (stdlib `json`, `hashlib`, `time`, `pathlib`).
304
+ 12. **Cost-aware wake** — `auto_wake()` prompts before sending billable wake API calls. TTY users get an interactive choice; JSON/piped consumers get structured errors. The `--wake` flag opts in to auto-wake for scripts that accept the cost.
305
+ 13. **Smart wake state** — wake state is cached separately (30s TTL). If the vehicle was recently confirmed online, both the prompt and the wake API call are skipped entirely.
306
+ 14. **Vehicle Command Protocol** — `SignedCommandAPI` wraps `CommandAPI` via composition. When keys are enrolled and tier is `full`, `get_command_api()` returns `SignedCommandAPI` which transparently routes signed commands through ECDH session + HMAC path. Unsigned commands (`wake_up`) pass through to legacy REST. The `command_protocol` setting (`auto`/`signed`/`unsigned`) controls routing.
307
+ 15. **Tier enforcement** — `execute_command()` checks `setup_tier` before running write commands. Readonly tier raises `TierError` with guidance to upgrade via `tescmd setup`.
308
+ 16. **Key enrollment** — `tescmd key enroll <VIN>` sends the public key to the vehicle via the unsigned `add_key_request` endpoint, then guides the user through Tesla app approval with an interactive prompt or `--wait` auto-polling.
309
+ 17. **Cross-platform token storage** — `TokenStore` uses a `_TokenBackend` protocol with two implementations: `_KeyringBackend` (OS keyring) and `_FileBackend` (JSON file with atomic writes and restricted permissions). Backend selection: explicit `TESLA_TOKEN_FILE` → file; keyring probe success → keyring; probe failure → file fallback at `{config_dir}/tokens.json` with one-time warning.
310
+ 18. **Cross-platform file permissions** — `_internal/permissions.py` provides `secure_file()` which uses `chmod 0600` on Unix and `icacls` on Windows. Used by both token storage and key generation. Failures are silently ignored (Docker volumes, network mounts, etc.).
311
+
312
+ ## Environment Variables
313
+
314
+ | Variable | Description | Default |
315
+ |---|---|---|
316
+ | `TESLA_CLIENT_ID` | OAuth2 application client ID | — |
317
+ | `TESLA_CLIENT_SECRET` | OAuth2 application client secret | — |
318
+ | `TESLA_VIN` | Default vehicle VIN | — |
319
+ | `TESLA_REGION` | API region (`na`, `eu`, `cn`) | `na` |
320
+ | `TESLA_TOKEN_FILE` | File path for token storage (skips keyring; auto-set on headless fallback) | (keyring or `{config_dir}/tokens.json`) |
321
+ | `TESLA_CONFIG_DIR` | Override config directory | `~/.config/tescmd` |
322
+ | `TESLA_OUTPUT_FORMAT` | Force output format (`rich`, `json`, `quiet`) | (auto) |
323
+ | `TESLA_PROFILE` | Active config profile name | `default` |
324
+ | `TESLA_CACHE_ENABLED` | Enable/disable response cache | `true` |
325
+ | `TESLA_CACHE_TTL` | Cache entry time-to-live (seconds) | `60` |
326
+ | `TESLA_CACHE_DIR` | Cache directory path | `~/.cache/tescmd` |
327
+ | `TESLA_COMMAND_PROTOCOL` | Command signing: `auto`, `signed`, `unsigned` | `auto` |
328
+ | `TESLA_TEMP_UNIT` | Temperature display unit: `F` or `C` | `F` |
329
+ | `TESLA_DISTANCE_UNIT` | Distance display unit: `mi` or `km` | `mi` |
330
+ | `TESLA_PRESSURE_UNIT` | Pressure display unit: `psi` or `bar` | `psi` |
331
+ | `TESLA_ACCESS_TOKEN` | Override access token (bypass keyring) | — |
332
+ | `TESLA_REFRESH_TOKEN` | Override refresh token (bypass keyring) | — |
333
+ | `TESLA_DOMAIN` | Domain for Fleet API key hosting | — |
334
+ | `TESLA_SETUP_TIER` | Setup tier (`readonly` or `full`) | — |
335
+ | `TESLA_GITHUB_REPO` | GitHub repo for key deployment (e.g., `user/user.github.io`) | — |
336
+
337
+ All variables can also be set in a `.env` file in the working directory or `$TESLA_CONFIG_DIR/.env`.
338
+
339
+ ## CLI Flags
340
+
341
+ | Flag | Scope | Description |
342
+ |---|---|---|
343
+ | `--vin VIN` | Global | Vehicle VIN (also positional on most commands) |
344
+ | `--profile NAME` | Global | Config profile name |
345
+ | `--format {rich,json,quiet}` | Global | Force output format |
346
+ | `--quiet` | Global | Suppress normal output |
347
+ | `--region {na,eu,cn}` | Global | Tesla API region |
348
+ | `--verbose` | Global | Enable verbose logging |
349
+ | `--no-cache` / `--fresh` | Global | Bypass response cache for this invocation |
350
+ | `--wake` | Global | Auto-wake vehicle without confirmation (billable) |
351
+ | `--units {us,metric}` | Global | Display units preset (us: °F/mi/psi, metric: °C/km/bar) |
352
+
353
+ All global flags can be specified at the root level (`tescmd --wake charge status`) or after the subcommand (`tescmd charge status --wake`).
354
+
355
+ ## API Coverage Validation
356
+
357
+ The project includes a spec-driven validation utility to ensure our implementation stays in sync with the Tesla Fleet API.
358
+
359
+ - **`spec/fleet_api_spec.json`** — canonical specification covering all Fleet API endpoints, params, and types (sourced from Tesla's docs + Go SDK)
360
+ - **`scripts/validate_fleet_api.py`** — validates the implementation against the spec using AST introspection (no imports needed)
361
+
362
+ ```bash
363
+ # Run the validator
364
+ python scripts/validate_fleet_api.py # Summary output
365
+ python scripts/validate_fleet_api.py --verbose # Show all endpoints
366
+ python scripts/validate_fleet_api.py --json # Machine-readable output
367
+ ```
368
+
369
+ The validator checks: vehicle commands (params + types), vehicle endpoints, energy endpoints, user endpoints, charging endpoints, partner endpoints, and protocol registry entries. Exit code 0 = all pass, 1 = errors found.
370
+
371
+ **When to run:** After adding or modifying API methods, after Tesla updates their docs, or periodically to catch drift.
372
+
373
+ ## Common Commands (for reference)
374
+
375
+ ```bash
376
+ # Dev
377
+ ruff check src/ tests/
378
+ ruff format src/ tests/
379
+ mypy src/
380
+ pytest
381
+ pytest tests/cli/ -k "test_auth"
382
+ pytest tests/cache/ # Cache-specific tests
383
+
384
+ # API coverage
385
+ python scripts/validate_fleet_api.py
386
+
387
+ # Build
388
+ python -m build
389
+
390
+ # Cache management
391
+ tescmd cache status # Show cache stats
392
+ tescmd cache clear # Clear all cached entries
393
+ tescmd cache clear --vin VIN # Clear cache for a specific vehicle
394
+ tescmd cache clear --site 12345 # Clear cache for an energy site
395
+ tescmd cache clear --scope account # Clear account-level entries
396
+
397
+ # Cost-optimized usage
398
+ tescmd charge status # Uses cache, prompts before wake
399
+ tescmd charge status --fresh # Bypasses cache, still prompts before wake
400
+ tescmd charge status --wake # Auto-wakes without prompting (billable)
401
+ ```
tescmd-0.1.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sean McLellan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.