How Settings Are Managed in TeslaMate: A Deep Dive into the Configuration Layer
TeslaMate organizes all configuration within a dedicated Settings context that uses two Ecto schemas—GlobalSettings and CarSettings—to persist data in PostgreSQL and trigger side effects like efficiency recalculation and geocoder refreshes.
TeslaMate, the open-source Tesla data logger built with Elixir and Phoenix, implements a robust settings management layer that separates global application preferences from per-vehicle configurations. All settings are managed through the TeslaMate.Settings context module, which provides a transactional API for reading, updating, and validating configuration data while coordinating side effects across the application's logging, location, and vehicle supervision subsystems.
The Settings Context Architecture
The settings layer is organized around two distinct Ecto schemas defined in lib/teslamate/settings/global_settings.ex and lib/teslamate/settings/car_settings.ex.
GlobalSettings Schema
The GlobalSettings schema maps to the settings table and stores application-wide options that affect the entire TeslaMate instance. This includes unit preferences (kilometers vs. miles), UI language, theme selection, and base URLs for external services. The schema is defined with strict validation rules and default values to ensure data integrity across the application.
CarSettings Schema
The CarSettings schema persists per-vehicle configuration in the car_settings table. These records store charging behavior preferences, streaming API toggles, and an enable/disable flag that controls whether TeslaMate actively monitors a specific vehicle. Each CarSettings record maintains a foreign key relationship to its associated Car record, allowing the context to preload vehicle data when retrieving configuration.
Reading and Updating Settings via the Public API
The TeslaMate.Settings module in lib/teslamate/settings.ex exposes a clean public API for interacting with configuration data. All database operations are wrapped in transactions to maintain consistency.
Retrieving Current Configuration
To fetch the global configuration, use get_global_settings!/0, which returns the single global row:
global = TeslaMate.Settings.get_global_settings!()
IO.inspect(global.unit_of_length) # :km or :mi
IO.inspect(global.language) # e.g., "en"
For per-vehicle settings, get_car_settings/0 returns all car settings pre-loaded with their associated Car records:
car_settings = TeslaMate.Settings.get_car_settings()
Updating Settings with Side Effects
The update_global_settings/2 and update_car_settings/2 functions wrap changeset-driven updates in transactions and automatically trigger side effects. For example, changing the UI language refreshes geocoded addresses:
{:ok, new_global} =
TeslaMate.Settings.update_global_settings(
global,
%{language: "de"} # change to German
)
Similarly, updating car-specific behavior like the suspend interval uses the car settings changeset:
car_settings = Enum.find(TeslaMate.Settings.get_car_settings(), &(&1.car.id == 1))
{:ok, updated} =
TeslaMate.Settings.update_car_settings(
car_settings,
%{suspend_min: 30} # suspend after 30 minutes of idle
)
Building Changesets for Forms
For UI forms that require validation before submission, the context exposes change_global_settings/2 and change_car_settings/2 to generate raw changesets:
changeset = TeslaMate.Settings.change_global_settings(global)
Side Effects and Change Propagation
When settings change, the context module coordinates with other TeslaMate subsystems to ensure data consistency. The update_global_settings/2 function invokes on_language_change/2 to trigger Locations.refresh_addresses/1, re-geocoding stored locations when the display language changes.
Changing the preferred range setting (ideal vs. rated) automatically calls Log.recalculate_efficiencies/1 to update efficiency calculations across historical drives. If the enabled flag on a car toggles, the context invokes Vehicles.restart/0 to restart the vehicle supervision tree, immediately applying the new monitoring state.
The Web Interface
The settings UI is implemented as a Phoenix LiveView module located at lib/teslamate_web/live/settings_live/index.ex. This component loads current settings, builds changesets for validation, and dispatches updates to the Settings context upon form submission.
The route is mounted at /settings in lib/teslamate_web/router.ex, providing a real-time interface where changes are validated and persisted immediately without page reloads.
Database Persistence and Migrations
All settings are stored in PostgreSQL with standard Ecto migrations. The settings table was created in priv/repo/migrations/20190731154452_create_settings.exs, while the car_settings table added per-vehicle configuration including the enabled column in priv/repo/migrations/20240603152807_add_enabled_to_car_settings.exs.
This migration-based approach ensures that schema changes are version-controlled and deployable across different environments.
Summary
- Dual-schema architecture: TeslaMate separates global preferences (
GlobalSettings) from per-vehicle options (CarSettings) using distinct Ecto schemas. - Transactional API: The
TeslaMate.Settingscontext providesget_global_settings!/0,update_global_settings/2, and car-specific equivalents that wrap database operations in transactions. - Automatic side effects: Changing language refreshes addresses, modifying range preference recalculates efficiencies, and toggling car enabled status restarts vehicle processes.
- LiveView interface: The
/settingsroute renders a real-time configuration interface usingSettingsLive.Index. - PostgreSQL persistence: Settings migrate through standard Ecto migration files and maintain relational integrity with the cars table.
Frequently Asked Questions
Where are TeslaMate settings stored?
TeslaMate persists all configuration in a PostgreSQL database using two tables: settings for global application preferences and car_settings for per-vehicle configurations. These tables are managed by Ecto schemas defined in lib/teslamate/settings/global_settings.ex and lib/teslamate/settings/car_settings.ex, with migrations located in priv/repo/migrations/.
What happens when I change the preferred range setting in TeslaMate?
When you update the preferred range from ideal to rated (or vice versa) via update_global_settings/2, the Settings context automatically triggers Log.recalculate_efficiencies/1. This function recalculates efficiency metrics across your historical drive data to ensure consistency with the new range preference.
How can I programmatically subscribe to settings changes for a specific car?
You can subscribe to settings changes using the subscribe_to_changes/1 function in the Settings context. Pass the car struct as an argument, and your process will receive messages whenever that vehicle's settings are updated:
TeslaMate.Settings.subscribe_to_changes(car)
Why does TeslaMate restart vehicle processes when I disable a car?
The update_car_settings/2 function detects changes to the enabled flag and invokes Vehicles.restart/0 to restart the vehicle supervision tree. This ensures that the streaming API connection and data collection processes are immediately terminated for disabled vehicles or restarted for newly enabled ones, maintaining proper resource management and API rate limit compliance.
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 →