Understanding the Vehicle State Machine in TeslaMate
The vehicle state machine in TeslaMate is a pure-functional state transformer implemented in the TeslaApi.Vehicle.State module that converts raw Tesla API JSON into five typed Elixir structs representing charge, climate, drive, configuration, and vehicle status.
TeslaMate, the open-source Tesla data logger written in Elixir, relies on this state machine to normalize real-time telemetry from Tesla's fleet API. Unlike traditional GenServer-based state machines, this implementation uses side-effect-free functions to transform unstructured JSON payloads into strongly-typed structs that power the dashboard, database layer, and MQTT integrations.
Architecture of the Vehicle State Machine
The core implementation resides in lib/tesla_api/vehicle/state.ex. Rather than implementing a behaviour or maintaining mutable state in a process, this module acts as a functional transformer that maps raw API responses into five distinct sub-states.
Each sub-state captures a specific domain of vehicle telemetry through its own struct and a lightweight result/1 mapper:
- Charge State — Battery metrics including state of charge, charger power, and charging limits (
TeslaApi.Vehicle.State.Charge) - Climate State — Cabin temperature, fan status, seat heaters, and pre-conditioning data (
TeslaApi.Vehicle.State.Climate) - Drive State — Real-time motion data including speed, GPS coordinates, heading, and shift state (
TeslaApi.Vehicle.State.Drive) - Vehicle Config — Static configuration such as model, trim, and hardware options (
TeslaApi.Vehicle.State.VehicleConfig) - Vehicle State — High-level status including door locks, software updates (via a nested
SoftwareUpdatestruct), TPMS, and Sentry Mode (TeslaApi.Vehicle.State.VehicleState)
Each sub-state module exposes a result/1 function that performs the JSON-to-struct mapping. For example, Charge.result/1 parses the nested charge_state map from the Tesla API response.
How the State Transformation Works
The vehicle state machine operates through a side-effect-free pipeline that ensures data consistency across the application. This pure-functional approach makes the system highly testable and resilient to API changes.
The transformation flow follows four distinct steps:
- API Retrieval —
TeslaApi.Vehicle.get/2fetches the raw JSON payload from Tesla's cloud endpoint. - State Extraction — The JSON map routes to specific mapper functions (
result/1) based on the sub-state category. - Struct Assembly — Typed Elixir structs replace raw maps, providing compile-time guarantees and cleaner access patterns.
- Consumer Distribution — The unified state propagates to
TeslaMate.Vehicles.Vehiclefor persistence, MQTT PubSub for external integrations, and LiveView processes for UI updates.
This architecture decouples the Tesla API's schema from TeslaMate's internal representations, allowing the application to handle field additions or deprecations gracefully.
Key Source Files and Implementation Details
Understanding the vehicle state machine requires familiarity with several critical files in the TeslaMate repository:
lib/tesla_api/vehicle/state.ex— Defines the five sub-state structs and their pure mapping functions (result/1).lib/tesla_api/vehicle.ex— Wraps HTTP calls to the Tesla API and returns raw JSON for state transformation.lib/teslamate/vehicles/vehicle.ex— Aggregates mapped structs into database-ready schemas and exposes helper functions.lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex— Listens for updates, executes state mapping, and publishes snapshots over MQTT.lib/teslamate_web/live/car_live/summary.ex— Consumes the unified vehicle state to render the real-time dashboard UI.
The result/1 functions in lib/tesla_api/vehicle/state.ex handle nil-safety and type coercion, ensuring that missing API fields default to sensible values rather than causing runtime exceptions.
Practical Usage Example
Working with the vehicle state machine involves extracting specific sub-states from the API response and accessing their typed fields. Here is how to fetch and inspect charging data:
# Fetch raw vehicle data from Tesla API
{:ok, raw_vehicle} = TeslaApi.Vehicle.get(vin, token)
# Transform JSON maps into typed structs using the state machine
charge_state = TeslaApi.Vehicle.State.Charge.result(raw_vehicle["charge_state"])
climate_state = TeslaApi.Vehicle.State.Climate.result(raw_vehicle["climate_state"])
drive_state = TeslaApi.Vehicle.State.Drive.result(raw_vehicle["drive_state"])
# Access strongly-typed fields with pattern matching
%TeslaApi.Vehicle.State.Charge{
battery_level: level,
charger_power: power,
charging_state: state
} = charge_state
IO.puts("Battery at #{level}%, charging at #{power} kW (#{state})")
This pattern ensures that downstream components in lib/teslamate_web/live/car_live/summary.ex receive validated data structures rather than raw maps, eliminating entire categories of runtime errors.
Summary
- The vehicle state machine in TeslaMate transforms raw Tesla API JSON into five typed Elixir structs using pure functions in
TeslaApi.Vehicle.State. - Each sub-state (
Charge,Climate,Drive,VehicleConfig,VehicleState) exposes aresult/1mapper that handles API normalization. - The implementation is side-effect-free, making it unit-testable and resilient to API schema changes.
- Processed states flow to the database layer via
TeslaMate.Vehicles.Vehicle, to MQTT subscribers viaVehicleSubscriber, and to the UI via LiveView components. - Key implementation files include
lib/tesla_api/vehicle/state.exandlib/tesla_api/vehicle.ex.
Frequently Asked Questions
Is the TeslaMate vehicle state machine a GenServer?
No, the vehicle state machine does not implement a behaviour or run as a GenServer. It operates as a pure-functional transformer that takes raw JSON input and returns typed Elixir structs. The "machine" terminology refers to the systematic collection and normalization of disparate vehicle data points, not a concurrent process managing mutable state.
How does TeslaMate handle missing or null fields in the Tesla API?
The result/1 functions in each state module (lib/tesla_api/vehicle/state.ex) implement defensive mapping strategies that coerce null values to appropriate defaults. For example, missing numeric fields typically default to 0, while missing boolean flags default to false. This ensures that structs are always fully populated and safe to access throughout the application.
Can I extend the vehicle state machine to capture new Tesla API fields?
Yes, extending the state machine requires modifying the relevant struct definition in lib/tesla_api/vehicle/state.ex and updating its corresponding result/1 function to extract the new field from the JSON map. Because the mapping is pure and side-effect-free, you can add new telemetry fields without affecting existing consumers, provided you maintain backward compatibility or update the database schema accordingly.
What is the performance impact of the state transformation pipeline?
The pure-functional transformation adds negligible overhead compared to the network latency of the Tesla API itself. Since result/1 functions perform simple pattern matching and struct instantiation without database queries or external I/O, they execute in microseconds. This efficiency allows TeslaMate to process high-frequency updates from the streaming API without bottlenecking the system.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →