# How TeslaMate Publishes Vehicle Data to MQTT Topics Using the MQTT Publisher

> Learn how TeslaMate publishes vehicle data to MQTT topics using the MQTT publisher. Explore its GenServer module and Tortoise311 client for reliable data delivery.

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

---

**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`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex), the subscriber aliases the publisher:

```elixir
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:

```elixir
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:

```elixir
Publisher.publish(topic, payload, qos: 1, retain: true)

```

This call enters the GenServer's `handle_call/3` callback in [`lib/teslamate/mqtt/publisher.ex`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/mqtt/publisher.ex).
- **VehicleSubscriber** in [`lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/mqtt/pubsub/vehicle_subscriber.ex) captures vehicle events and formats them as JSON payloads using `VehicleSummary`.
- 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.