tescmd 0.2.0__tar.gz → 0.3.1__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 (278) hide show
  1. tescmd-0.3.1/.env.example +75 -0
  2. {tescmd-0.2.0 → tescmd-0.3.1}/.gitignore +2 -1
  3. {tescmd-0.2.0 → tescmd-0.3.1}/CHANGELOG.md +60 -1
  4. tescmd-0.3.1/CLAUDE.md +131 -0
  5. {tescmd-0.2.0 → tescmd-0.3.1}/PKG-INFO +80 -32
  6. {tescmd-0.2.0 → tescmd-0.3.1}/README.md +74 -29
  7. {tescmd-0.2.0 → tescmd-0.3.1}/docs/api-costs.md +26 -11
  8. {tescmd-0.2.0 → tescmd-0.3.1}/docs/architecture.md +129 -11
  9. {tescmd-0.2.0 → tescmd-0.3.1}/docs/authentication.md +52 -8
  10. {tescmd-0.2.0 → tescmd-0.3.1}/docs/bot-integration.md +31 -1
  11. {tescmd-0.2.0 → tescmd-0.3.1}/docs/commands.md +263 -1
  12. {tescmd-0.2.0 → tescmd-0.3.1}/docs/development.md +13 -5
  13. {tescmd-0.2.0 → tescmd-0.3.1}/docs/faq.md +22 -2
  14. tescmd-0.3.1/docs/mcp.md +416 -0
  15. tescmd-0.3.1/docs/openclaw.md +463 -0
  16. {tescmd-0.2.0 → tescmd-0.3.1}/docs/setup.md +72 -31
  17. {tescmd-0.2.0 → tescmd-0.3.1}/docs/vehicle-command-protocol.md +43 -12
  18. tescmd-0.3.1/images/tescmd_header.jpeg +0 -0
  19. tescmd-0.3.1/images/tescmd_logo.jpeg +0 -0
  20. {tescmd-0.2.0 → tescmd-0.3.1}/pyproject.toml +14 -2
  21. tescmd-0.3.1/skills/tescmd/SKILL.md +524 -0
  22. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/__init__.py +1 -1
  23. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/client.py +41 -4
  24. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/command.py +1 -1
  25. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/errors.py +5 -0
  26. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/signed_command.py +19 -14
  27. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/oauth.py +5 -1
  28. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/server.py +6 -1
  29. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/token_store.py +8 -1
  30. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cache/response_cache.py +8 -1
  31. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/_client.py +142 -20
  32. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/_options.py +2 -4
  33. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/auth.py +96 -14
  34. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/energy.py +2 -0
  35. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/main.py +27 -8
  36. tescmd-0.3.1/src/tescmd/cli/mcp_cmd.py +153 -0
  37. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/nav.py +3 -1
  38. tescmd-0.3.1/src/tescmd/cli/openclaw.py +169 -0
  39. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/security.py +7 -1
  40. tescmd-0.3.1/src/tescmd/cli/serve.py +923 -0
  41. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/setup.py +18 -7
  42. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/sharing.py +2 -0
  43. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/status.py +1 -1
  44. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/trunk.py +8 -17
  45. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/user.py +16 -1
  46. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/vehicle.py +135 -462
  47. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/deploy/github_pages.py +8 -0
  48. tescmd-0.3.1/src/tescmd/mcp/__init__.py +7 -0
  49. tescmd-0.3.1/src/tescmd/mcp/server.py +648 -0
  50. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/auth.py +5 -2
  51. tescmd-0.3.1/src/tescmd/openclaw/__init__.py +23 -0
  52. tescmd-0.3.1/src/tescmd/openclaw/bridge.py +330 -0
  53. tescmd-0.3.1/src/tescmd/openclaw/config.py +167 -0
  54. tescmd-0.3.1/src/tescmd/openclaw/dispatcher.py +522 -0
  55. tescmd-0.3.1/src/tescmd/openclaw/emitter.py +175 -0
  56. tescmd-0.3.1/src/tescmd/openclaw/filters.py +123 -0
  57. tescmd-0.3.1/src/tescmd/openclaw/gateway.py +687 -0
  58. tescmd-0.3.1/src/tescmd/openclaw/telemetry_store.py +53 -0
  59. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/rich_output.py +46 -14
  60. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/commands.py +2 -2
  61. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/encoder.py +16 -13
  62. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/payloads.py +132 -11
  63. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/session.py +8 -5
  64. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/signer.py +3 -17
  65. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/__init__.py +9 -0
  66. tescmd-0.3.1/src/tescmd/telemetry/cache_sink.py +154 -0
  67. tescmd-0.3.1/src/tescmd/telemetry/csv_sink.py +180 -0
  68. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/dashboard.py +4 -4
  69. tescmd-0.3.1/src/tescmd/telemetry/fanout.py +49 -0
  70. tescmd-0.3.1/src/tescmd/telemetry/fields.py +427 -0
  71. tescmd-0.3.1/src/tescmd/telemetry/mapper.py +239 -0
  72. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/server.py +26 -19
  73. tescmd-0.3.1/src/tescmd/telemetry/setup.py +468 -0
  74. tescmd-0.3.1/src/tescmd/telemetry/tui.py +1716 -0
  75. tescmd-0.3.1/src/tescmd/triggers/__init__.py +18 -0
  76. tescmd-0.3.1/src/tescmd/triggers/manager.py +264 -0
  77. tescmd-0.3.1/src/tescmd/triggers/models.py +93 -0
  78. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_signed_command.py +2 -2
  79. tescmd-0.3.1/tests/cli/test_auth.py +301 -0
  80. tescmd-0.3.1/tests/cli/test_bugfixes.py +236 -0
  81. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_nav_exec.py +1 -1
  82. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_setup.py +76 -0
  83. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_trunk_exec.py +10 -23
  84. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_vehicle_exec.py +1 -1
  85. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_wake_confirmation.py +13 -1
  86. tescmd-0.3.1/tests/integration/test_serve.py +163 -0
  87. tescmd-0.3.1/tests/mcp/test_server.py +407 -0
  88. tescmd-0.3.1/tests/mcp/test_tools.py +86 -0
  89. tescmd-0.3.1/tests/openclaw/test_bridge.py +618 -0
  90. tescmd-0.3.1/tests/openclaw/test_config.py +196 -0
  91. tescmd-0.3.1/tests/openclaw/test_dispatcher.py +1128 -0
  92. tescmd-0.3.1/tests/openclaw/test_emitter.py +165 -0
  93. tescmd-0.3.1/tests/openclaw/test_filters.py +131 -0
  94. tescmd-0.3.1/tests/openclaw/test_gateway.py +943 -0
  95. tescmd-0.3.1/tests/openclaw/test_telemetry_store.py +106 -0
  96. tescmd-0.3.1/tests/output/__init__.py +0 -0
  97. {tescmd-0.2.0 → tescmd-0.3.1}/tests/output/test_rich_output.py +7 -7
  98. tescmd-0.3.1/tests/protocol/__init__.py +0 -0
  99. tescmd-0.3.1/tests/protocol/test_boombox.py +74 -0
  100. {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_commands.py +15 -0
  101. {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_encoder.py +22 -19
  102. tescmd-0.3.1/tests/protocol/test_navigation.py +142 -0
  103. {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_session.py +3 -3
  104. {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_signer.py +3 -13
  105. tescmd-0.3.1/tests/telemetry/__init__.py +0 -0
  106. tescmd-0.3.1/tests/telemetry/test_cache_sink.py +277 -0
  107. tescmd-0.3.1/tests/telemetry/test_csv_sink.py +321 -0
  108. tescmd-0.3.1/tests/telemetry/test_fanout.py +105 -0
  109. tescmd-0.3.1/tests/telemetry/test_fields.py +119 -0
  110. tescmd-0.3.1/tests/telemetry/test_mapper.py +192 -0
  111. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_stream_cmd.py +2 -0
  112. tescmd-0.3.1/tests/telemetry/test_tui.py +600 -0
  113. tescmd-0.3.1/tests/triggers/__init__.py +0 -0
  114. tescmd-0.3.1/tests/triggers/test_manager.py +474 -0
  115. tescmd-0.3.1/tests/triggers/test_models.py +162 -0
  116. tescmd-0.2.0/CLAUDE.md +0 -430
  117. tescmd-0.2.0/src/tescmd/telemetry/fields.py +0 -248
  118. tescmd-0.2.0/tests/cli/test_auth.py +0 -74
  119. tescmd-0.2.0/tests/telemetry/test_fields.py +0 -73
  120. {tescmd-0.2.0 → tescmd-0.3.1}/.github/workflows/publish.yml +0 -0
  121. {tescmd-0.2.0 → tescmd-0.3.1}/.github/workflows/test.yml +0 -0
  122. {tescmd-0.2.0 → tescmd-0.3.1}/LICENSE +0 -0
  123. {tescmd-0.2.0 → tescmd-0.3.1}/scripts/validate_fleet_api.py +0 -0
  124. {tescmd-0.2.0 → tescmd-0.3.1}/spec/fleet_api_spec.json +0 -0
  125. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/__main__.py +0 -0
  126. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/__init__.py +0 -0
  127. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/async_utils.py +0 -0
  128. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/permissions.py +0 -0
  129. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/vin.py +0 -0
  130. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/__init__.py +0 -0
  131. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/charging.py +0 -0
  132. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/energy.py +0 -0
  133. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/partner.py +0 -0
  134. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/sharing.py +0 -0
  135. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/user.py +0 -0
  136. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/vehicle.py +0 -0
  137. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/__init__.py +0 -0
  138. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/ble/__init__.py +0 -0
  139. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cache/__init__.py +0 -0
  140. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cache/keys.py +0 -0
  141. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/__init__.py +0 -0
  142. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/billing.py +0 -0
  143. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/cache.py +0 -0
  144. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/charge.py +0 -0
  145. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/climate.py +0 -0
  146. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/key.py +0 -0
  147. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/media.py +0 -0
  148. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/partner.py +0 -0
  149. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/raw.py +0 -0
  150. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/software.py +0 -0
  151. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/config/__init__.py +0 -0
  152. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/__init__.py +0 -0
  153. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/ecdh.py +0 -0
  154. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/keys.py +0 -0
  155. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/schnorr.py +0 -0
  156. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/deploy/__init__.py +0 -0
  157. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/deploy/tailscale_serve.py +0 -0
  158. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/__init__.py +0 -0
  159. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/command.py +0 -0
  160. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/config.py +0 -0
  161. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/energy.py +0 -0
  162. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/sharing.py +0 -0
  163. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/user.py +0 -0
  164. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/vehicle.py +0 -0
  165. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/__init__.py +0 -0
  166. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/formatter.py +0 -0
  167. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/json_output.py +0 -0
  168. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/__init__.py +0 -0
  169. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/metadata.py +0 -0
  170. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/protobuf/__init__.py +0 -0
  171. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/protobuf/messages.py +0 -0
  172. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/py.typed +0 -0
  173. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/decoder.py +0 -0
  174. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/flatbuf.py +0 -0
  175. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/__init__.py +0 -0
  176. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_alert.proto +0 -0
  177. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_alert_pb2.py +0 -0
  178. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_alert_pb2.pyi +0 -0
  179. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_connectivity.proto +0 -0
  180. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_connectivity_pb2.py +0 -0
  181. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_connectivity_pb2.pyi +0 -0
  182. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_data.proto +0 -0
  183. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_data_pb2.py +0 -0
  184. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_data_pb2.pyi +0 -0
  185. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_error.proto +0 -0
  186. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_error_pb2.py +0 -0
  187. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_error_pb2.pyi +0 -0
  188. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_metric.proto +0 -0
  189. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_metric_pb2.py +0 -0
  190. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_metric_pb2.pyi +0 -0
  191. {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/tailscale.py +0 -0
  192. {tescmd-0.2.0 → tescmd-0.3.1}/tests/__init__.py +0 -0
  193. {tescmd-0.2.0 → tescmd-0.3.1}/tests/_internal/__init__.py +0 -0
  194. {tescmd-0.2.0 → tescmd-0.3.1}/tests/_internal/test_async_utils.py +0 -0
  195. {tescmd-0.2.0 → tescmd-0.3.1}/tests/_internal/test_vin.py +0 -0
  196. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/__init__.py +0 -0
  197. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_client.py +0 -0
  198. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_command_api.py +0 -0
  199. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_energy_api.py +0 -0
  200. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_partner_api.py +0 -0
  201. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_sharing_api.py +0 -0
  202. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_user_api.py +0 -0
  203. {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_vehicle_api.py +0 -0
  204. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/__init__.py +0 -0
  205. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_oauth.py +0 -0
  206. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_oauth_extended.py +0 -0
  207. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_server.py +0 -0
  208. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_token_store.py +0 -0
  209. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_token_store_fallback.py +0 -0
  210. {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_token_store_file.py +0 -0
  211. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/__init__.py +0 -0
  212. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/test_generic_cache.py +0 -0
  213. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/test_keys.py +0 -0
  214. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/test_response_cache.py +0 -0
  215. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/__init__.py +0 -0
  216. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/_helpers.py +0 -0
  217. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/conftest.py +0 -0
  218. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_auth_exec.py +0 -0
  219. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_cache.py +0 -0
  220. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_cached_api_call.py +0 -0
  221. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_charge_exec.py +0 -0
  222. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_cli_integration.py +0 -0
  223. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_climate_exec.py +0 -0
  224. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_e2e_smoke.py +0 -0
  225. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_energy.py +0 -0
  226. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_energy_exec.py +0 -0
  227. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_error_handlers.py +0 -0
  228. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_key.py +0 -0
  229. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_key_enroll.py +0 -0
  230. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_key_unenroll.py +0 -0
  231. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_main_errors.py +0 -0
  232. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_media.py +0 -0
  233. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_media_exec.py +0 -0
  234. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_nav.py +0 -0
  235. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_partner.py +0 -0
  236. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_raw.py +0 -0
  237. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_raw_exec.py +0 -0
  238. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_security_exec.py +0 -0
  239. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_setup_scope_check.py +0 -0
  240. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_sharing.py +0 -0
  241. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_sharing_exec.py +0 -0
  242. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_software.py +0 -0
  243. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_software_exec.py +0 -0
  244. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_status_exec.py +0 -0
  245. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_tier_enforcement.py +0 -0
  246. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_user.py +0 -0
  247. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_user_exec.py +0 -0
  248. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_vcsec_guard.py +0 -0
  249. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_vehicle_power_exec.py +0 -0
  250. {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_verbose.py +0 -0
  251. {tescmd-0.2.0 → tescmd-0.3.1}/tests/conftest.py +0 -0
  252. {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/__init__.py +0 -0
  253. {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/test_ecdh.py +0 -0
  254. {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/test_keys.py +0 -0
  255. {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/test_schnorr.py +0 -0
  256. {tescmd-0.2.0 → tescmd-0.3.1}/tests/deploy/__init__.py +0 -0
  257. {tescmd-0.2.0 → tescmd-0.3.1}/tests/deploy/test_github_pages.py +0 -0
  258. {tescmd-0.2.0 → tescmd-0.3.1}/tests/deploy/test_tailscale_serve.py +0 -0
  259. {tescmd-0.2.0/tests/models → tescmd-0.3.1/tests/integration}/__init__.py +0 -0
  260. {tescmd-0.2.0/tests/output → tescmd-0.3.1/tests/mcp}/__init__.py +0 -0
  261. {tescmd-0.2.0/tests/protocol → tescmd-0.3.1/tests/models}/__init__.py +0 -0
  262. {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_auth.py +0 -0
  263. {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_config.py +0 -0
  264. {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_energy.py +0 -0
  265. {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_sharing.py +0 -0
  266. {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_user_models.py +0 -0
  267. {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_vehicle.py +0 -0
  268. {tescmd-0.2.0/tests/telemetry → tescmd-0.3.1/tests/openclaw}/__init__.py +0 -0
  269. {tescmd-0.2.0 → tescmd-0.3.1}/tests/output/test_formatter.py +0 -0
  270. {tescmd-0.2.0 → tescmd-0.3.1}/tests/output/test_json_output.py +0 -0
  271. {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/conftest.py +0 -0
  272. {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_metadata.py +0 -0
  273. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/conftest.py +0 -0
  274. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_dashboard.py +0 -0
  275. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_decoder.py +0 -0
  276. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_flatbuf.py +0 -0
  277. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_server.py +0 -0
  278. {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_tailscale.py +0 -0
@@ -0,0 +1,75 @@
1
+ # tescmd — Environment Variables
2
+ #
3
+ # Copy this file to .env and uncomment the variables you need.
4
+ # The setup wizard (tescmd setup) will create ~/.config/tescmd/.env
5
+ # for you automatically.
6
+ #
7
+ # Resolution order:
8
+ # 1. Shell environment variables (highest priority)
9
+ # 2. .env in the current working directory
10
+ # 3. ~/.config/tescmd/.env (created by tescmd setup)
11
+
12
+ # ─── Tesla Fleet API Credentials ─────────────────────────────────
13
+ # Required. Obtain from https://developer.tesla.com
14
+ # TESLA_CLIENT_ID=
15
+ # TESLA_CLIENT_SECRET=
16
+
17
+ # ─── Vehicle ──────────────────────────────────────────────────────
18
+ # Default vehicle VIN (avoids passing --vin on every command)
19
+ # TESLA_VIN=
20
+
21
+ # API region: na (North America), eu (Europe), cn (China)
22
+ # TESLA_REGION=na
23
+
24
+ # ─── Token Override / Storage ─────────────────────────────────────
25
+ # Direct token override (bypasses keyring / token file)
26
+ # TESLA_ACCESS_TOKEN=
27
+ # TESLA_REFRESH_TOKEN=
28
+
29
+ # File path for token storage (skips OS keyring)
30
+ # Useful for Docker, headless Linux, CI environments
31
+ # TESLA_TOKEN_FILE=~/.config/tescmd/tokens.json
32
+
33
+ # ─── Config & Cache ──────────────────────────────────────────────
34
+ # TESLA_CONFIG_DIR=~/.config/tescmd
35
+ # TESLA_CACHE_DIR=~/.cache/tescmd
36
+ # TESLA_CACHE_TTL=60
37
+ # TESLA_CACHE_ENABLED=true
38
+
39
+ # ─── Output Format & Display Units ───────────────────────────────
40
+ # Force output format: rich (TTY default), json (piped default), quiet
41
+ # TESLA_OUTPUT_FORMAT=
42
+
43
+ # Display unit preferences
44
+ # TESLA_TEMP_UNIT=F # F or C
45
+ # TESLA_DISTANCE_UNIT=mi # mi or km
46
+ # TESLA_PRESSURE_UNIT=psi # psi or bar
47
+
48
+ # ─── Command Protocol & Key Hosting ──────────────────────────────
49
+ # Command signing: auto (default), signed, unsigned
50
+ # TESLA_COMMAND_PROTOCOL=auto
51
+
52
+ # Domain for key hosting (set by tescmd setup)
53
+ # TESLA_DOMAIN=
54
+
55
+ # Key hosting method: github or tailscale
56
+ # TESLA_HOSTING_METHOD=
57
+
58
+ # GitHub repo for key deployment (e.g. username/username.github.io)
59
+ # TESLA_GITHUB_REPO=
60
+
61
+ # Setup tier: readonly or full
62
+ # TESLA_SETUP_TIER=
63
+
64
+ # Config profile name
65
+ # TESLA_PROFILE=default
66
+
67
+ # ─── MCP Server ──────────────────────────────────────────────────
68
+ # Credentials for MCP OAuth 2.1 authentication
69
+ # Required for tescmd serve and tescmd mcp serve
70
+ # TESCMD_MCP_CLIENT_ID=
71
+ # TESCMD_MCP_CLIENT_SECRET=
72
+
73
+ # ─── OpenClaw ────────────────────────────────────────────────────
74
+ # Gateway authentication token
75
+ # OPENCLAW_GATEWAY_TOKEN=
@@ -5,8 +5,9 @@ dist/
5
5
  build/
6
6
  .venv/
7
7
  .env
8
+ .env.bak
8
9
  *.pem
9
10
  .mypy_cache/
10
11
  .pytest_cache/
11
12
  .ruff_cache/
12
- .coverage
13
+ .coverage
@@ -5,6 +5,65 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.1] - 2026-02-02
9
+
10
+ ### Added
11
+
12
+ - **README overhaul** — header banner, logo, new "What It Does" summary section, expanded Prerequisites table with Python 3.11+, pip, Tesla account, and helpful links
13
+ - **Tailscale Funnel auto-detection in auth setup** — `_interactive_setup` now detects Tailscale and offers to start Funnel so Tesla can verify the origin URL during Developer Portal configuration; cleans up Funnel on exit
14
+ - **Tailscale hostname passthrough** — setup wizard forwards detected Tailscale hostname to auth flow, showing a concrete "Also add" origin URL instead of the generic placeholder
15
+ - **Comprehensive agent skill documentation** — expanded skill covering all command groups, triggers, and OpenClaw dispatch
16
+
17
+ ### Fixed
18
+
19
+ - **Cross-platform file permissions** — OpenClaw gateway key file now uses `secure_file()` from `_internal.permissions` instead of raw `chmod(0o600)`, adding proper Windows support via `icacls`
20
+ - **12-factor app compliance** — config, disposability, and concurrency improvements across the codebase
21
+ - **Documentation accuracy** — corrected `media_adjust_volume` tool name in MCP docs; emphasized MCP/OpenClaw over direct CLI for cost savings in agent skill
22
+
23
+ ## [0.3.0] - 2026-02-01
24
+
25
+ ### Added
26
+
27
+ - **Unified `tescmd serve`** — single command combining MCP server, Fleet Telemetry streaming, cache warming, and optional OpenClaw bridge; full-screen TUI dashboard shows live telemetry, MCP status, tunnel URL, sink count, and connection health
28
+ - **TUI dashboard** — 8-panel Textual layout with telemetry field table, server info sidebar, activity log, request log, and filter status; command palette (`ctrl+p`), keybindings (`q` to quit, `f` to toggle filters), clean graceful shutdown
29
+ - **OpenClaw Bridge** — `tescmd openclaw bridge [VIN]` streams Fleet Telemetry to an OpenClaw Gateway with configurable delta+throttle filtering per field; supports `--dry-run` for JSONL output without a gateway connection
30
+ - **OpenClaw node role** — bidirectional command dispatch over the gateway WebSocket; bots send commands via the gateway that are forwarded as `node.invoke.request` events, routed through `CommandDispatcher` with read/write separation, tier enforcement, and VCSEC signing guards
31
+ - **Trigger subscription system** — `trigger.create`, `trigger.delete`, `trigger.list`, `trigger.poll` commands let bots register conditions on any telemetry field; supports operators `lt`, `gt`, `lte`, `gte`, `eq`, `neq`, `changed`, `enter`, `leave`; one-shot and persistent modes with configurable cooldown; max 100 triggers, 500 pending notifications
32
+ - **Geofence triggers** — `enter`/`leave` operators on Location field detect boundary crossings using haversine distance; fires only on actual crossing (not "already inside"), requires previous position for comparison
33
+ - **Trigger convenience aliases** — `cabin_temp.trigger`, `outside_temp.trigger`, `battery.trigger`, `location.trigger` pre-fill the field name so bots don't need to know raw telemetry field names
34
+ - **Trigger notification delivery** — dual-channel: OpenClaw push events (`trigger.fired`) for connected bots, and MCP polling via `trigger_poll` tool for agent frameworks
35
+ - **`system.run` meta-dispatch** — allows bots to invoke any registered handler by name with alias mapping (e.g., `door_lock` → `door.lock`, `auto_conditioning_start` → `climate.on`); guards against recursive self-dispatch
36
+ - **MCP Server** — `tescmd mcp serve` exposes all tescmd commands as MCP tools for Claude Desktop/Code and other agent frameworks; supports stdio and streamable-http transports with OAuth 2.1 authentication
37
+ - **MCP custom tool registry** — `MCPServer.register_custom_tool()` allows runtime registration of non-CLI tools (used by trigger system); custom tools appear alongside CLI-backed tools with proper schemas
38
+ - **Agent skill** — `skills/tescmd/SKILL.md` teaches AI agents how to use every tescmd command group with examples, parameter types, and common patterns
39
+ - **Reusable telemetry session** — extracted shared telemetry lifecycle (server → tunnel → partner registration → fleet config → cleanup) into `telemetry/setup.py` for use by serve, stream, and bridge commands
40
+ - **Dual-gate telemetry filter** — `openclaw/filters.py` combines delta threshold (value change) and throttle interval (minimum time between emissions) to reduce noise in telemetry streams; includes haversine distance for location fields
41
+ - **OpenClaw event emitter** — maps telemetry fields to OpenClaw `req:agent` event payloads (location, battery, temperature, speed, charge state transitions, security changes)
42
+ - **Gateway WebSocket client** — implements the OpenClaw operator protocol (challenge → connect → hello-ok handshake) with Ed25519 device key signing and exponential backoff reconnection
43
+ - **Bridge configuration** — `BridgeConfig` pydantic model with per-field filter settings, loadable from JSON file or CLI flags
44
+ - **CSV telemetry logging** — wide-format CSV log with one row per frame and one column per subscribed field; written to `~/.config/tescmd/logs/` by default, disable with `--no-log`
45
+ - **Cache sink** — telemetry-driven cache warming keeps read-command cache fresh while telemetry is active, making agent reads free
46
+ - **Frame fanout** — `FrameFanout` distributes decoded telemetry frames to multiple sinks (dashboard, CSV, cache, OpenClaw bridge, triggers) in parallel
47
+ - **Telemetry field mapper** — `telemetry/mapper.py` maps protobuf field names to tescmd model field names with unit conversion
48
+ - **Command guards in dispatcher** — extracted `check_command_guards()` (tier check + VCSEC signing requirement) into a shared function called by both CLI and OpenClaw dispatcher paths
49
+ - **Standard dependencies** — `websockets>=14.0`, `mcp>=1.0`, and `textual>=1.0` now included in default install
50
+
51
+ ### Changed
52
+
53
+ - Refactored `_cmd_telemetry_stream` in `cli/vehicle.py` to use the shared `telemetry_session()` context manager (no behavioral change)
54
+ - Proto-aligned telemetry field definitions with proper `interval_seconds` for delta fields
55
+ - Separated JSON serialization errors from WebSocket connection errors in gateway send path for clearer diagnostics
56
+ - Raised trigger push notification and lifecycle event failures from debug to warning level for observability
57
+
58
+ ### Fixed
59
+
60
+ - Fixed prompt for re-authentication when refresh token is expired or revoked (was failing silently)
61
+ - Fixed delta fields requiring `interval_seconds` configuration
62
+ - Fixed log file paths not shown on quit
63
+ - Added `exc_info` to MCP custom tool error logging for full stack traces
64
+ - Added JSON parse guards and explicit parameter validation in MCP tool wrappers
65
+ - Added `system.run` recursive self-dispatch guard
66
+
8
67
  ## [0.2.0] - 2026-01-31
9
68
 
10
69
  ### Added
@@ -18,7 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
77
  - **Tailscale Funnel integration** — automatic Funnel start/stop with cert retrieval for Fleet Telemetry HTTPS requirement
19
78
  - **JSONL output** — piped mode emits one JSON line per telemetry frame for scripting and log ingestion
20
79
  - **TunnelError hierarchy** — `TunnelError` parent with `TailscaleError` subtype; actionable install/setup guidance
21
- - **Optional dependency group** — `pip install tescmd[telemetry]` adds `websockets>=14.0`
80
+ - **websockets dependency** — `websockets>=14.0` now included in default install
22
81
  - **Tailscale key hosting** — `tescmd key deploy --method tailscale` hosts the public key via Tailscale Funnel at `https://<machine>.tailnet.ts.net/.well-known/appspecific/com.tesla.3p.public-key.pem`; auto-detected as second priority after GitHub Pages
23
82
  - **Key hosting priority chain** — setup wizard and `key deploy` auto-detect the best hosting method: GitHub Pages → Tailscale Funnel → manual; `--method` flag overrides auto-detection
24
83
  - **`TESLA_HOSTING_METHOD` setting** — persists the chosen key hosting method (`github`, `tailscale`) across sessions
tescmd-0.3.1/CLAUDE.md ADDED
@@ -0,0 +1,131 @@
1
+ # CLAUDE.md — Project Context for Claude Code
2
+
3
+ ## Project Overview
4
+
5
+ **tescmd** is a Python 3.11+ CLI for querying and controlling Tesla vehicles via the [Tesla Fleet API](https://developer.tesla.com/docs/fleet-api). It covers auth, vehicle queries/commands, energy products, Supercharger billing, Fleet Telemetry streaming, OpenClaw bridge, and an MCP server for agent integration.
6
+
7
+ ## Tech Stack
8
+
9
+ - **Python 3.11+**, **pydantic v2**, **click**, **httpx** (async), **rich**, **cryptography**, **protobuf**, **keyring**, **python-dotenv**
10
+ - **websockets** (telemetry + OpenClaw), **mcp** (MCP server) — core dependencies
11
+ - Optional: **bleak** (`[ble]` extra — BLE key enrollment)
12
+
13
+ ## Project Structure
14
+
15
+ ```
16
+ src/tescmd/
17
+ ├── cli/ # Click CLI layer
18
+ │ ├── main.py # Root group, AppContext, _register_commands()
19
+ │ ├── _options.py # Shared Click options/decorators (@global_options)
20
+ │ ├── _client.py # API client builders, auto_wake, cached_vehicle_data, cached_api_call, TTL tiers
21
+ │ ├── auth.py, cache.py, charge.py, billing.py, climate.py, security.py
22
+ │ ├── status.py, trunk.py, vehicle.py, media.py, nav.py, partner.py
23
+ │ ├── software.py, energy.py, user.py, sharing.py, raw.py, key.py
24
+ │ ├── setup.py # Interactive first-run wizard
25
+ │ ├── serve.py # Unified MCP + telemetry + OpenClaw command
26
+ │ ├── openclaw.py # Standalone openclaw bridge command
27
+ │ └── mcp_cmd.py # mcp serve command
28
+ ├── api/ # HTTP client + domain APIs (composition pattern)
29
+ │ ├── client.py # TeslaFleetClient (base HTTP, auth headers, retries)
30
+ │ ├── vehicle.py, command.py, signed_command.py, energy.py
31
+ │ ├── charging.py, partner.py, sharing.py, user.py
32
+ │ └── errors.py # AuthError, VehicleAsleepError, TierError, TunnelError, etc.
33
+ ├── models/ # Pydantic v2 models (vehicle, energy, user, auth, command, config)
34
+ ├── auth/ # OAuth2 PKCE, token_store (keyring + file fallback), callback server
35
+ ├── protocol/ # Vehicle Command Protocol (ECDH sessions, HMAC signing, protobuf)
36
+ ├── crypto/ # EC key gen, ECDH, Schnorr signatures
37
+ ├── cache/ # File-based JSON cache with tiered TTLs
38
+ ├── output/ # OutputFormatter, RichOutput (DisplayUnits), JsonOutput
39
+ ├── telemetry/ # Fleet Telemetry streaming
40
+ │ ├── setup.py # Reusable telemetry_session() context manager
41
+ │ ├── server.py, decoder.py, fields.py, dashboard.py, tailscale.py
42
+ ├── openclaw/ # OpenClaw bridge
43
+ │ ├── config.py # BridgeConfig, FieldFilter, NodeCapabilities (pydantic)
44
+ │ ├── filters.py # DualGateFilter (delta + throttle), haversine()
45
+ │ ├── emitter.py # EventEmitter (telemetry → OpenClaw events)
46
+ │ ├── gateway.py # GatewayClient (WebSocket, node protocol, Ed25519 auth)
47
+ │ ├── bridge.py # TelemetryBridge orchestrator, build_openclaw_pipeline()
48
+ │ ├── dispatcher.py # CommandDispatcher (reads, writes, triggers, system.run)
49
+ │ └── telemetry_store.py # In-memory latest-value cache for telemetry fields
50
+ ├── triggers/ # Trigger subscription system
51
+ │ ├── models.py # TriggerOperator, TriggerCondition, TriggerDefinition, TriggerNotification
52
+ │ └── manager.py # TriggerManager (evaluation, cooldown, delivery, geofencing)
53
+ ├── mcp/ # MCP server
54
+ │ └── server.py # MCPServer (FastMCP, CliRunner + custom callable tools)
55
+ ├── deploy/ # Key hosting (GitHub Pages, Tailscale Funnel)
56
+ └── _internal/ # vin.py, async_utils.py, permissions.py
57
+ ```
58
+
59
+ ## Coding Conventions
60
+
61
+ - **Type hints everywhere** — all function signatures, all variables where non-obvious
62
+ - **async/await** — all API calls are async; CLI entry points use `run_async()` helper
63
+ - **Pydantic models** — all API request/response payloads; all configuration
64
+ - **src layout** — code in `src/tescmd/`, tests in `tests/`
65
+ - **No star imports** — explicit imports only
66
+ - **Single responsibility** — CLI modules handle args + output, API modules handle HTTP
67
+ - **Composition over inheritance** — API classes wrap `TeslaFleetClient`, don't extend it
68
+ - **Error stream routing** — JSON/piped mode writes errors to stderr; Rich/TTY mode uses stdout
69
+
70
+ ## Key Patterns
71
+
72
+ **CLI command registration:** New command groups are registered in `cli/main.py:_register_commands()`. Each CLI module defines a Click group and is imported there.
73
+
74
+ **Global options propagation:** Use `@global_options` decorator from `cli/_options.py` on commands that need VIN, format, wake, cache flags. Options flow through `AppContext` via `@click.pass_obj`.
75
+
76
+ **API client construction:** `cli/_client.py` provides `get_vehicle_api(app_ctx)` → returns `(client, api)` tuple. Also `get_command_api()` for write commands (handles signed vs unsigned routing).
77
+
78
+ **Cache:** All read commands use `cached_api_call()` with scope-aware TTLs (STATIC 1h, SLOW 5m, DEFAULT 1m, FAST 30s). Write commands invalidate via `invalidate_cache_for_vin()` / `invalidate_cache_for_site()`.
79
+
80
+ **Output:** `OutputFormatter` auto-detects TTY → Rich, piped → JSON, `--quiet` → stderr only. JSON output uses a consistent `{ok, command, data, error, timestamp}` envelope.
81
+
82
+ **Telemetry session lifecycle:** `telemetry/setup.py:telemetry_session()` is an async context manager handling: server start → Tailscale tunnel → partner domain re-registration → fleet config → yield → cleanup. Used by both `cli/vehicle.py` (stream) and `cli/openclaw.py` (bridge).
83
+
84
+ **OpenClaw pipeline:** `TelemetryServer.on_frame` → `DualGateFilter.should_emit()` → `EventEmitter.to_event()` → `GatewayClient.send_event()`. The `TelemetryBridge` class wires these together. `build_openclaw_pipeline()` is the shared factory used by both `cli/openclaw.py` (standalone) and `cli/serve.py` (combined mode). Inbound commands flow: gateway `node.invoke.request` → `CommandDispatcher.dispatch()` → handler → `node.invoke.result`.
85
+
86
+ **Trigger system:** `TriggerManager` evaluates conditions on every telemetry frame (independent of the dual-gate filter). Supports numeric comparison (`lt`, `gt`, `eq`, etc.), `changed` detection, and geofence `enter`/`leave` with haversine distance. One-shot and persistent (with cooldown) firing modes. Notifications delivered via OpenClaw push and MCP polling (`trigger.poll`).
87
+
88
+ **MCP tools:** `mcp/server.py` maps tool names to CLI arg lists via `_CliToolDef` dataclasses. `invoke_tool()` uses `CliRunner.invoke(cli, ["--format", "json", "--wake", *args], env=os.environ.copy())`. Custom tools (e.g. trigger CRUD) use `_CustomToolDef` with direct callable handlers registered via `register_custom_tool()`. Read/write tools are separated for `readOnlyHint` annotations. HTTP transport uses OAuth 2.1 via `_InMemoryOAuthProvider` (auto-approve authorization, dynamic client registration, in-memory token storage) with `_PermissiveClient` wrappers that accept any redirect URI. DNS rebinding protection is configured to allow the Tailscale Funnel hostname when `public_url` is set.
89
+
90
+ ## Build & Test
91
+
92
+ ```bash
93
+ # Build
94
+ python -m build # hatchling via pyproject.toml
95
+
96
+ # Test (parallel by default via pytest-xdist)
97
+ pytest # all ~1600 tests
98
+ pytest tests/openclaw/ -x -v # openclaw tests
99
+ pytest tests/triggers/ -x -v # trigger tests
100
+ pytest tests/mcp/ -x -v # mcp tests
101
+ pytest -m e2e # live API smoke tests (needs TESLA_ACCESS_TOKEN)
102
+
103
+ # Lint
104
+ ruff check src/ tests/
105
+ ruff format src/ tests/
106
+ mypy src/
107
+ ```
108
+
109
+ ## Environment Variables
110
+
111
+ | Variable | Description |
112
+ |---|---|
113
+ | `TESLA_CLIENT_ID` / `TESLA_CLIENT_SECRET` | OAuth2 app credentials |
114
+ | `TESLA_VIN` | Default vehicle VIN |
115
+ | `TESLA_REGION` | API region: `na`, `eu`, `cn` |
116
+ | `TESLA_ACCESS_TOKEN` / `TESLA_REFRESH_TOKEN` | Direct token override |
117
+ | `TESLA_TOKEN_FILE` | File path for token storage (skips keyring) |
118
+ | `TESLA_CONFIG_DIR` | Config directory (default: `~/.config/tescmd`) |
119
+ | `TESLA_CACHE_DIR` / `TESLA_CACHE_TTL` / `TESLA_CACHE_ENABLED` | Cache settings |
120
+ | `TESLA_OUTPUT_FORMAT` | Force format: `rich`, `json`, `quiet` |
121
+ | `TESLA_COMMAND_PROTOCOL` | `auto` (default), `signed`, `unsigned` |
122
+ | `TESLA_TEMP_UNIT` / `TESLA_DISTANCE_UNIT` / `TESLA_PRESSURE_UNIT` | Display units |
123
+ | `TESLA_DOMAIN` / `TESLA_HOSTING_METHOD` / `TESLA_GITHUB_REPO` | Key hosting |
124
+ | `TESLA_SETUP_TIER` | `readonly` or `full` |
125
+ | `TESLA_PROFILE` | Active config profile |
126
+ | `OPENCLAW_GATEWAY_TOKEN` | OpenClaw gateway auth token |
127
+
128
+
129
+ ## Additional Resources
130
+
131
+ Tesla's published vehicle-command proto omits some VehicleAction fields (e.g. navigation commands). The Teslemetry project maintains a more complete proto: https://github.com/Teslemetry/python-tesla-fleet-api/blob/main/proto/car_server.proto
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tescmd
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: A Python CLI for querying and controlling Tesla vehicles via the Fleet API
5
5
  Project-URL: Homepage, https://github.com/oceanswave/tescmd
6
6
  Project-URL: Repository, https://github.com/oceanswave/tescmd
@@ -26,11 +26,16 @@ Requires-Dist: click>=8.1
26
26
  Requires-Dist: cryptography>=42.0
27
27
  Requires-Dist: httpx>=0.27
28
28
  Requires-Dist: keyring>=25.0
29
+ Requires-Dist: mcp>=1.0
29
30
  Requires-Dist: protobuf>=5.29
30
31
  Requires-Dist: pydantic-settings>=2.0
31
32
  Requires-Dist: pydantic>=2.0
32
33
  Requires-Dist: python-dotenv>=1.0
33
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
34
39
  Provides-Extra: ble
35
40
  Requires-Dist: bleak>=0.22; extra == 'ble'
36
41
  Provides-Extra: dev
@@ -44,23 +49,33 @@ Requires-Dist: pytest-httpx>=0.34; extra == 'dev'
44
49
  Requires-Dist: pytest-xdist>=3.0; extra == 'dev'
45
50
  Requires-Dist: pytest>=8.0; extra == 'dev'
46
51
  Requires-Dist: ruff>=0.8; extra == 'dev'
47
- Provides-Extra: telemetry
48
- Requires-Dist: websockets>=14.0; extra == 'telemetry'
49
52
  Description-Content-Type: text/markdown
50
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
+
51
58
  # tescmd
52
59
 
53
- [![PyPI](https://img.shields.io/pypi/v/tescmd)](https://pypi.org/project/tescmd/)
54
- [![Python](https://img.shields.io/pypi/pyversions/tescmd)](https://pypi.org/project/tescmd/)
55
- [![Build](https://img.shields.io/github/actions/workflow/status/oceanswave/tescmd/test.yml?branch=main&label=build)](https://github.com/oceanswave/tescmd/actions/workflows/test.yml)
56
- [![License](https://img.shields.io/github/license/oceanswave/tescmd)](LICENSE)
57
- [![GitHub Release](https://img.shields.io/github/v/release/oceanswave/tescmd)](https://github.com/oceanswave/tescmd/releases)
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>
58
67
 
59
68
  A Python CLI for querying and controlling Tesla vehicles via the Fleet API — built for both human operators and AI agents.
60
69
 
70
+ ## What It Does
71
+
72
+ tescmd gives you full command-line access to Tesla's Fleet API: check battery and charge status, lock or unlock doors, control climate, open trunks, send navigation waypoints, manage Powerwalls, stream live telemetry, and more. It handles OAuth2 authentication, token refresh, key enrollment, command signing, and response caching so you don't have to. Every command works in both interactive (Rich tables) and scripted (JSON) modes, and an MCP server lets AI agents call any command as a tool.
73
+
61
74
  ## Why tescmd?
62
75
 
63
- Tesla's Fleet API gives developers full access to vehicle data and commands, but working with it directly means juggling OAuth2 PKCE flows, token refresh, regional endpoints, key enrollment, and raw JSON responses. tescmd wraps all of that into a single command-line tool that handles authentication, token management, and output formatting so you can focus on what you actually want to do — check your battery, find your car, or control your vehicle.
76
+ Tesla's Fleet API gives developers full access to vehicle data and commands, but working with it directly means juggling OAuth2 PKCE flows, token refresh, regional endpoints, key enrollment, and raw JSON responses.
77
+
78
+ tescmd wraps all of that into a single command-line tool that handles authentication, token management, and output formatting so you can focus on what you actually want to do — check your battery, find your car, or control your vehicle.
64
79
 
65
80
  tescmd is designed to work as a tool that AI agents can invoke directly. Platforms like [OpenClaw](https://openclaw.ai/), [Claude Desktop](https://claude.ai), and other agent frameworks can call tescmd commands, parse the structured JSON output, and take actions on your behalf — "lock my car", "what's my battery at?", "start climate control". The deterministic JSON output, meaningful exit codes, cost-aware wake confirmation, and `--wake` opt-in flag make it safe for autonomous agent use without surprise billing.
66
81
 
@@ -73,7 +88,11 @@ tescmd is designed to work as a tool that AI agents can invoke directly. Platfor
73
88
  - **Tier enforcement** — readonly tier blocks write commands with clear guidance to upgrade
74
89
  - **Energy products** — Powerwall live status, site info, backup reserve, operation mode, storm mode, time-of-use settings, charging history, calendar history, grid import/export
75
90
  - **User & sharing** — account info, region, orders, feature flags, driver management, vehicle sharing invites
76
- - **Fleet Telemetry streaming** — `tescmd vehicle telemetry stream` starts a real-time dashboard with push-based data from your vehicle via Tailscale Funnelno polling, 99%+ cost reduction
91
+ - **Live Dashboard** — `tescmd serve` launches a full-screen TUI showing live telemetry data, MCP server info, tunnel URL, sink count, and cache stats all in a scrollable, interactive terminal UI powered by Textual
92
+ - **Fleet Telemetry streaming** — `tescmd serve` (or `tescmd vehicle telemetry stream`) receives push-based data from your vehicle via Tailscale Funnel — no polling, 99%+ cost reduction. Telemetry sessions produce a wide-format CSV log by default
93
+ - **OpenClaw Bridge** — `tescmd serve --openclaw ws://...` streams filtered telemetry to an OpenClaw Gateway with configurable delta+throttle filtering per field; supports bidirectional command dispatch so bots can send vehicle commands back through the gateway
94
+ - **Trigger subscriptions** — register conditions on any telemetry field (battery < 20%, speed > 80, location enters geofence) and get notified via OpenClaw push events or MCP polling; supports one-shot and persistent modes with cooldown
95
+ - **MCP Server** — `tescmd serve` (or `tescmd mcp serve`) exposes all commands as MCP tools for Claude.ai, Claude Desktop, Claude Code, and other agent frameworks via OAuth 2.1
77
96
  - **Universal response caching** — all read commands are cached with tiered TTLs (1h for specs/warranty, 5m for fleet lists, 1m standard, 30s for location-dependent); bots can call tescmd as often as needed — within the TTL window, responses are instant and free
78
97
  - **Cost-aware wake** — prompts before sending billable wake API calls; `--wake` flag for scripts that accept the cost
79
98
  - **Guided OAuth2 setup** — `tescmd auth login` walks you through browser-based authentication with PKCE
@@ -100,24 +119,35 @@ tescmd charge status # Check battery and charging state
100
119
  tescmd vehicle info # Full vehicle data snapshot
101
120
  tescmd climate on --wake # Turn on climate (wakes vehicle if asleep)
102
121
  tescmd security lock --wake # Lock the car
103
- tescmd vehicle telemetry stream # Real-time telemetry dashboard
122
+ tescmd serve 5YJ3... # MCP + telemetry TUI dashboard + CSV log
123
+ tescmd serve --no-mcp # Telemetry-only TUI dashboard
104
124
  ```
105
125
 
106
126
  Every read command is cached — repeat calls within the TTL window are instant and free.
107
127
 
108
128
  ## Prerequisites
109
129
 
110
- The following tools should be installed and authenticated before running `tescmd setup`:
130
+ | Requirement | Required | What it is | Why tescmd needs it |
131
+ |---|---|---|---|
132
+ | **Python 3.11+** | Yes | The programming language runtime that runs tescmd | tescmd is a Python package — you need Python installed to use it |
133
+ | **pip** | Yes | Python's package installer (ships with Python) | Used to install tescmd and its dependencies via `pip install tescmd` |
134
+ | **Tesla account** | Yes | A [tesla.com](https://www.tesla.com) account linked to a vehicle or energy product | tescmd authenticates via OAuth2 against your Tesla account to access the Fleet API |
135
+ | **Git** | Yes | Version control tool ([git-scm.com](https://git-scm.com)) | Used during setup for key hosting via GitHub Pages |
136
+ | **GitHub CLI** (`gh`) | Recommended | GitHub's command-line tool ([cli.github.com](https://cli.github.com)) — authenticate with `gh auth login` | Auto-creates a `*.github.io` site to host your public key at the `.well-known` path Tesla requires |
137
+ | **Tailscale** | Recommended | Mesh VPN with public tunneling ([tailscale.com](https://tailscale.com)) — authenticate with `tailscale login` | Provides a public HTTPS URL for key hosting and Fleet Telemetry streaming with zero infrastructure setup |
138
+
139
+ ### Self-Hosting with Tailscale (No Domain Required)
140
+
141
+ If you have **Tailscale** installed with Funnel enabled, you don't need a custom domain or GitHub Pages at all. Tailscale Funnel gives you a public HTTPS URL (`<machine>.tailnet.ts.net`) that serves both your public key and (optionally) Fleet Telemetry streaming — all from your local machine with zero infrastructure setup.
111
142
 
112
- | Tool | Required | Purpose | Auth |
113
- |------|----------|---------|------|
114
- | **Git** | Yes | Version control, repo management | N/A |
115
- | **GitHub CLI** (`gh`) | Recommended | Auto-creates `*.github.io` domain for key hosting | `gh auth login` |
116
- | **Tailscale** | Optional | Key hosting via Funnel + Fleet Telemetry streaming | `tailscale login` |
143
+ ```bash
144
+ # Install Tailscale, enable Funnel in your tailnet ACL, then:
145
+ tescmd setup # wizard auto-detects Tailscale and offers it as the hosting method
146
+ ```
117
147
 
118
- Without the GitHub CLI, `tescmd setup` will try Tailscale Funnel for key hosting (requires Funnel enabled in your tailnet ACL). Without either, you'll need to manually host your public key at the Tesla-required `.well-known` path on your own domain.
148
+ This is the fastest path to a working setup: Tailscale handles TLS certificates, NAT traversal, and public DNS automatically. The tradeoff is that your machine needs to be running for Tesla to reach your key and for telemetry streaming to work. For always-on key hosting with offline machines, use GitHub Pages instead.
119
149
 
120
- For telemetry streaming, you need **Tailscale** with Funnel enabled.
150
+ Without either GitHub CLI or Tailscale, you'll need to manually host your public key at the Tesla-required `.well-known` path on your own domain.
121
151
 
122
152
  ## Installation
123
153
 
@@ -214,6 +244,9 @@ Check which backend is active with `tescmd status` — the output includes a `To
214
244
  | `sharing` | `add-driver`, `remove-driver`, `create-invite`, `redeem-invite`, `revoke-invite`, `list-invites` | Vehicle sharing and driver management |
215
245
  | `key` | `generate`, `deploy`, `validate`, `show`, `enroll`, `unenroll` | Key management and enrollment |
216
246
  | `partner` | `public-key`, `telemetry-error-vins`, `telemetry-errors` | Partner account endpoints (require client credentials) |
247
+ | `serve` | *(unified server)* | Combined MCP + telemetry + OpenClaw TUI dashboard with trigger subscriptions |
248
+ | `openclaw` | `bridge` | Standalone OpenClaw bridge with bidirectional command dispatch |
249
+ | `mcp` | `serve` | Standalone MCP server exposing all commands as agent tools |
217
250
  | `cache` | `status`, `clear` | Response cache management |
218
251
  | `raw` | `get`, `post` | Arbitrary Fleet API endpoint access |
219
252
 
@@ -295,7 +328,7 @@ A naive script that polls `vehicle_data` every 5 minutes generates **4-5 billabl
295
328
 
296
329
  | | Without tescmd | With tescmd |
297
330
  |---|---|---|
298
- | Vehicle asleep, check battery | 408 error (billable) + wake (billable) + poll (billable) + data (billable) = **4+ requests** | Cache miss → prompt user → user wakes via Tesla app (free) → retry → data (billable) = **1 request** |
331
+ | Vehicle asleep, check battery | 408 error (billable) + wake (billable) + poll (billable) + data (billable) = **4+ requests** | Data attempt408 (billable) → prompt user → user wakes via Tesla app (free) → retry → data (billable) = **2 requests** |
299
332
  | Check battery again 30s later | Another 4+ requests | **0 requests** (cache hit) |
300
333
  | 10 checks in 1 minute | **40+ billable requests** | **1 billable request** + 9 cache hits |
301
334
 
@@ -350,26 +383,35 @@ Configure via environment variables:
350
383
  Tesla's Fleet Telemetry lets your vehicle push real-time data directly to your server — no polling, no per-request charges. tescmd handles all the setup:
351
384
 
352
385
  ```bash
353
- # Install telemetry dependencies
354
- pip install tescmd[telemetry]
386
+ # Full-screen TUI with live telemetry + MCP server
387
+ tescmd serve 5YJ3...
355
388
 
356
- # Stream real-time data (Rich dashboard in TTY, JSONL when piped)
357
- tescmd vehicle telemetry stream
389
+ # Telemetry-only mode (full-screen TUI, no MCP)
390
+ tescmd serve 5YJ3... --no-mcp
358
391
 
359
392
  # Select field presets
360
- tescmd vehicle telemetry stream --fields driving # Speed, location, power
361
- tescmd vehicle telemetry stream --fields charging # Battery, voltage, current
362
- tescmd vehicle telemetry stream --fields climate # Temps, HVAC state
363
- tescmd vehicle telemetry stream --fields all # Everything (120+ fields)
393
+ tescmd serve 5YJ3... --fields driving # Speed, location, power
394
+ tescmd serve 5YJ3... --fields charging # Battery, voltage, current
395
+ tescmd serve 5YJ3... --fields all # Everything (120+ fields)
364
396
 
365
397
  # Override polling interval
366
- tescmd vehicle telemetry stream --interval 5 # Every 5 seconds
398
+ tescmd serve 5YJ3... --interval 5 # Every 5 seconds
399
+
400
+ # JSONL output for scripting (non-TTY / piped)
401
+ tescmd serve 5YJ3... --no-mcp --format json | jq .
402
+
403
+ # Disable CSV log
404
+ tescmd serve 5YJ3... --no-log
367
405
 
368
- # JSONL output for scripting
369
- tescmd vehicle telemetry stream --format json | jq .
406
+ # Legacy Rich Live dashboard
407
+ tescmd serve 5YJ3... --legacy-dashboard
370
408
  ```
371
409
 
372
- **Requires Tailscale** with Funnel enabled. The stream command starts a local WebSocket server, exposes it via Tailscale Funnel (handles TLS + NAT traversal), configures Tesla to push data to it, and renders an interactive dashboard with live uptime counter, unit conversion, and connection status. Press `q` to stop — cleanup messages show each step (removing telemetry config, restoring partner domain, stopping tunnel).
410
+ **Requires Tailscale** with Funnel enabled. The serve command starts a local WebSocket server, exposes it via Tailscale Funnel (handles TLS + NAT traversal), configures Tesla to push data to it, and renders a full-screen TUI with live telemetry data, server info (MCP URL, tunnel, sinks), unit conversion, and connection status. Press `q` to quit.
411
+
412
+ By default, telemetry sessions write a wide-format CSV log to `~/.config/tescmd/logs/` with one row per frame and one column per subscribed field. Disable with `--no-log`.
413
+
414
+ `tescmd vehicle telemetry stream` is an alias for `tescmd serve --no-mcp`.
373
415
 
374
416
  ### Telemetry vs Polling Costs
375
417
 
@@ -481,6 +523,8 @@ See [docs/development.md](docs/development.md) for detailed contribution guideli
481
523
  - [Command Reference](docs/commands.md) — detailed usage for every command
482
524
  - [API Costs](docs/api-costs.md) — detailed cost breakdown and savings calculations
483
525
  - [Bot Integration](docs/bot-integration.md) — JSON schema, exit codes, telemetry streaming, headless auth
526
+ - [OpenClaw Bridge](docs/openclaw.md) — gateway protocol, bidirectional commands, trigger subscriptions, geofencing
527
+ - [MCP Server](docs/mcp.md) — tool reference, authentication, custom tools, trigger polling
484
528
  - [Architecture](docs/architecture.md) — layered design, module responsibilities, design decisions
485
529
  - [Vehicle Command Protocol](docs/vehicle-command-protocol.md) — ECDH sessions and signed commands
486
530
  - [Authentication](docs/authentication.md) — OAuth2 PKCE flow, token storage, scopes
@@ -493,3 +537,7 @@ See [CHANGELOG.md](CHANGELOG.md) for release history.
493
537
  ## License
494
538
 
495
539
  MIT
540
+
541
+ <p align="center">
542
+ <img src="images/tescmd_logo.jpeg" alt="tescmd logo" width="180">
543
+ </p>