
Plan Kimi K2.7-coder
To understand this blog post you need to read the post "Keeeping token cost down and evaluation test"
Validation summary Plan Strengths Weaknesses plan-big-pickle.md Most detailed. Real API cost table, Danish Konstant tariffs, thorough error-handling matrix, UI page breakdown, setup wizard, health endpoint. Polls every 15 min if the car is unplugged (adds API cost). Bundles VCP into the same container. Less structured as an executable plan. plan-Kimi K2.7 Code.md Best structured for execution. SOC reads only twice daily (18:00/20:00). Clean VCP sidecar. API usage tracking. DK1/DK2 region config. Manual overrides. Explicit verification. Less depth on error handling, UI pages, and cost math than Plan 1. Oddly specific default car model. plan-MAI.md Simplest MVP framing. Too vague: no price source, no auth plan, no UI, no API budget analysis, not actionable. Best standalone plan: plan-Kimi K2.7 Code.md — it is the most executable, respects the API budget best, and has the cleanest architecture.
However, combining the best of Plan 1 + Plan 2 is better than either alone.
Tesla Cheapest-Hours Charge Planner
A minimal Dockerized Node.js + Express app that charges a Tesla during the cheapest Danish electricity hours, reaches a user-defined target SOC by a chosen morning departure time, and stays comfortably within the Tesla Fleet API $10/month individual-developer credit by reading vehicle state only twice daily (18:00 and 20:00) and issuing only start/stop commands.
Requirements
- Primary goal: the car is ready in the morning at the target battery percentage the user asked for.
- Strategy: charge only during the cheapest hourly electricity prices of the day.
- Tesla Fleet API budget: stay within the $10/month credit provided for individual developers/small applications.
- Limit reads to two scheduled SOC checks per day (18:00 and 20:00).
- Issue commands only to start/stop charging and to wake the car when needed.
- Price data: free Danish public electricity price feed (Energi Data Service), region DK1 or DK2.
- Runtime: local Docker, with environment variables for secrets and a mounted
./datavolume for persistence.
Decisions
- Price source: Energi Data Service (energidataservice.dk), region configurable DK1/DK2.
- Tariffs: editable tiers in the web UI, defaulting to a Danish Konstant-style tariff model.
- SOC reads: only at 18:00 and 20:00 to keep API spend low; no continuous polling.
- Architecture: Node.js backend + Vehicle Command Proxy (VCP) sidecar (cleaner than bundling VCP into the same image).
- Frontend: htmx + Tailwind CSS served directly by Express, no separate build step.
- Storage: JSON files in a mounted
./datavolume. - Notifications: optional ntfy.sh push notifications.
- Public HTTPS for OAuth: Cloudflare Tunnel (or user-supplied reverse proxy) so Tesla can redirect back to the app.
Architecture
text┌──────────────────────────────────────────────┐ │ Docker Compose stack │ │ │ │ ┌──────────────┐ HTTP ┌──────────────┐ │ │ │ Node.js │◄────────►│ VCP │ │ │ │ Express │ │ (Go proxy) │ │ │ │ scheduler │ │ │ │ │ │ htmx UI │ │ │ │ │ └──────┬───────┘ └──────────────┘ │ │ │ │ │ ┌─────▼──────┐ │ │ │ ./data/ │ mounted volume │ │ │ config.json│ │ │ │ price_cache.json │ │ │ api_usage.json │ │ └────────────┘ │ └──────────────────────────────────────────────┘
Stack
- Runtime: Node.js 22 + TypeScript + Express 5
- Scheduling:
node-cronin-process - UI: htmx + Tailwind CSS
- Charts: Chart.js via CDN for the 24h price chart
- Command signing:
teslamotors/vehicle-command(VCP sidecar) - Storage: JSON files on mounted volume
Daily cycle
| Time | Action | Tesla API calls | Cost |
|---|---|---|---|
| ~14:00 CET | Fetch Energi Data Service DK1/DK2 prices for next 24-36h | 0 | $0 |
| 18:00 | vehicle_data?endpoints=charge_state | 1 Data ($0.002) | $0.002 |
| 18:00 | If plugged in → compute cheapest window | — | — |
| 18:00 | If not plugged in → re-check at 20:00 only | +1 Data ($0.002) | $0.002 |
| Window start | wake_up (if asleep) + charge_start | 1 Wake ($0.02) + 1 Cmd ($0.001) | $0.021 |
| Window end | charge_stop | 1 Cmd ($0.001) | $0.001 |
| Total (best case) | $0.024/night | ||
| Total (worst case) | ~$0.04/night | ||
| Monthly (30 days) | $0.72-$1.20 |
Scheduling algorithm
- Fetch hourly spot prices from Energi Data Service (DK1/DK2).
- Add Danish tariffs from the configurable tariff model (default Konstant-style tiers).
- Get
current_socfrom the scheduledvehicle_dataread. - Compute energy needed:
kwh_needed = (target_soc - current_soc) / 100 * battery_kwh. - Compute hours needed:
hours_needed = ceil(kwh_needed / charger_kw). - Filter candidate hours to the interval
[now, departure_time], excluding past hours. - Find the cheapest contiguous window of
hours_neededlength. - If no suitable window exists → skip the night and log the reason.
Auth & setup
Reuses a simplified OAuth PKCE flow inspired by the existing Tesla Charger app:
- In-app setup wizard served at
/setup:- Enter Tesla Developer
client_id+client_secret. - Click "Connect to Tesla" → redirected to Tesla authorize URL.
- User logs in on Tesla and grants permissions.
- Tesla redirects to
/api/auth/callbackwith an auth code. - App exchanges the code for access and refresh tokens, stored AES-encrypted in
config.json. - App registers as a Fleet API partner via
partner_accounts. - App generates an EC P-256 key pair and serves the public key at the well-known Tesla endpoint.
- Enter Tesla Developer
- Token refresh happens automatically 5 minutes before expiry.
- If refresh fails, the UI shows a "re-auth required" flag.
Configuration
| Field | Type | Default | Editable via UI |
|---|---|---|---|
| target_soc | number | 80 | Yes (slider 50-100) |
| departure_time | string | "07:00" | Yes (time picker) |
| soc_read_times | string[] | ["18:00", "20:00"] | Yes |
| charger_kw | number | 11 | Yes |
| battery_kwh | number | 75 | Yes |
| region | string | "DK1" | Yes (DK1/DK2) |
| active_days | bitmask | 62 (Mon-Fri) | Yes (checkboxes) |
| tariff_tiers | array | Konstant default | Yes |
| ntfy_topic | string | "" | Yes |
| tesla_vin | string | auto-detected | No |
| tesla_client_id | string | — | Setup wizard |
| tesla_client_secret | string | — | Setup wizard (encrypted) |
| access_token | string | — | OAuth (encrypted) |
| refresh_token | string | — | OAuth (encrypted) |
| ec_private_key | string | — | Generated (encrypted) |
| ec_public_key | string | — | Generated |
UI pages
Dashboard (/)
- Large battery SOC percentage display.
- Charging status badge: Idle / Charging / Disconnected / Done / Planned.
- Next planned charging window, e.g. "Charging 03:00-05:00".
- Target SOC slider.
- Departure time picker.
- SOC read-time config.
Prices (/prices)
- 24h bar chart via Chart.js.
- Cheapest hours highlighted in green.
- Planned charging window highlighted in blue.
- Current price indicator.
Settings (/settings)
- Charger power (kW), battery capacity (kWh), active days.
- DK1/DK2 region selector.
- Editable tariff tiers.
- ntfy.sh topic.
Setup (/setup)
- Three-step OAuth wizard, hidden once setup is complete.
API usage (/usage)
- Call counts per category (Data, Command, Wake).
- Estimated monthly cost.
API endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/config | Yes | Read config |
| POST | /api/config | Yes | Update config |
| GET | /api/status | Yes | Vehicle SOC, charging state, schedule |
| GET | /api/prices | Yes | Today + tomorrow prices |
| POST | /api/prices/refresh | Yes | Force price refresh |
| GET | /api/schedule | Yes | Planned charging window |
| POST | /api/charge/start | Yes | Manual override: start now |
| POST | /api/charge/stop | Yes | Manual override: stop now |
| POST | /api/charge/skip | Yes | Skip tonight |
| GET | /api/usage | Yes | API call counts and cost estimate |
| GET | /api/auth/status | No | Tesla auth status |
| GET | /api/auth/start | No | Initiate OAuth flow |
| GET | /api/auth/callback | No | OAuth callback |
| POST | /api/auth/register | Yes | Register partner account |
| GET | /health | No | Container health |
| GET | /.well-known/appspecific/com.tesla.3p.public-key.pem | No | Tesla public key |
Error handling
| Scenario | Behavior |
|---|---|
| Car already at/above target SOC | Skip night. Log: "Already charged." |
| Not plugged in at 18:00 | Re-check once at 20:00. If still unplugged, abort the night. |
| Price API down | Use last cached prices. If no cache, skip the night. |
| Tesla 429 rate limit | Exponential backoff: 1 min → 5 min → 15 min → abort night. |
charge_start fails | Retry once after 30 s. If still fails, log + ntfy + abort. |
charge_stop fails | Retry once after 30 s. Log + ntfy. The car stops on its own at target SOC. |
| Container restart mid-charge | On boot: check vehicle_data. If charging and past the planned window, stop. |
| Token refresh fails | Clear tokens. Re-auth required flag in the UI. |
| VCP proxy unreachable | Log error; retry up to twice, then abort the command. |
Project structure
texttesla-cheap-charger/ ├── docker-compose.yml # Node app + VCP sidecar ├── Dockerfile # Node app image ├── vcp/ │ └── Dockerfile # VCP sidecar image ├── .env.example ├── .gitignore ├── README.md ├── package.json ├── tsconfig.json └── src/ ├── index.ts # Express entry point ├── config.ts # JSON config/cache/usage helpers ├── scheduler/ │ ├── jobs.ts # node-cron jobs │ └── optimizer.ts # Cheapest contiguous window ├── tesla/ │ ├── auth.ts # OAuth PKCE flow │ ├── client.ts # Fleet API + VCP wrapper │ └── keys.ts # EC P-256 key management ├── prices/ │ ├── energidata.ts # Energi Data Service client │ └── tariffs.ts # Tariff calculation ├── notifications/ │ └── ntfy.ts # ntfy.sh sender ├── routes/ │ ├── api.ts # REST API routes │ ├── auth.ts # OAuth callback routes │ └── pages.ts # HTML page routes ├── views/ │ ├── layout.html │ ├── dashboard.html │ ├── prices.html │ ├── settings.html │ ├── setup.html │ └── usage.html └── utils/ ├── time.ts # Denmark timezone helpers └── crypto.ts # AES encryption for tokens
Docker Compose
yamlservices: vcp: build: ./vcp environment: - TZ=Europe/Copenhagen volumes: - ./data:/data networks: - tesla-net app: build: . ports: - "3001:3001" volumes: - ./data:/app/data environment: - PORT=3001 - TZ=Europe/Copenhagen - NODE_ENV=production - VCP_URL=http://vcp:4040 networks: - tesla-net depends_on: - vcp restart: unless-stopped networks: tesla-net:
Budget
| Item | Monthly | Notes |
|---|---|---|
| Tesla Fleet API | ~$0.72-$1.20 | Covered by $10 monthly credit |
| VPS/hosting | $0 | Docker on existing home server |
| Energi Data Service | $0 | Free, open data |
| ntfy.sh | $0 | Free, open source |
| Total out-of-pocket | $0 |
Dependencies
express— HTTP frameworkaxios— HTTP client (Tesla, Energi Data Service, ntfy)node-cron— In-process schedulingcrypto-js— AES token encryptionuuid— OAuth state noncedotenv— Env file loading- Dev:
typescript,tsx,@types/* - No native Node modules required.
Implementation phases
- Scaffold —
package.json, Express server, Dockerfiles,docker-compose.yml, VCP sidecar. - Storage & config — JSON file helpers, config schema, API usage log.
- Tesla auth — OAuth PKCE, token refresh, encrypted token storage, public key endpoint.
- Tesla commands — VCP client, start/stop charging, wake-up, SOC read.
- Price fetcher — Energi Data Service client, price cache, tariff editor.
- Optimizer — Hours-needed calculation, cheapest contiguous window search.
- Scheduler — node-cron jobs for prices, SOC polls, command execution, boot reconciliation.
- Web UI — settings page, schedule view, status, manual overrides, API usage view.
- Notifications — ntfy.sh integration.
- Deployment docs — Cloudflare Tunnel setup, Tesla developer app registration, env vars.
Verification
docker-compose up --buildstarts the backend and VCP without errors.- OAuth setup wizard completes, stores encrypted tokens, and registers the partner account.
GET /api/pricesreturns hourly DK1/DK2 prices and a populated 24h chart.- Optimizer unit test: for a sample SOC gap, battery, charger kW, and departure time, it returns the cheapest contiguous window.
- SOC read jobs fire at 18:00 and 20:00; scheduler issues
charge_start/charge_stopat the planned window boundaries in a test environment. - UI displays config, planned schedule, status, manual overrides, and API call count/cost.
- ntfy.sh notifications are sent on start, stop, and error paths.
- Monthly API cost estimate shown in the UI stays under $2 for normal usage.
Out of scope
- Geofencing / home detection (assumed home at planning time).
- Per-day departure/SOC schedules (active-days bitmask only).
- Multiple vehicles.
- Cloud hosting instructions beyond Cloudflare Tunnel.
- Advanced telemetry or analytics.
Share this post
Related Posts

Plan MAI----Own plan
To understand this blog post you need to read the post "Keeeping token cost down and evaluation test" and the other posts
July 4, 2026

Plan Big Pickle----Own plan
To understand this blog post you need to read the post "Keeeping token cost down and evaluation test" and the other posts
July 4, 2026

Plan Kimi K2.7-coder----Own plan
To understand this blog post you need to read the post "Keeeping token cost down and evaluation test" and the other posts
July 4, 2026
Comments
Be the first to leave a comment.