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 SoftwareUpdate struct), 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:

  1. API Retrieval — TeslaApi.Vehicle.get/2 fetches the raw JSON payload from Tesla's cloud endpoint.
  2. State Extraction — The JSON map routes to specific mapper functions (result/1) based on the sub-state category.
  3. Struct Assembly — Typed Elixir structs replace raw maps, providing compile-time guarantees and cleaner access patterns.
  4. Consumer Distribution — The unified state propagates to TeslaMate.Vehicles.Vehicle for 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:

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 a result/1 mapper 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 via VehicleSubscriber, and to the UI via LiveView components.
  • Key implementation files include lib/tesla_api/vehicle/state.ex and lib/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →