How TeslaMate Publishes Vehicle Data to MQTT Topics Using the MQTT Publisher
TeslaMate publishes vehicle telemetry to an MQTT broker through a GenServer-based publisher module that wraps the Tortoise311 client, supporting both fire-and-forget and acknowledged delivery modes.
TeslaMate is an open-source data logger for Tesla vehicles built with Elixir. To enable real-time integrations with home automation systems and monitoring tools, the application exposes vehicle state through MQTT topics. Understanding how the MQTT publisher publishes vehicle data to topics reveals the architectural patterns that ensure reliable message delivery.
The MQTT Publisher Architecture
The core publishing logic resides in TeslaMate.Mqtt.Publisher, a GenServer that serializes publish requests and manages the lifecycle of MQTT message delivery.
GenServer Structure and Initialization
The publisher is initialized once at application startup by TeslaMate.Application. It maintains state to track in-flight messages requiring acknowledgment. Because it runs as a GenServer, all publish requests are serialized, ensuring ordered delivery and preventing race conditions when multiple vehicle events occur simultaneously.
Tortoise311 Integration
The publisher delegates actual network operations to the Tortoise311 MQTT client library. When TeslaMate.Mqtt.Publisher.publish/3 is invoked, the GenServer calls Tortoise311.publish/4 with the topic string, payload, and MQTT options including Quality of Service (QoS) and retain flags.
How Vehicle Data Flows to MQTT Topics
Vehicle telemetry reaches MQTT subscribers through a coordinated pipeline involving event subscription, payload transformation, and topic routing.
Event Subscription in VehicleSubscriber
The TeslaMate.Mqtt.PubSub.VehicleSubscriber module subscribes to internal vehicle events such as state changes, location updates, and charging status. When the subscriber receives a TeslaMate.Vehicles.Vehicle struct update, it triggers the publishing workflow.
In lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex, the subscriber aliases the publisher:
alias TeslaMate.Mqtt.Publisher
Topic Construction and Payload Serialization
The subscriber constructs topic names following the hierarchical pattern:
teslamate/vehicles/<vehicle_id>/<event_type>
Here, <vehicle_id> represents the vehicle's identifier and <event_type> can be state, position, charge, or other telemetry categories. The module converts the vehicle struct into a JSON-serializable map using TeslaMate.Vehicles.VehicleSummary, then encodes it with Jason:
topic = "teslamate/vehicles/#{vehicle.id}/state"
payload = vehicle |> VehicleSummary.into() |> Jason.encode!()
The Publishing Workflow
The subscriber initiates the publish operation by calling Publisher.publish/3 with the constructed topic, JSON payload, and MQTT options:
Publisher.publish(topic, payload, qos: 1, retain: true)
This call enters the GenServer's handle_call/3 callback in lib/teslamate/mqtt/publisher.ex, where the QoS level determines the subsequent handling strategy.
QoS Handling and Delivery Guarantees
The publisher distinguishes between at-most-once and at-least-once delivery semantics based on the requested QoS level.
Fire-and-Forget (QoS 0)
When publishing with QoS 0, the GenServer immediately invokes Tortoise311.publish/4 and returns :ok to the caller without waiting for broker confirmation. This mode offers minimal latency but no delivery guarantees.
Acknowledged Delivery (QoS > 0)
For QoS 1 or 2, the publisher stores the message reference returned by Tortoise311.publish/4 in its GenServer state. The process then awaits the broker's acknowledgment through handle_info/2. Once received, the publisher forwards the result to the original caller, completing the synchronous publish cycle.
This approach ensures that vehicle state changes are reliably propagated to subscribers before the operation returns.
Summary
- TeslaMate.Mqtt.Publisher acts as a GenServer wrapper around Tortoise311, serializing all MQTT publish operations in
lib/teslamate/mqtt/publisher.ex. - VehicleSubscriber in
lib/teslamate/mqtt/pubsub/vehicle_subscriber.excaptures vehicle events and formats them as JSON payloads usingVehicleSummary. - Topics follow the structure
teslamate/vehicles/<vehicle_id>/<event_type>to organize telemetry hierarchically. - QoS 0 provides fire-and-forget publishing, while QoS > 0 implements acknowledged delivery with in-flight message tracking.
- The architecture ensures ordered, reliable delivery of vehicle data to MQTT brokers.
Frequently Asked Questions
What MQTT topic structure does TeslaMate use?
TeslaMate publishes to topics under the prefix teslamate/vehicles/<vehicle_id>/, where <vehicle_id> is the vehicle identifier and the final segment indicates the data type such as state, position, or charge. This hierarchical structure allows subscribers to listen to specific vehicles or event types using MQTT wildcards.
How does TeslaMate handle MQTT connection failures?
The underlying Tortoise311 client manages connection state and reconnection logic. The TeslaMate.Mqtt.Publisher GenServer maintains its state independently and will attempt to publish once the connection recovers. For QoS > 0 messages, the broker's acknowledgment mechanism ensures eventual delivery or explicit timeout errors.
What QoS levels does TeslaMate support?
The publisher supports QoS 0 (at most once), QoS 1 (at least once), and QoS 2 (exactly once) through Tortoise311. Vehicle state updates typically use QoS 1 with the retain flag enabled to ensure new subscribers immediately receive the last known state.
How is the vehicle data formatted before publishing?
The TeslaMate.Vehicles.VehicleSummary module transforms the internal Vehicle struct into a flat map suitable for JSON serialization. The VehicleSubscriber then uses Jason.encode!/1 to convert this map into a JSON string before passing it to the publisher.
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 →