# Ecto Schema for TeslaMate Charges, Drives, and Positions: Complete Technical Guide

> Explore the Ecto schema for TeslaMate charges, drives, and positions. Learn how TeslaMate structures vehicle telemetry in PostgreSQL with relational integrity and precise decimal handling.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: technical-guide
- Published: 2026-06-16

---

**TeslaMate persists vehicle telemetry using three core Ecto schemas—Charge, Drive, and Position—located in `lib/teslamate/log/` to map raw Tesla API data to structured PostgreSQL tables with relational integrity and precise decimal handling.**

TeslaMate, the open-source Tesla data logger built with Elixir and Phoenix, structures all vehicle persistence through these **Ecto schemas for charges, drives, and positions**. Understanding these models is essential for querying charging history, analyzing trip efficiency, or extending the application with custom telemetry features.

## Schema Architecture Overview

The three schemas form the backbone of TeslaMate's logging system under the `TeslaMate.Log` namespace. Each module follows a consistent pattern using `Ecto.Schema` and defines a `changeset/2` function for data validation and casting.

| Schema | Source File | Primary Table | Key Associations |
|--------|-------------|---------------|------------------|
| **Charge** | [`lib/teslamate/log/charge.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/charge.ex) | `charges` | `belongs_to :charging_process, ChargingProcess` |
| **Drive** | [`lib/teslamate/log/drive.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/drive.ex) | `drives` | `belongs_to :car, Car`, `belongs_to :start_position/end_position, Position`, `has_many :positions, Position` |
| **Position** | [`lib/teslamate/log/position.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/position.ex) | `positions` | `belongs_to :car, Car`, `belongs_to :drive, Drive` |

## The Charge Schema

Located at [`lib/teslamate/log/charge.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/charge.ex), the **Charge** schema captures individual data points within a charging session. It stores raw values reported directly by the vehicle's onboard charger.

### Key Fields and Validations

- **Temporal data**: `date` (UTC DateTime)
- **Battery metrics**: `battery_level`, `charge_energy_added`, `ideal_battery_range_km`, `rated_battery_range_km`
- **Charger telemetry**: `charger_power`, `charger_voltage`, `charger_actual_current`, `charger_phases`, `fast_charger_present`, `fast_charger_type`
- **Environmental**: `outside_temp`, `battery_heater_no_power`, `battery_heater_on`, `battery_heater`

The schema uses `read_after_writes: true` on decimal fields (such as `charge_energy_added` and `ideal_battery_range_km`) to prevent precision loss when PostgreSQL rounds floating-point values. Validations enforce required fields including `date`, `charging_process_id`, `charge_energy_added`, `charger_power`, and `ideal_battery_range_km`, plus a positive integer constraint on `charger_phases`.

### Charge Changeset Example

```elixir
alias TeslaMate.Log.{Charge, Repo}

attrs = %{
  date: DateTime.utc_now(),
  battery_level: 45,
  charge_energy_added: Decimal.new("12.3"),
  charger_power: 7_200,
  ideal_battery_range_km: Decimal.new("350.0"),
  charging_process_id: 123
}

%Charge{}
|> Charge.changeset(attrs)
|> Repo.insert()

```

## The Drive Schema

The **Drive** schema in [`lib/teslamate/log/drive.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/drive.ex) represents a complete vehicle trip between two geographical points. It aggregates telemetry into high-level metrics while maintaining references to start and end positions.

### Relational Structure

The Drive schema maintains multiple associations for geospatial analysis:

- **Positions**: `belongs_to :start_position, Position` and `belongs_to :end_position, Position`
- **Geocoding**: `belongs_to :start_address, Address` and `belongs_to :end_address, Address`
- **Geofencing**: `belongs_to :start_geofence, GeoFence` and `belongs_to :end_geofence, GeoFence`
- **Telemetry collection**: `has_many :positions, Position`
- **Vehicle**: `belongs_to :car, Car`

### Drive Metrics and Fields

Core fields include `start_date`, `end_date`, `start_km`, `end_km`, and calculated `distance`. Performance data encompasses `speed_max`, `power_max`, and `power_min`. Temperature tracking uses `inside_temp_avg`, `outside_temp_avg`, and `odometer` values anchor the trip to the vehicle's total mileage. Battery efficiency is calculated via `start_ideal_battery_range_km` and `end_ideal_battery_range_km` (plus rated equivalents).

### Creating a Drive Record

```elixir
alias TeslaMate.Log.{Drive, Repo}

attrs = %{
  car_id: 1,
  start_date: ~U[2024-05-01 08:00:00Z],
  end_date: ~U[2024-05-01 09:30:00Z],
  start_km: 12345.0,
  end_km: 12410.6,
  distance: 65.6,
  duration_min: 90,
  start_address_id: 5,
  end_address_id: 9
}

%Drive{}
|> Drive.changeset(attrs)
|> Repo.insert()

```

## The Position Schema

Defined in [`lib/teslamate/log/position.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/position.ex), the **Position** schema stores one-second (or telemetry-frequency) snapshots of vehicle state while the car is moving or stationary with significant state changes.

