tesla-cli 4.8.1__tar.gz → 4.9.0__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.
Potentially problematic release.
This version of tesla-cli might be problematic. Click here for more details.
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/CHANGELOG.md +38 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/CLAUDE.md +1 -1
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/PKG-INFO +1 -5
- tesla_cli-4.9.0/cron-runt-check.sh +39 -0
- tesla_cli-4.9.0/plugins/claude-code/skills/energy/SKILL.md +74 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/order/SKILL.md +2 -2
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/pyproject.toml +1 -2
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/__init__.py +1 -1
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/app.py +150 -110
- tesla_cli-4.9.0/src/tesla_cli/api/routes/abrp.py +73 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/automations.py +131 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/ble.py +114 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/charge.py +360 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/dashcam.py +102 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/energy_mgmt.py +119 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/energy_prices.py +108 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/mqtt.py +85 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/notify.py +193 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/order.py +50 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/scenes.py +218 -0
- tesla_cli-4.9.0/src/tesla_cli/api/routes/service.py +127 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/vehicle.py +7 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Action-Bx3ITOrA.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Action-legacy-3WvXcEZ2.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Analytics-buNYOtEj.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Analytics-legacy-BuePPNnL.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Automations-4tvMUHNd.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Automations-legacy-CQbByZ9U.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/BarChart-C0vhuT9k.js +32 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/BarChart-legacy-D6VWK1UH.js +10 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ChargeContent-iS7RMYje.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ChargeContent-legacy-B-HEhFFx.js +1 -0
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ClimateContent-legacy-vnEmaAAb.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ClimateContent-legacy-BQrDkZQd.js +1 -1
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ClimateContent-nHWdn2-B.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ControlsContent-BBBfJYQI.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ControlsContent-legacy-DtgtKrEG.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Dossier-CdecB5e7.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Dossier-legacy-FePjUmKv.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Energy-C6k2P_bp.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Energy-legacy-WmNeX_MR.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Icons-BPrRnLWP.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Icons-legacy-CacO0o_R.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Navigation-CuZJEwUU.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Navigation-legacy-DoiCAHN8.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Order-legacy-BIct8DQc.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Order-m0uoum_X.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ScheduleContent-BFuYRCdg.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/ScheduleContent-legacy-C57h_D4F.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Settings-Bim_v6Qk.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/Settings-legacy--XOAgFSe.js +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/VehicleMap-CFi1lT_-.js +4 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/VehicleMap-a6kZMJSm.css +1 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/VehicleMap-legacy-DvN0TFdZ.js +5 -0
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/index-Dn7HrE6f.css → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/index-B-8ZqtaE.css +1 -1
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/index-CYkv-WXf.js +265 -0
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/index-legacy-EzXM-nsI.js +351 -0
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-BgwEQWW6-BfKDwSA5.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-BgwEQWW6-Z0UahNeN.js +2 -2
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-BgwEQWW6-legacy-yCIQxmQv.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-BgwEQWW6-legacy-DjxjLUeR.js +1 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-CBzELu-H-cPBKnAaJ.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-CBzELu-H-CyPNfgft.js +2 -2
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-CBzELu-H-legacy-2r1u5Khh.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-CBzELu-H-legacy-BAKv_5SE.js +2 -2
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-CU1SSH8_-P0od39fw.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-CU1SSH8_-CNdgjVXB.js +1 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-CU1SSH8_-legacy-B_i4fNzM.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-CU1SSH8_-legacy-BvckqPS3.js +1 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-CneGxKsZ-legacy-BusG5VYv.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-CneGxKsZ-legacy-BIucGu0r.js +1 -1
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-CneGxKsZ-n-afBYQi.js +3 -0
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-Cwv-vmkN-legacy-DfmkzYoA.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-Cwv-vmkN-legacy-zbWap398.js +1 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-Cwv-vmkN-CWemJXeI.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-Cwv-vmkN-tjrN1zNk.js +2 -2
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-Cz5nLPGT-_FdcV1Vv.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-Cz5nLPGT-DMY47GKb.js +1 -1
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-Cz5nLPGT-legacy-BOhdpZea.js +5 -0
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-D6Ynv7Xh-BEhybWNo.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-D6Ynv7Xh-BRyZz-Or.js +1 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-D6Ynv7Xh-legacy-BI4TMtG3.js → tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/p-D6Ynv7Xh-legacy-BzZfcKZZ.js +1 -1
- tesla_cli-4.9.0/src/tesla_cli/api/ui_dist/assets/polyfills-legacy-D1m5vk93.js +1 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/ui_dist/index.html +4 -4
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/data_cmd.py +149 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/serve.py +1 -1
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/automation.py +31 -1
- tesla_cli-4.9.0/src/tesla_cli/core/backends/energy_prices.py +169 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/sources.py +9 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_api_routes.py +9 -7
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_sources.py +4 -0
- tesla_cli-4.9.0/ui/src/App.tsx +224 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/api/client.ts +233 -8
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/BatteryGauge.tsx +3 -3
- tesla_cli-4.9.0/ui/src/components/ErrorBoundary.tsx +31 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/ModelYSilhouette.tsx +1 -1
- tesla_cli-4.9.0/ui/src/components/OrderProcessTracker.tsx +473 -0
- tesla_cli-4.9.0/ui/src/components/RecentCharges.tsx +57 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/hooks/useDashboardTiles.ts +10 -9
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/hooks/useVehicleData.ts +41 -2
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Action.tsx +2 -1
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Analytics.tsx +146 -2
- tesla_cli-4.9.0/ui/src/pages/Automations.tsx +579 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Dashboard.tsx +104 -17
- tesla_cli-4.9.0/ui/src/pages/Dossier.tsx +606 -0
- tesla_cli-4.9.0/ui/src/pages/Energy.tsx +455 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Navigation.tsx +2 -2
- tesla_cli-4.9.0/ui/src/pages/Order.tsx +373 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Settings.tsx +111 -3
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Vehicle.tsx +18 -15
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/vehicle/ScheduleContent.tsx +56 -18
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/theme/variables.css +26 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/uv.lock +2 -1467
- tesla_cli-4.8.1/src/tesla_cli/api/routes/charge.py +0 -178
- tesla_cli-4.8.1/src/tesla_cli/api/routes/notify.py +0 -86
- tesla_cli-4.8.1/src/tesla_cli/api/routes/order.py +0 -25
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ChargeContent-DB-NWhH1.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ChargeContent-legacy-BtQimBgV.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ClimateContent-D4CM6N0Z.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ControlsContent-D36it6CW.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ControlsContent-legacy-ewul7U4S.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ScheduleContent-ICcltjam.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/ScheduleContent-legacy-DAHcE83Y.js +0 -1
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/index-CWH3Fl8L.js +0 -298
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/index-legacy-DTZLiX6K.js +0 -352
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-CneGxKsZ-BSMrC0Tx.js +0 -3
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/p-Cz5nLPGT-legacy-o-dW52P2.js +0 -5
- tesla_cli-4.8.1/src/tesla_cli/api/ui_dist/assets/polyfills-legacy-CAPX7zLk.js +0 -4
- tesla_cli-4.8.1/ui/src/App.tsx +0 -119
- tesla_cli-4.8.1/ui/src/components/RecentCharges.tsx +0 -64
- tesla_cli-4.8.1/ui/src/pages/Dossier.tsx +0 -350
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.dockerignore +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.env.example +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.github/workflows/ci.yml +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.github/workflows/publish.yml +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.gitignore +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/.python-version +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/CONTRIBUTING.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/Dockerfile +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/Formula/tesla-cli.rb +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/LICENSE +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/Makefile +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/README.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/SECURITY.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docker-compose.full.yml +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docker-compose.yml +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/api-reference.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/architecture.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/configuration.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/data-sources.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/research/competitive-analysis.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/roadmap.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/user-guide.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/docs/why-tesla-cli.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/.claude-plugin/marketplace.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/.gitignore +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/CHANGELOG.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/CLAUDE.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/LICENSE +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/README.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/bin/tesla-check +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/docs/design.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/analytics/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/automations/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/charge/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/control/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/dossier/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/serve/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/setup/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/status/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/telemetry/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/plugins/claude-code/skills/tesla-context/SKILL.md +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/__main__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/auth.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/auth.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/climate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/colombia.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/dossier.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/fleet.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/geofence.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/security.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/sources.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/routes/teslaMate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/ui_dist/favicon.png +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/api/ui_dist/manifest.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/app.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/abrp.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/automations.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/ble.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/charge.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/climate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/config_cmd.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/dashcam.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/dossier.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/energy.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/geofence.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/ha.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/media.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/mqtt_cmd.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/notify.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/order.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/providers_cmd.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/scenes.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/security.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/service.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/setup.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/telemetry.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/teslaMate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/commands/vehicle.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/i18n.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/cli/output.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/browser_login.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/encryption.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/oauth.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/portal_scrape.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/tessie.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/auth/tokens.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/base.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/dossier.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/energy.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/fleet.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/fleet_signed.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/fleet_telemetry.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/http.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/order.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/owner.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/runt.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/simit.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/teslaMate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/backends/tessie.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/config.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/exceptions.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/automation.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/charge.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/climate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/dossier.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/drive.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/energy.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/order.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/models/vehicle.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/base.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/abrp.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/apprise_notify.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/ble.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/ha.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/mqtt.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/teslaMate.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/impl/vehicle_api.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/loader.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/core/providers/registry.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/endpoints.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/infra/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/infra/fleet_telemetry_stack.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/infra/teslamate_stack.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/src/tesla_cli/py.typed +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tesla-config-backup.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/__init__.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/conftest.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_api_routes_new.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_auth.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_browser_login.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_commands.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_models.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_new_commands.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_order_backend.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_pipeline.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_providers.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_providers_new.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_runt.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_runt_integration.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_server.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_simit.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_simit_integration.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_teslamate_stack.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v260.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v270.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v280.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v290.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v300.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v310.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v320.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v330.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/tests/test_v340.py +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/.browserslistrc +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/.gitignore +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/cypress/e2e/test.cy.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/cypress/fixtures/example.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/cypress/support/commands.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/cypress/support/e2e.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/cypress.config.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/eslint.config.js +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/index.html +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/ionic.config.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/ionic.starter.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/package-lock.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/package.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/public/favicon.png +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/public/manifest.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/App.test.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/CommandButton.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/ExploreContainer.css +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/ExploreContainer.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/SeatHeaterGrid.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/StatusBadge.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/VehicleMap.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/CollapsibleSection.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/LogisticsCard.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/OptionCodesPills.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/RuntCard.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/SimitCard.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/SpecsGrid.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/dossier/StatusPipeline.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/icons/Icons.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/components/icons/Spinner.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/hooks/useDossierData.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/main.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/Home.css +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/vehicle/ChargeContent.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/vehicle/ClimateContent.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/pages/vehicle/ControlsContent.tsx +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/setupTests.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/src/vite-env.d.ts +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/tsconfig.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/tsconfig.node.json +0 -0
- {tesla_cli-4.8.1 → tesla_cli-4.9.0}/ui/vite.config.ts +0 -0
|
@@ -5,6 +5,44 @@ 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.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.9.0] - 2026-04-06
|
|
9
|
+
|
|
10
|
+
### Dashboard
|
|
11
|
+
- **Dedicated Order page** with 15-step financing/delivery tracker
|
|
12
|
+
- **Energy page** — Powerwall controls, city tariff comparison, cost calculator
|
|
13
|
+
- **Automations page** — CRUD rules, quick setup, test/toggle/delete
|
|
14
|
+
- **Notification history** timeline in Settings
|
|
15
|
+
- **Offline mode** — cached vehicle data when server is down
|
|
16
|
+
- **Mobile responsive** — CSS breakpoints for phones/tablets
|
|
17
|
+
- **Scene cards** on dashboard (Morning/Goodnight/Trip)
|
|
18
|
+
- **Code-split routes** — lazy-loaded pages for faster initial load
|
|
19
|
+
|
|
20
|
+
### API (30+ new endpoints)
|
|
21
|
+
- Energy management: sites, status, backup, mode, storm
|
|
22
|
+
- Automations: CRUD, enable/disable, test, status
|
|
23
|
+
- Charge scheduling + analytics: sessions, cost, forecast
|
|
24
|
+
- Service: history, appointments, reminders
|
|
25
|
+
- Scenes: list + execute
|
|
26
|
+
- ABRP, BLE, MQTT, Dashcam endpoints
|
|
27
|
+
- Energy tariffs: by city/estrato + vehicle location
|
|
28
|
+
- 429 rate-limit retry with Retry-After
|
|
29
|
+
|
|
30
|
+
### CLI
|
|
31
|
+
- BLE key management (enroll, list, remove, state reads)
|
|
32
|
+
- Safety Score + Service scheduling (first-mover)
|
|
33
|
+
- EV vs gas savings calculator
|
|
34
|
+
- Location-based charge/precondition schedules
|
|
35
|
+
- Country-aware sources (9 countries, 35+ sources)
|
|
36
|
+
- Dashcam processing (list/process/export)
|
|
37
|
+
- Webhook automations (IFTTT/HomeKit/Google Home)
|
|
38
|
+
- Energy tariffs command
|
|
39
|
+
|
|
40
|
+
### Quality
|
|
41
|
+
- 17 UI bugs fixed (critical crashes, color errors, missing guards)
|
|
42
|
+
- Error boundary + React.Suspense on all lazy routes
|
|
43
|
+
- Flaky test fixed permanently
|
|
44
|
+
- 1682 tests passing
|
|
45
|
+
|
|
8
46
|
## [4.8.0] - 2026-04-05
|
|
9
47
|
|
|
10
48
|
### New Features
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Full-stack Tesla management platform: CLI (Typer) + REST API (FastAPI) + Web Dashboard (React/Ionic). Tracks orders, controls vehicles, aggregates data from 15+ sources, integrates with TeslaMate/MQTT/HA/ABRP.
|
|
6
6
|
|
|
7
|
-
**Version**: v4.
|
|
7
|
+
**Version**: v4.9.0 | **Python**: 3.12+ | **Package manager**: uv | **Tests**: 1682
|
|
8
8
|
|
|
9
9
|
## Architecture (quick reference)
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tesla-cli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.9.0
|
|
4
4
|
Summary: CLI tool for Tesla order tracking and vehicle control
|
|
5
5
|
Project-URL: Homepage, https://github.com/dacrypt/tesla
|
|
6
6
|
Project-URL: Repository, https://github.com/dacrypt/tesla
|
|
@@ -35,10 +35,6 @@ Requires-Dist: pytest>=8; extra == 'dev'
|
|
|
35
35
|
Requires-Dist: ruff>=0.9; extra == 'dev'
|
|
36
36
|
Provides-Extra: fleet
|
|
37
37
|
Requires-Dist: tesla-fleet-api>=1.0; extra == 'fleet'
|
|
38
|
-
Provides-Extra: ocr
|
|
39
|
-
Requires-Dist: easyocr>=1.7; extra == 'ocr'
|
|
40
|
-
Requires-Dist: paddleocr>=3.0; extra == 'ocr'
|
|
41
|
-
Requires-Dist: paddlepaddle>=3.0; extra == 'ocr'
|
|
42
38
|
Provides-Extra: pdf
|
|
43
39
|
Requires-Dist: fpdf2>=2.7; extra == 'pdf'
|
|
44
40
|
Provides-Extra: query
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
cd /Users/david/dev/dacrypt/tesla
|
|
4
|
+
|
|
5
|
+
if [ -x .venv/bin/python3 ]; then
|
|
6
|
+
PY=.venv/bin/python3
|
|
7
|
+
else
|
|
8
|
+
PY=python3
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
rm -f history/last-changes.json history/pending-alert.txt
|
|
12
|
+
|
|
13
|
+
"$PY" refresh-mission-control.py
|
|
14
|
+
CHANGE_OUTPUT=$("$PY" change-detector.py 2>&1 || true)
|
|
15
|
+
echo "$CHANGE_OUTPUT"
|
|
16
|
+
bash build-activity-log.sh
|
|
17
|
+
|
|
18
|
+
if echo "$CHANGE_OUTPUT" | grep -q "✅ No changes detected"; then
|
|
19
|
+
echo "NO_CHANGES"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ -f history/last-changes.json ]; then
|
|
24
|
+
python3 - <<'PY'
|
|
25
|
+
import json
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
p = Path('history/last-changes.json')
|
|
28
|
+
if not p.exists():
|
|
29
|
+
print('NO_CHANGES')
|
|
30
|
+
raise SystemExit(0)
|
|
31
|
+
obj = json.loads(p.read_text())
|
|
32
|
+
if obj.get('has_changes'):
|
|
33
|
+
print(obj.get('alert','').strip())
|
|
34
|
+
else:
|
|
35
|
+
print('NO_CHANGES')
|
|
36
|
+
PY
|
|
37
|
+
else
|
|
38
|
+
echo "NO_CHANGES"
|
|
39
|
+
fi
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: energy
|
|
3
|
+
description: >
|
|
4
|
+
Tesla energy management — Powerwall status, Solar production, electricity
|
|
5
|
+
tariffs by city, backup reserve, operation mode, storm watch.
|
|
6
|
+
argument-hint: "[query: status|tariffs|backup|mode|storm|savings]"
|
|
7
|
+
allowed-tools: Bash(tesla *)
|
|
8
|
+
level: 2
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Tesla Energy Management
|
|
12
|
+
|
|
13
|
+
Monitor and control Powerwall, solar production, and electricity tariffs.
|
|
14
|
+
|
|
15
|
+
## Powerwall Status
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
tesla energy status # Powerwall charge %, solar W, grid W, home W
|
|
19
|
+
tesla energy status --oneline # compact: 🔋 82% | ☀️ 3.2kW solar | 🏠 1.1kW home
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Tariffs by City
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
tesla data energia --ciudad bogota --estrato 4 # tariff for Bogotá estrato 4
|
|
26
|
+
tesla data energia --ciudad medellin --estrato 3 # Medellín estrato 3
|
|
27
|
+
tesla data energia # use vehicle location automatically
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Shows COP/kWh rate, tier breakdown, and estimated monthly cost.
|
|
31
|
+
|
|
32
|
+
## Backup Reserve
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
tesla energy backup # show current backup reserve %
|
|
36
|
+
tesla energy backup 30 # set backup reserve to 30%
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Operation Mode
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
tesla energy mode # show current mode
|
|
43
|
+
tesla energy mode self_consumption # maximize solar self-consumption
|
|
44
|
+
tesla energy mode autonomous # Time-Based Control (TOU)
|
|
45
|
+
tesla energy mode backup # full backup mode
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Storm Watch
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
tesla energy storm --on # enable Storm Watch (charge to 100%)
|
|
52
|
+
tesla energy storm --off # disable Storm Watch
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Savings Calculator
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
tesla charge savings # EV vs gas savings summary
|
|
59
|
+
tesla charge savings --months 6 # last 6 months comparison
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Response Guidelines
|
|
63
|
+
|
|
64
|
+
- If the CLI is not configured, **stop and tell the user to run `/tesla:setup`** — never auto-configure
|
|
65
|
+
- Start with Powerwall % and current solar/grid/home power flows
|
|
66
|
+
- For tariff queries: show rate in COP/kWh and compare to grid cost
|
|
67
|
+
- For backup/mode changes: confirm the new setting after applying
|
|
68
|
+
- For `$ARGUMENTS`:
|
|
69
|
+
- "status" / no args → `tesla energy status`
|
|
70
|
+
- "tariffs" / "precio" / "estrato" → `tesla data energia`
|
|
71
|
+
- "backup" → `tesla energy backup`
|
|
72
|
+
- "mode" / "modo" → `tesla energy mode`
|
|
73
|
+
- "storm" → `tesla energy storm`
|
|
74
|
+
- "savings" / "ahorro" → `tesla charge savings`
|
|
@@ -25,7 +25,7 @@ tesla order delivery # delivery appointment (date, time, location)
|
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
tesla order eta # delivery ETA (best/typical/worst estimates)
|
|
28
|
-
tesla order gates #
|
|
28
|
+
tesla order gates # 15-step financing/delivery journey tracker (gates 1–15)
|
|
29
29
|
tesla order estimate # community-based delivery date estimation
|
|
30
30
|
tesla order stores # nearby Tesla stores/Service Centers
|
|
31
31
|
tesla order stores --near 4.6,-74.1 # stores near coordinates
|
|
@@ -73,7 +73,7 @@ tesla order documents --download # download all to ~/.tesla-cli/documents/
|
|
|
73
73
|
|
|
74
74
|
- If the CLI is not configured, **stop and tell the user to run `/tesla:setup`** — never auto-configure
|
|
75
75
|
- Start with the **current order status** and **delivery date** if available
|
|
76
|
-
- Show the gate progress (which of
|
|
76
|
+
- Show the gate progress (which of the 15 financing/delivery steps have been passed)
|
|
77
77
|
- If the car hasn't been delivered yet, include ETA estimates
|
|
78
78
|
- If delivery is upcoming, suggest the checklist
|
|
79
79
|
- For `$ARGUMENTS`:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tesla-cli"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.9.0"
|
|
4
4
|
description = "CLI tool for Tesla order tracking and vehicle control"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -36,7 +36,6 @@ teslaMate = ["psycopg2-binary>=2.9"]
|
|
|
36
36
|
pdf = ["fpdf2>=2.7"]
|
|
37
37
|
serve = ["fastapi>=0.110", "uvicorn[standard]>=0.29"]
|
|
38
38
|
query = ["openquery"]
|
|
39
|
-
ocr = ["paddlepaddle>=3.0", "paddleocr>=3.0", "easyocr>=1.7"]
|
|
40
39
|
dev = ["pytest>=8", "pytest-httpx>=0.35", "ruff>=0.9"]
|
|
41
40
|
|
|
42
41
|
[project.scripts]
|
|
@@ -193,13 +193,19 @@ def _register_middleware(app: FastAPI) -> None:
|
|
|
193
193
|
|
|
194
194
|
def _register_routes(app: FastAPI) -> None:
|
|
195
195
|
"""Include all API routers."""
|
|
196
|
+
from tesla_cli.api.routes.abrp import router as abrp_router
|
|
196
197
|
from tesla_cli.api.routes.auth import router as auth_router
|
|
198
|
+
from tesla_cli.api.routes.automations import router as automations_router
|
|
199
|
+
from tesla_cli.api.routes.ble import router as ble_router
|
|
197
200
|
from tesla_cli.api.routes.charge import router as charge_router
|
|
198
201
|
from tesla_cli.api.routes.climate import router as climate_router
|
|
199
202
|
from tesla_cli.api.routes.colombia import router as colombia_router
|
|
203
|
+
from tesla_cli.api.routes.dashcam import router as dashcam_router
|
|
200
204
|
from tesla_cli.api.routes.dossier import router as dossier_router
|
|
205
|
+
from tesla_cli.api.routes.energy_prices import router as energy_prices_router
|
|
201
206
|
from tesla_cli.api.routes.fleet import router as fleet_router
|
|
202
207
|
from tesla_cli.api.routes.geofence import router as geofence_router
|
|
208
|
+
from tesla_cli.api.routes.mqtt import router as mqtt_router
|
|
203
209
|
from tesla_cli.api.routes.notify import router as notify_router
|
|
204
210
|
from tesla_cli.api.routes.order import router as order_router
|
|
205
211
|
from tesla_cli.api.routes.security import router as security_router
|
|
@@ -210,6 +216,7 @@ def _register_routes(app: FastAPI) -> None:
|
|
|
210
216
|
app.include_router(auth_router, prefix="/api/auth", tags=["Auth"])
|
|
211
217
|
app.include_router(sources_router, prefix="/api/sources", tags=["Sources"])
|
|
212
218
|
app.include_router(colombia_router, prefix="/api/co", tags=["Colombia"])
|
|
219
|
+
app.include_router(energy_prices_router, prefix="/api/energy", tags=["Energy"])
|
|
213
220
|
app.include_router(vehicle_router, prefix="/api/vehicle", tags=["Vehicle"])
|
|
214
221
|
app.include_router(charge_router, prefix="/api/charge", tags=["Charge"])
|
|
215
222
|
app.include_router(climate_router, prefix="/api/climate", tags=["Climate"])
|
|
@@ -220,6 +227,76 @@ def _register_routes(app: FastAPI) -> None:
|
|
|
220
227
|
app.include_router(geofence_router, prefix="/api/geofences", tags=["Geofences"])
|
|
221
228
|
app.include_router(teslaMate_router, prefix="/api/teslaMate", tags=["TeslaMate"])
|
|
222
229
|
app.include_router(fleet_router, prefix="/api/fleet", tags=["Fleet"])
|
|
230
|
+
app.include_router(automations_router, prefix="/api/automations", tags=["Automations"])
|
|
231
|
+
app.include_router(abrp_router, prefix="/api/abrp", tags=["ABRP"])
|
|
232
|
+
app.include_router(ble_router, prefix="/api/ble", tags=["BLE"])
|
|
233
|
+
app.include_router(mqtt_router, prefix="/api/mqtt", tags=["MQTT"])
|
|
234
|
+
app.include_router(dashcam_router, prefix="/api/dashcam", tags=["Dashcam"])
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _build_health_deep(result: dict) -> dict:
|
|
238
|
+
"""Extend health result with deep diagnostics."""
|
|
239
|
+
from tesla_cli.core.auth import tokens
|
|
240
|
+
|
|
241
|
+
cfg = load_config()
|
|
242
|
+
result["backend"] = cfg.general.backend
|
|
243
|
+
result["vin_configured"] = bool(cfg.general.default_vin)
|
|
244
|
+
result["has_auth_token"] = tokens.has_token(tokens.ORDER_REFRESH_TOKEN)
|
|
245
|
+
result["telemetry_enabled"] = cfg.telemetry.enabled
|
|
246
|
+
result["teslamate_managed"] = cfg.teslaMate.managed
|
|
247
|
+
return result
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _build_vehicles_list(cfg) -> list:
|
|
251
|
+
"""Build the vehicles list from config (default VIN + aliases)."""
|
|
252
|
+
vehicles = []
|
|
253
|
+
default = cfg.general.default_vin
|
|
254
|
+
if default:
|
|
255
|
+
vehicles.append({"vin": default, "alias": "default", "is_default": True})
|
|
256
|
+
for alias, vin in cfg.vehicles.aliases.items():
|
|
257
|
+
if vin != default:
|
|
258
|
+
vehicles.append({"vin": vin, "alias": alias, "is_default": False})
|
|
259
|
+
return vehicles
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _build_config_response(cfg) -> dict:
|
|
263
|
+
"""Serialize public config fields for /api/config."""
|
|
264
|
+
return {
|
|
265
|
+
"backend": cfg.general.backend,
|
|
266
|
+
"default_vin": cfg.general.default_vin,
|
|
267
|
+
"cost_per_kwh": cfg.general.cost_per_kwh,
|
|
268
|
+
"teslaMate_url": bool(cfg.teslaMate.database_url),
|
|
269
|
+
"ha_url": cfg.home_assistant.url,
|
|
270
|
+
"abrp_configured": bool(cfg.abrp.user_token),
|
|
271
|
+
"geofences": list(cfg.geofences.zones.keys()),
|
|
272
|
+
"notifications": cfg.notifications.enabled,
|
|
273
|
+
"vehicles": cfg.vehicles.aliases,
|
|
274
|
+
"auth_enabled": bool(cfg.server.api_key),
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _build_provider_capabilities() -> dict:
|
|
279
|
+
"""Build capability map from the provider registry."""
|
|
280
|
+
from tesla_cli.core.providers import get_registry
|
|
281
|
+
from tesla_cli.core.providers.base import Capability
|
|
282
|
+
|
|
283
|
+
registry = get_registry()
|
|
284
|
+
out = {}
|
|
285
|
+
for cap in sorted(Capability.all()):
|
|
286
|
+
available = [p.name for p in registry.for_capability(cap)]
|
|
287
|
+
all_p = [p.name for p in registry.for_capability(cap, available_only=False)]
|
|
288
|
+
out[cap] = {"available": available, "all": all_p}
|
|
289
|
+
return out
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _build_config_validation(cfg) -> dict:
|
|
293
|
+
"""Run config checks and return a structured validation result."""
|
|
294
|
+
from tesla_cli.cli.commands.config_cmd import _run_config_checks
|
|
295
|
+
|
|
296
|
+
checks = _run_config_checks(cfg)
|
|
297
|
+
errors = sum(1 for c in checks if c["status"] == "error")
|
|
298
|
+
warnings = sum(1 for c in checks if c["status"] == "warn")
|
|
299
|
+
return {"valid": errors == 0, "errors": errors, "warnings": warnings, "checks": checks}
|
|
223
300
|
|
|
224
301
|
|
|
225
302
|
def _register_system_endpoints(app: FastAPI) -> None:
|
|
@@ -235,14 +312,7 @@ def _register_system_endpoints(app: FastAPI) -> None:
|
|
|
235
312
|
"""
|
|
236
313
|
result: dict = {"status": "ok", "version": __version__}
|
|
237
314
|
if deep:
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
cfg = load_config()
|
|
241
|
-
result["backend"] = cfg.general.backend
|
|
242
|
-
result["vin_configured"] = bool(cfg.general.default_vin)
|
|
243
|
-
result["has_auth_token"] = tokens.has_token(tokens.ORDER_REFRESH_TOKEN)
|
|
244
|
-
result["telemetry_enabled"] = cfg.telemetry.enabled
|
|
245
|
-
result["teslamate_managed"] = cfg.teslaMate.managed
|
|
315
|
+
_build_health_deep(result)
|
|
246
316
|
return result
|
|
247
317
|
|
|
248
318
|
@app.get("/api/status", tags=["System"])
|
|
@@ -258,33 +328,11 @@ def _register_system_endpoints(app: FastAPI) -> None:
|
|
|
258
328
|
@app.get("/api/vehicles", tags=["System"])
|
|
259
329
|
def api_vehicles() -> list:
|
|
260
330
|
"""List all configured vehicles (aliases + default VIN)."""
|
|
261
|
-
|
|
262
|
-
vehicles = []
|
|
263
|
-
# Always include the default VIN first
|
|
264
|
-
default = cfg.general.default_vin
|
|
265
|
-
if default:
|
|
266
|
-
vehicles.append({"vin": default, "alias": "default", "is_default": True})
|
|
267
|
-
# Add any aliases that are different from the default
|
|
268
|
-
for alias, vin in cfg.vehicles.aliases.items():
|
|
269
|
-
if vin != default:
|
|
270
|
-
vehicles.append({"vin": vin, "alias": alias, "is_default": False})
|
|
271
|
-
return vehicles
|
|
331
|
+
return _build_vehicles_list(load_config())
|
|
272
332
|
|
|
273
333
|
@app.get("/api/config", tags=["System"])
|
|
274
334
|
def api_config() -> dict:
|
|
275
|
-
|
|
276
|
-
return {
|
|
277
|
-
"backend": cfg.general.backend,
|
|
278
|
-
"default_vin": cfg.general.default_vin,
|
|
279
|
-
"cost_per_kwh": cfg.general.cost_per_kwh,
|
|
280
|
-
"teslaMate_url": bool(cfg.teslaMate.database_url),
|
|
281
|
-
"ha_url": cfg.home_assistant.url,
|
|
282
|
-
"abrp_configured": bool(cfg.abrp.user_token),
|
|
283
|
-
"geofences": list(cfg.geofences.zones.keys()),
|
|
284
|
-
"notifications": cfg.notifications.enabled,
|
|
285
|
-
"vehicles": cfg.vehicles.aliases,
|
|
286
|
-
"auth_enabled": bool(cfg.server.api_key),
|
|
287
|
-
}
|
|
335
|
+
return _build_config_response(load_config())
|
|
288
336
|
|
|
289
337
|
@app.get("/api/providers", tags=["System"])
|
|
290
338
|
def api_providers() -> list:
|
|
@@ -296,16 +344,7 @@ def _register_system_endpoints(app: FastAPI) -> None:
|
|
|
296
344
|
@app.get("/api/providers/capabilities", tags=["System"])
|
|
297
345
|
def api_provider_capabilities() -> dict:
|
|
298
346
|
"""Capability map — which providers serve which capabilities."""
|
|
299
|
-
|
|
300
|
-
from tesla_cli.core.providers.base import Capability
|
|
301
|
-
|
|
302
|
-
registry = get_registry()
|
|
303
|
-
out = {}
|
|
304
|
-
for cap in sorted(Capability.all()):
|
|
305
|
-
available = [p.name for p in registry.for_capability(cap)]
|
|
306
|
-
all_p = [p.name for p in registry.for_capability(cap, available_only=False)]
|
|
307
|
-
out[cap] = {"available": available, "all": all_p}
|
|
308
|
-
return out
|
|
347
|
+
return _build_provider_capabilities()
|
|
309
348
|
|
|
310
349
|
@app.get("/api/config/validate", tags=["System"])
|
|
311
350
|
def api_config_validate() -> dict:
|
|
@@ -313,13 +352,7 @@ def _register_system_endpoints(app: FastAPI) -> None:
|
|
|
313
352
|
|
|
314
353
|
Returns {valid, errors, warnings, checks[]} suitable for a dashboard health widget.
|
|
315
354
|
"""
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
cfg = load_config()
|
|
319
|
-
checks = _run_config_checks(cfg)
|
|
320
|
-
errors = sum(1 for c in checks if c["status"] == "error")
|
|
321
|
-
warnings = sum(1 for c in checks if c["status"] == "warn")
|
|
322
|
-
return {"valid": errors == 0, "errors": errors, "warnings": warnings, "checks": checks}
|
|
355
|
+
return _build_config_validation(load_config())
|
|
323
356
|
|
|
324
357
|
|
|
325
358
|
def _register_metrics(app: FastAPI) -> None:
|
|
@@ -407,6 +440,68 @@ def _register_metrics(app: FastAPI) -> None:
|
|
|
407
440
|
return PlainTextResponse("".join(lines), media_type="text/plain; version=0.0.4")
|
|
408
441
|
|
|
409
442
|
|
|
443
|
+
def _sse_topic_events(data: dict, ts: int, topic_set: set) -> list[str]:
|
|
444
|
+
"""Build optional per-topic SSE event strings for battery/climate/drive/location."""
|
|
445
|
+
want_battery = "battery" in topic_set
|
|
446
|
+
want_climate = "climate" in topic_set
|
|
447
|
+
want_drive = "drive" in topic_set
|
|
448
|
+
want_location = "location" in topic_set
|
|
449
|
+
|
|
450
|
+
events = []
|
|
451
|
+
if want_battery:
|
|
452
|
+
cs = data.get("charge_state") or {}
|
|
453
|
+
events.append(f"event: battery\ndata: {json.dumps({'ts': ts, 'data': _sanitize(cs)})}\n\n")
|
|
454
|
+
if want_climate:
|
|
455
|
+
cl = data.get("climate_state") or {}
|
|
456
|
+
events.append(f"event: climate\ndata: {json.dumps({'ts': ts, 'data': _sanitize(cl)})}\n\n")
|
|
457
|
+
if want_drive:
|
|
458
|
+
ds = data.get("drive_state") or {}
|
|
459
|
+
events.append(f"event: drive\ndata: {json.dumps({'ts': ts, 'data': _sanitize(ds)})}\n\n")
|
|
460
|
+
if want_location:
|
|
461
|
+
ds = data.get("drive_state") or {}
|
|
462
|
+
loc = {
|
|
463
|
+
"lat": ds.get("latitude"),
|
|
464
|
+
"lon": ds.get("longitude"),
|
|
465
|
+
"heading": ds.get("heading"),
|
|
466
|
+
"speed": ds.get("speed"),
|
|
467
|
+
}
|
|
468
|
+
events.append(f"event: location\ndata: {json.dumps({'ts': ts, 'data': loc})}\n\n")
|
|
469
|
+
return events
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _sse_geofence_events(data: dict, ts: int, geofence_state: dict[str, bool]) -> list[str]:
|
|
473
|
+
"""Compute geofence enter/exit SSE events from current vehicle position."""
|
|
474
|
+
drive = data.get("drive_state") or data.get("response", {}).get("drive_state", {})
|
|
475
|
+
lat = drive.get("latitude") if isinstance(drive, dict) else None
|
|
476
|
+
lon = drive.get("longitude") if isinstance(drive, dict) else None
|
|
477
|
+
if lat is None or lon is None:
|
|
478
|
+
return []
|
|
479
|
+
|
|
480
|
+
events = []
|
|
481
|
+
reload_cfg = load_config()
|
|
482
|
+
for name, zone in reload_cfg.geofences.zones.items():
|
|
483
|
+
dist = _haversine_km(lat, lon, zone["lat"], zone["lon"])
|
|
484
|
+
inside = dist <= zone.get("radius_km", 0.5)
|
|
485
|
+
was_inside = geofence_state.get(name)
|
|
486
|
+
if was_inside is None:
|
|
487
|
+
geofence_state[name] = inside
|
|
488
|
+
elif inside != was_inside:
|
|
489
|
+
geofence_state[name] = inside
|
|
490
|
+
event = "enter" if inside else "exit"
|
|
491
|
+
gf_payload = json.dumps(
|
|
492
|
+
{
|
|
493
|
+
"ts": ts,
|
|
494
|
+
"zone": name,
|
|
495
|
+
"event": event,
|
|
496
|
+
"lat": lat,
|
|
497
|
+
"lon": lon,
|
|
498
|
+
"dist_km": round(dist, 3),
|
|
499
|
+
}
|
|
500
|
+
)
|
|
501
|
+
events.append(f"event: geofence\ndata: {gf_payload}\n\n")
|
|
502
|
+
return events
|
|
503
|
+
|
|
504
|
+
|
|
410
505
|
def _register_sse_stream(app: FastAPI) -> None:
|
|
411
506
|
"""Register the real-time SSE vehicle stream endpoint."""
|
|
412
507
|
|
|
@@ -435,15 +530,9 @@ def _register_sse_stream(app: FastAPI) -> None:
|
|
|
435
530
|
cfg = load_config()
|
|
436
531
|
v = resolve_vin(cfg, app.state.override_vin)
|
|
437
532
|
topic_set = {t.strip() for t in topics.split(",") if t.strip()}
|
|
438
|
-
want_geofence = "geofence" in topic_set
|
|
439
|
-
want_battery = "battery" in topic_set
|
|
440
|
-
want_climate = "climate" in topic_set
|
|
441
|
-
want_drive = "drive" in topic_set
|
|
442
|
-
want_location = "location" in topic_set
|
|
443
533
|
geofence_state: dict[str, bool] = {} # zone_name → was_inside
|
|
444
534
|
|
|
445
535
|
async def _generate():
|
|
446
|
-
nonlocal geofence_state
|
|
447
536
|
backend = get_vehicle_backend(cfg)
|
|
448
537
|
while True:
|
|
449
538
|
if await request.is_disconnected():
|
|
@@ -453,69 +542,20 @@ def _register_sse_stream(app: FastAPI) -> None:
|
|
|
453
542
|
None, lambda: backend.get_vehicle_data(v)
|
|
454
543
|
)
|
|
455
544
|
ts = int(time.time())
|
|
456
|
-
payload = json.dumps(
|
|
457
|
-
{
|
|
458
|
-
"ts": ts,
|
|
459
|
-
"data": _sanitize(data),
|
|
460
|
-
}
|
|
461
|
-
)
|
|
545
|
+
payload = json.dumps({"ts": ts, "data": _sanitize(data)})
|
|
462
546
|
yield f"event: vehicle\ndata: {payload}\n\n"
|
|
463
547
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
yield f"event: battery\ndata: {json.dumps({'ts': ts, 'data': _sanitize(cs)})}\n\n"
|
|
467
|
-
|
|
468
|
-
if want_climate:
|
|
469
|
-
cl = data.get("climate_state") or {}
|
|
470
|
-
yield f"event: climate\ndata: {json.dumps({'ts': ts, 'data': _sanitize(cl)})}\n\n"
|
|
471
|
-
|
|
472
|
-
if want_drive:
|
|
473
|
-
ds = data.get("drive_state") or {}
|
|
474
|
-
yield f"event: drive\ndata: {json.dumps({'ts': ts, 'data': _sanitize(ds)})}\n\n"
|
|
475
|
-
|
|
476
|
-
if want_location:
|
|
477
|
-
ds = data.get("drive_state") or {}
|
|
478
|
-
loc = {
|
|
479
|
-
"lat": ds.get("latitude"),
|
|
480
|
-
"lon": ds.get("longitude"),
|
|
481
|
-
"heading": ds.get("heading"),
|
|
482
|
-
"speed": ds.get("speed"),
|
|
483
|
-
}
|
|
484
|
-
yield f"event: location\ndata: {json.dumps({'ts': ts, 'data': loc})}\n\n"
|
|
548
|
+
for ev in _sse_topic_events(data, ts, topic_set):
|
|
549
|
+
yield ev
|
|
485
550
|
|
|
486
551
|
if fanout:
|
|
487
552
|
await asyncio.get_event_loop().run_in_executor(
|
|
488
553
|
None, _fanout_telemetry, data, v, cfg
|
|
489
554
|
)
|
|
490
555
|
|
|
491
|
-
if
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
)
|
|
495
|
-
lat = drive.get("latitude") if isinstance(drive, dict) else None
|
|
496
|
-
lon = drive.get("longitude") if isinstance(drive, dict) else None
|
|
497
|
-
if lat is not None and lon is not None:
|
|
498
|
-
reload_cfg = load_config()
|
|
499
|
-
for name, zone in reload_cfg.geofences.zones.items():
|
|
500
|
-
dist = _haversine_km(lat, lon, zone["lat"], zone["lon"])
|
|
501
|
-
inside = dist <= zone.get("radius_km", 0.5)
|
|
502
|
-
was_inside = geofence_state.get(name)
|
|
503
|
-
if was_inside is None:
|
|
504
|
-
geofence_state[name] = inside
|
|
505
|
-
elif inside != was_inside:
|
|
506
|
-
geofence_state[name] = inside
|
|
507
|
-
event = "enter" if inside else "exit"
|
|
508
|
-
gf_payload = json.dumps(
|
|
509
|
-
{
|
|
510
|
-
"ts": ts,
|
|
511
|
-
"zone": name,
|
|
512
|
-
"event": event,
|
|
513
|
-
"lat": lat,
|
|
514
|
-
"lon": lon,
|
|
515
|
-
"dist_km": round(dist, 3),
|
|
516
|
-
}
|
|
517
|
-
)
|
|
518
|
-
yield f"event: geofence\ndata: {gf_payload}\n\n"
|
|
556
|
+
if "geofence" in topic_set:
|
|
557
|
+
for ev in _sse_geofence_events(data, ts, geofence_state):
|
|
558
|
+
yield ev
|
|
519
559
|
|
|
520
560
|
except Exception as exc: # noqa: BLE001
|
|
521
561
|
yield f"event: vehicle\ndata: {json.dumps({'error': str(exc)})}\n\n"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""ABRP API routes: /api/abrp/*"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
6
|
+
|
|
7
|
+
from tesla_cli.core.config import load_config, resolve_vin
|
|
8
|
+
|
|
9
|
+
router = APIRouter()
|
|
10
|
+
|
|
11
|
+
_ABRP_API = "https://api.iternio.com/1/tlm/send"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@router.get("/status")
|
|
15
|
+
def abrp_status() -> dict:
|
|
16
|
+
"""ABRP connection status and configuration."""
|
|
17
|
+
cfg = load_config()
|
|
18
|
+
return {
|
|
19
|
+
"user_token_set": bool(cfg.abrp.user_token),
|
|
20
|
+
"api_key_set": bool(cfg.abrp.api_key),
|
|
21
|
+
"configured": bool(cfg.abrp.user_token),
|
|
22
|
+
"abrp_api": _ABRP_API,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@router.get("/config")
|
|
27
|
+
def abrp_config() -> dict:
|
|
28
|
+
"""Get ABRP configuration (api_key configured, user_token set)."""
|
|
29
|
+
cfg = load_config()
|
|
30
|
+
return {
|
|
31
|
+
"user_token_set": bool(cfg.abrp.user_token),
|
|
32
|
+
"api_key_set": bool(cfg.abrp.api_key),
|
|
33
|
+
"abrp_api": _ABRP_API,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@router.post("/send")
|
|
38
|
+
def abrp_send(request: Request) -> dict:
|
|
39
|
+
"""Send current vehicle telemetry to ABRP."""
|
|
40
|
+
cfg = load_config()
|
|
41
|
+
|
|
42
|
+
if not cfg.abrp.user_token:
|
|
43
|
+
raise HTTPException(
|
|
44
|
+
status_code=400,
|
|
45
|
+
detail="ABRP user token not configured. Run: tesla config set abrp-user-token <TOKEN>",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
from tesla_cli.core.backends import get_vehicle_backend
|
|
50
|
+
|
|
51
|
+
v = resolve_vin(cfg, request.app.state.override_vin)
|
|
52
|
+
backend = get_vehicle_backend(cfg)
|
|
53
|
+
data = backend.get_vehicle_data(v)
|
|
54
|
+
except Exception as exc:
|
|
55
|
+
raise HTTPException(status_code=502, detail=f"Failed to get vehicle data: {exc}")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
from tesla_cli.core.providers.impl.abrp import AbrpProvider
|
|
59
|
+
|
|
60
|
+
provider = AbrpProvider(cfg)
|
|
61
|
+
result = provider.execute("push", data=data)
|
|
62
|
+
if not result.ok:
|
|
63
|
+
raise HTTPException(status_code=502, detail=result.error or "ABRP push failed")
|
|
64
|
+
return {
|
|
65
|
+
"ok": True,
|
|
66
|
+
"telemetry": result.data.get("tlm") if result.data else {},
|
|
67
|
+
"abrp_response": result.data.get("response") if result.data else {},
|
|
68
|
+
"latency_ms": result.latency_ms,
|
|
69
|
+
}
|
|
70
|
+
except HTTPException:
|
|
71
|
+
raise
|
|
72
|
+
except Exception as exc:
|
|
73
|
+
raise HTTPException(status_code=502, detail=str(exc))
|