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.
- tescmd-0.3.1/.env.example +75 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/.gitignore +2 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/CHANGELOG.md +60 -1
- tescmd-0.3.1/CLAUDE.md +131 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/PKG-INFO +80 -32
- {tescmd-0.2.0 → tescmd-0.3.1}/README.md +74 -29
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/api-costs.md +26 -11
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/architecture.md +129 -11
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/authentication.md +52 -8
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/bot-integration.md +31 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/commands.md +263 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/development.md +13 -5
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/faq.md +22 -2
- tescmd-0.3.1/docs/mcp.md +416 -0
- tescmd-0.3.1/docs/openclaw.md +463 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/setup.md +72 -31
- {tescmd-0.2.0 → tescmd-0.3.1}/docs/vehicle-command-protocol.md +43 -12
- tescmd-0.3.1/images/tescmd_header.jpeg +0 -0
- tescmd-0.3.1/images/tescmd_logo.jpeg +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/pyproject.toml +14 -2
- tescmd-0.3.1/skills/tescmd/SKILL.md +524 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/__init__.py +1 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/client.py +41 -4
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/command.py +1 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/errors.py +5 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/signed_command.py +19 -14
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/oauth.py +5 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/server.py +6 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/token_store.py +8 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cache/response_cache.py +8 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/_client.py +142 -20
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/_options.py +2 -4
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/auth.py +96 -14
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/energy.py +2 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/main.py +27 -8
- tescmd-0.3.1/src/tescmd/cli/mcp_cmd.py +153 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/nav.py +3 -1
- tescmd-0.3.1/src/tescmd/cli/openclaw.py +169 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/security.py +7 -1
- tescmd-0.3.1/src/tescmd/cli/serve.py +923 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/setup.py +18 -7
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/sharing.py +2 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/status.py +1 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/trunk.py +8 -17
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/user.py +16 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/vehicle.py +135 -462
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/deploy/github_pages.py +8 -0
- tescmd-0.3.1/src/tescmd/mcp/__init__.py +7 -0
- tescmd-0.3.1/src/tescmd/mcp/server.py +648 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/auth.py +5 -2
- tescmd-0.3.1/src/tescmd/openclaw/__init__.py +23 -0
- tescmd-0.3.1/src/tescmd/openclaw/bridge.py +330 -0
- tescmd-0.3.1/src/tescmd/openclaw/config.py +167 -0
- tescmd-0.3.1/src/tescmd/openclaw/dispatcher.py +522 -0
- tescmd-0.3.1/src/tescmd/openclaw/emitter.py +175 -0
- tescmd-0.3.1/src/tescmd/openclaw/filters.py +123 -0
- tescmd-0.3.1/src/tescmd/openclaw/gateway.py +687 -0
- tescmd-0.3.1/src/tescmd/openclaw/telemetry_store.py +53 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/rich_output.py +46 -14
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/commands.py +2 -2
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/encoder.py +16 -13
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/payloads.py +132 -11
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/session.py +8 -5
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/signer.py +3 -17
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/__init__.py +9 -0
- tescmd-0.3.1/src/tescmd/telemetry/cache_sink.py +154 -0
- tescmd-0.3.1/src/tescmd/telemetry/csv_sink.py +180 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/dashboard.py +4 -4
- tescmd-0.3.1/src/tescmd/telemetry/fanout.py +49 -0
- tescmd-0.3.1/src/tescmd/telemetry/fields.py +427 -0
- tescmd-0.3.1/src/tescmd/telemetry/mapper.py +239 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/server.py +26 -19
- tescmd-0.3.1/src/tescmd/telemetry/setup.py +468 -0
- tescmd-0.3.1/src/tescmd/telemetry/tui.py +1716 -0
- tescmd-0.3.1/src/tescmd/triggers/__init__.py +18 -0
- tescmd-0.3.1/src/tescmd/triggers/manager.py +264 -0
- tescmd-0.3.1/src/tescmd/triggers/models.py +93 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_signed_command.py +2 -2
- tescmd-0.3.1/tests/cli/test_auth.py +301 -0
- tescmd-0.3.1/tests/cli/test_bugfixes.py +236 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_nav_exec.py +1 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_setup.py +76 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_trunk_exec.py +10 -23
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_vehicle_exec.py +1 -1
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_wake_confirmation.py +13 -1
- tescmd-0.3.1/tests/integration/test_serve.py +163 -0
- tescmd-0.3.1/tests/mcp/test_server.py +407 -0
- tescmd-0.3.1/tests/mcp/test_tools.py +86 -0
- tescmd-0.3.1/tests/openclaw/test_bridge.py +618 -0
- tescmd-0.3.1/tests/openclaw/test_config.py +196 -0
- tescmd-0.3.1/tests/openclaw/test_dispatcher.py +1128 -0
- tescmd-0.3.1/tests/openclaw/test_emitter.py +165 -0
- tescmd-0.3.1/tests/openclaw/test_filters.py +131 -0
- tescmd-0.3.1/tests/openclaw/test_gateway.py +943 -0
- tescmd-0.3.1/tests/openclaw/test_telemetry_store.py +106 -0
- tescmd-0.3.1/tests/output/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/output/test_rich_output.py +7 -7
- tescmd-0.3.1/tests/protocol/__init__.py +0 -0
- tescmd-0.3.1/tests/protocol/test_boombox.py +74 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_commands.py +15 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_encoder.py +22 -19
- tescmd-0.3.1/tests/protocol/test_navigation.py +142 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_session.py +3 -3
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_signer.py +3 -13
- tescmd-0.3.1/tests/telemetry/__init__.py +0 -0
- tescmd-0.3.1/tests/telemetry/test_cache_sink.py +277 -0
- tescmd-0.3.1/tests/telemetry/test_csv_sink.py +321 -0
- tescmd-0.3.1/tests/telemetry/test_fanout.py +105 -0
- tescmd-0.3.1/tests/telemetry/test_fields.py +119 -0
- tescmd-0.3.1/tests/telemetry/test_mapper.py +192 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_stream_cmd.py +2 -0
- tescmd-0.3.1/tests/telemetry/test_tui.py +600 -0
- tescmd-0.3.1/tests/triggers/__init__.py +0 -0
- tescmd-0.3.1/tests/triggers/test_manager.py +474 -0
- tescmd-0.3.1/tests/triggers/test_models.py +162 -0
- tescmd-0.2.0/CLAUDE.md +0 -430
- tescmd-0.2.0/src/tescmd/telemetry/fields.py +0 -248
- tescmd-0.2.0/tests/cli/test_auth.py +0 -74
- tescmd-0.2.0/tests/telemetry/test_fields.py +0 -73
- {tescmd-0.2.0 → tescmd-0.3.1}/.github/workflows/publish.yml +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/.github/workflows/test.yml +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/LICENSE +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/scripts/validate_fleet_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/spec/fleet_api_spec.json +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/__main__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/async_utils.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/permissions.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/_internal/vin.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/charging.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/partner.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/user.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/api/vehicle.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/auth/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/ble/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cache/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cache/keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/billing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/charge.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/climate.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/key.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/media.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/partner.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/raw.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/cli/software.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/config/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/ecdh.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/crypto/schnorr.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/deploy/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/deploy/tailscale_serve.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/command.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/config.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/user.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/models/vehicle.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/formatter.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/output/json_output.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/metadata.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/protobuf/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/protocol/protobuf/messages.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/py.typed +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/decoder.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/flatbuf.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_alert.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_alert_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_alert_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_connectivity.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_connectivity_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_connectivity_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_data.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_data_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_data_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_error.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_error_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_error_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_metric.proto +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_metric_pb2.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/protos/vehicle_metric_pb2.pyi +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/src/tescmd/telemetry/tailscale.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/_internal/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/_internal/test_async_utils.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/_internal/test_vin.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_client.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_command_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_energy_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_partner_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_sharing_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_user_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/api/test_vehicle_api.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_oauth.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_oauth_extended.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_server.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_token_store.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_token_store_fallback.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/auth/test_token_store_file.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/test_generic_cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/test_keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cache/test_response_cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/_helpers.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_auth_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_cache.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_cached_api_call.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_charge_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_cli_integration.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_climate_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_e2e_smoke.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_energy_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_error_handlers.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_key.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_key_enroll.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_key_unenroll.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_main_errors.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_media.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_media_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_nav.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_partner.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_raw.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_raw_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_security_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_setup_scope_check.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_sharing_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_software.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_software_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_status_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_tier_enforcement.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_user.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_user_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_vcsec_guard.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_vehicle_power_exec.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/cli/test_verbose.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/test_ecdh.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/test_keys.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/crypto/test_schnorr.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/deploy/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/deploy/test_github_pages.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/deploy/test_tailscale_serve.py +0 -0
- {tescmd-0.2.0/tests/models → tescmd-0.3.1/tests/integration}/__init__.py +0 -0
- {tescmd-0.2.0/tests/output → tescmd-0.3.1/tests/mcp}/__init__.py +0 -0
- {tescmd-0.2.0/tests/protocol → tescmd-0.3.1/tests/models}/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_auth.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_config.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_energy.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_sharing.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_user_models.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/models/test_vehicle.py +0 -0
- {tescmd-0.2.0/tests/telemetry → tescmd-0.3.1/tests/openclaw}/__init__.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/output/test_formatter.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/output/test_json_output.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/protocol/test_metadata.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/conftest.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_dashboard.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_decoder.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_flatbuf.py +0 -0
- {tescmd-0.2.0 → tescmd-0.3.1}/tests/telemetry/test_server.py +0 -0
- {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,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
|
-
- **
|
|
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.
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
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
|
-
- **
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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** |
|
|
331
|
+
| Vehicle asleep, check battery | 408 error (billable) + wake (billable) + poll (billable) + data (billable) = **4+ requests** | Data attempt → 408 (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
|
-
#
|
|
354
|
-
|
|
386
|
+
# Full-screen TUI with live telemetry + MCP server
|
|
387
|
+
tescmd serve 5YJ3...
|
|
355
388
|
|
|
356
|
-
#
|
|
357
|
-
tescmd
|
|
389
|
+
# Telemetry-only mode (full-screen TUI, no MCP)
|
|
390
|
+
tescmd serve 5YJ3... --no-mcp
|
|
358
391
|
|
|
359
392
|
# Select field presets
|
|
360
|
-
tescmd
|
|
361
|
-
tescmd
|
|
362
|
-
tescmd
|
|
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
|
|
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
|
-
#
|
|
369
|
-
tescmd
|
|
406
|
+
# Legacy Rich Live dashboard
|
|
407
|
+
tescmd serve 5YJ3... --legacy-dashboard
|
|
370
408
|
```
|
|
371
409
|
|
|
372
|
-
**Requires Tailscale** with Funnel enabled. The
|
|
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>
|