### Geospatial and Telemetry Fields

Each position records precise location data (`latitude`, `longitude`, `elevation`) alongside motion metrics (`speed`, `power`, `odometer`). Battery state is captured through `battery_level`, `usable_battery_level`, `est_battery_range_km`, and `ideal_battery_range_km`. Climate data includes `inside_temp`, `outside_temp`, and `fan_status`, while safety systems are monitored via `tpms_pressure_fl`, `tpms_pressure_fr`, `tpms_pressure_rl`, and `tpms_pressure_rr` (tire pressure monitoring).

### Associations and Constraints

Positions belong to both a `Car` and optionally a `Drive` (when part of a trip). The schema enforces presence of `car_id` and valid geolocation coordinates, ensuring data integrity for mapping and range analysis.

### Querying Latest Position

```elixir
alias TeslaMate.Log.{Position, Repo}

latest_position =
  Position
  |> order_by([p], desc: p.date)
  |> limit(1)
  |> Repo.one()

IO.inspect(latest_position.latitude, label: "Current Latitude")

```

## Querying Across Schemas

Because drives and positions maintain bidirectional associations, you can preload related data efficiently. This query retrieves all drives for a specific vehicle with their associated position snapshots:

```elixir
alias TeslaMate.Log.{Drive, Repo}
import Ecto.Query

car_id = 1

drives =
  Drive
  |> where([d], d.car_id == ^car_id)
  |> preload([:positions, :start_address, :end_address])
  |> Repo.all()

Enum.each(drives, fn drive ->
  IO.puts("Drive #{drive.id}: #{drive.distance} km with #{length(drive.positions)} telemetry points")
end)

```

## Summary

- **Charge** ([`lib/teslamate/log/charge.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/charge.ex)): Captures granular charging session data with decimal precision safeguards and heater status tracking.
- **Drive** ([`lib/teslamate/log/drive.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/drive.ex)): Aggregates trips with start/end geocoding, geofencing support, and calculated distance and efficiency metrics.
- **Position** ([`lib/teslamate/log/position.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/position.ex)): Stores high-frequency telemetry including GPS coordinates, battery state, climate settings, and tire pressures.
- All three schemas implement `changeset/2` functions that cast attributes and enforce foreign-key constraints to maintain PostgreSQL relational integrity.

## Frequently Asked Questions

### How does TeslaMate handle decimal precision in charge records?

The Charge schema uses the `read_after_writes: true` option on decimal fields like `charge_energy_added` and `ideal_battery_range_km`. This ensures PostgreSQL does not truncate precision after insert, maintaining accurate energy calculations for efficiency analytics.

### Can positions exist without being associated with a drive?

Yes. While positions collected during a trip belong to a drive via `belongs_to :drive, Drive`, the association is optional. Positions recorded while the vehicle is parked (e.g., periodic "asleep" checks or pre-conditioning events) exist independently with only a `car_id` reference.

### What is the relationship between drives and addresses in the schema?

The Drive schema uses `belongs_to` associations for both `start_address` and `end_address`, which reference the `Address` schema. These are populated through reverse-geocoding services after a drive completes, enabling location-based filtering and trip naming (e.g., "Home to Office").

### Where are the database constraints defined for these schemas?

Foreign-key constraints and database-level validations are enforced through Ecto changesets in each schema file ([`charge.ex`](https://github.com/teslamate-org/teslamate/blob/main/charge.ex), [`drive.ex`](https://github.com/teslamate-org/teslamate/blob/main/drive.ex), [`position.ex`](https://github.com/teslamate-org/teslamate/blob/main/position.ex)). The `changeset/2` functions validate required fields and check constraints (such as positive `charger_phases`), while the actual database migrations (not shown in the schema files) define the PostgreSQL table structures and referential integrity rules.