Wer sein Tesla mit Teslamate loggt, möchte die Daten nicht nur sammeln, sondern auch übersichtlich darstellen.

Besonders spannend ist der monatliche Stromverbrauch zum Beispiel, um die Kosten besser im Blick zu behalten oder mit dem Haushaltsstrom abzugleichen.

In diesem Beitrag zeige ich dir, wie du den Monatsverbrauch aus Teslamate abfragst und in Home Assistant visualisierst.


SQL-Integration konfigurieren

In den Einstellungen -> Integrationen -> nach SQL suchen und hinzufügen.

Dort die Verbindungsdaten zu deiner Teslamate-PostgreSQL-Datenbank eintragen.

Monatliche Tesla Model 3 Auswertung

Name Der Name ist frei wählbar.

postgresql://<Benutzer>:<Passwort>@<IP-Telsmamate-DB>:5432/teslamate

Datenbank-URL An diese Stellen müssen <Benutzer>, <Passwort> und <IP-Teslamate-DB> durch die entsprechenden Zugangsdaten und die IP-Adresse deiner Teslamate-Datenbank ersetzt werden.

month_total_km

Spalte

SQL
WITH selected_car AS (
  SELECT id AS car_id
  FROM cars
  WHERE UPPER(vin) = UPPER('<Deine VIN>')
  LIMIT 1
),

time_range AS (
  SELECT 
    date_trunc('month', CURRENT_DATE + INTERVAL '2 hours') AS month_start,
    date_trunc('month', CURRENT_DATE + INTERVAL '2 hours') + INTERVAL '1 month' AS month_end
),

charging_sessions AS (
  SELECT
    cp.id,
    COALESCE(NULLIF(cp.charge_energy_used, 0), cp.charge_energy_added) AS charge_energy,
    cp.cost,
    CASE 
      WHEN NULLIF(mode() WITHIN GROUP (ORDER BY c.charger_phases), 0) IS NULL THEN 'DC'
      ELSE 'AC'
    END AS charge_type,
    COALESCE(
      geofences.name,
      CONCAT_WS(', ', 
        COALESCE(addresses.name, NULLIF(CONCAT_WS(' ', addresses.road, addresses.house_number), '')), 
        addresses.city
      )
    ) AS address
  FROM charging_processes cp
  LEFT JOIN charges c ON cp.id = c.charging_process_id
  LEFT JOIN addresses ON addresses.id = cp.address_id
  LEFT JOIN public.geofences ON geofences.id = cp.geofence_id
  CROSS JOIN time_range t
  CROSS JOIN selected_car sc
  WHERE 
    cp.car_id = sc.car_id AND
    cp.start_date >= t.month_start AND
    cp.start_date < t.month_end
  GROUP BY 
    cp.id, cp.charge_energy_used, cp.charge_energy_added, cp.cost, 
    geofences.name, addresses.name, addresses.road, addresses.house_number, addresses.city
),

aggregated AS (
  SELECT
    address,
    charge_type,
    ROUND(SUM(charge_energy)::numeric, 2) AS total_kwh,
    ROUND(SUM(cost)::numeric, 2) AS total_cost
  FROM charging_sessions
  GROUP BY address, charge_type
),

monthly_km AS (
  SELECT 
    ROUND((MAX(odometer) - MIN(odometer))::numeric, 0) AS month_total_km
  FROM public.positions p
  CROSS JOIN time_range t
  CROSS JOIN selected_car sc
  WHERE 
    p.car_id = sc.car_id AND
    DATE_TRUNC('month', p.date + INTERVAL '2 hours') = t.month_start
),

monthly_cost AS (
  SELECT 
    ROUND(SUM(cp.cost)::numeric, 2) AS month_total_cost
  FROM public.charging_processes cp
  CROSS JOIN time_range t
  CROSS JOIN selected_car sc
  WHERE 
    cp.car_id = sc.car_id AND
    cp.end_date >= t.month_start AND
    cp.end_date < t.month_end
),

drive_data AS (
  SELECT
    distance,
    duration_min,
    end_date,
    start_date,
    car.efficiency AS car_efficiency,
    NULLIF(GREATEST(start_ideal_range_km - end_ideal_range_km, 0), 0) AS range_diff,
    duration_min > 1 AND distance > 1 AND ( 
      start_position.usable_battery_level IS NULL OR end_position.usable_battery_level IS NULL OR
      (end_position.battery_level - end_position.usable_battery_level) = 0 
    ) AS is_sufficiently_precise
  FROM drives
  LEFT JOIN positions start_position ON start_position_id = start_position.id
  LEFT JOIN positions end_position ON end_position_id = end_position.id
  LEFT JOIN cars car ON car.id = drives.car_id
  CROSS JOIN time_range t
  CROSS JOIN selected_car sc
  WHERE
    start_date >= t.month_start AND
    start_date < t.month_end AND
    drives.car_id = sc.car_id AND
    distance >= 1 AND
    distance / COALESCE(NULLIF(duration_min, 0) * 60, extract(epoch from end_date - start_date)) * 3600 >= 5
),

avg_consumption AS (
  SELECT
    ROUND(AVG(
      CASE 
        WHEN is_sufficiently_precise AND distance > 0 THEN
          range_diff * car_efficiency / distance * 1000
        ELSE NULL
      END
    )::numeric) AS month_avg_consumption_Wh_per_km
  FROM drive_data
),

avg_prices AS (
  SELECT
    ROUND(SUM(total_cost) / NULLIF(SUM(total_kwh), 0), 2) AS month_avg_price_per_kwh,
    json_agg(json_build_object(
      'address', address,
      'charge_type', charge_type,
      'avg_price_per_kwh', 
        CASE WHEN total_kwh > 0 THEN ROUND(total_cost / total_kwh, 2) ELSE 0 END
    ) ORDER BY address, charge_type) AS avg_price_per_kwh_by_location
  FROM aggregated
)

SELECT 
  (
    SELECT json_agg(json_build_object(
      'address', address,
      'charge_type', charge_type,
      'total_kwh', total_kwh,
      'total_cost', total_cost,
      'avg_price_per_kwh', 
        CASE WHEN total_kwh > 0 THEN ROUND(total_cost / total_kwh, 2) ELSE 0 END
    ) ORDER BY total_kwh DESC, address, charge_type)
    FROM aggregated
  ) AS charging_sessions_json,

  COALESCE(mk.month_total_km, 0) AS month_total_km,
  COALESCE(mc.month_total_cost, 0) AS month_total_cost,
  COALESCE(ac.month_avg_consumption_Wh_per_km, 0) AS month_avg_consumption_Wh_per_km,
  COALESCE((SELECT ROUND(SUM(total_kwh)::numeric, 2) FROM aggregated), 0) AS month_total_kwh,
  COALESCE(ROUND(
    (mc.month_total_cost / NULLIF((SELECT ROUND(SUM(total_kwh)::numeric, 2) FROM aggregated), 0)) 
    * (ac.month_avg_consumption_Wh_per_km / 1000)
    , 3), 0.0) AS month_cost_per_km_estimated,
  COALESCE(ROUND(mc.month_total_cost / NULLIF(mk.month_total_km, 0), 3), 0.0) AS month_cost_per_km_actual,
  COALESCE(ap.month_avg_price_per_kwh, 0.0) AS month_avg_price_per_kwh

FROM avg_prices ap
CROSS JOIN monthly_km mk
CROSS JOIN monthly_cost mc
CROSS JOIN avg_consumption ac;
Expand

Abfrage An dieser Stelle muss lediglich <Deine VIN> durch die VIN deines Teslas ersetzt werden.

km

Maßeinheit

Messwert

Zustandsklasse

So sieht der fertige SQL-Sensor in Home Assistant nach der Einrichtung aus:


Visualisierung der Statistik für den aktuellen Monat

Da der Sensor eingerichtet ist, können wir die Daten direkt im Dashboard auswerten und übersichtlich darstellen. Die Darstellung zeigt alle relevanten Informationen zu den Ladevorgängen des aktuellen Monats:

  • 🏠 Adresse: Jede Ladesäule, an der dein Tesla geladen hat, wird hier aufgelistet.
    Mit farblicher Markierung siehst du auf einen Blick, wie teuer der Strom pro kWh war,
    von grün (günstig) über gelb/orange bis rot (teuer).
  • ⚡ Typ: Typ der Ladung z. B. AC oder DC
  • 🔋 Energie: Geladene kWh Gesamtenergie der Ladevorgänge
  • 💶 Kosten: Gesamtkosten der Ladevorgänge

Unterhalb der einzelnen Ladevorgänge zeigt die Übersicht wichtige Durchschnittswerte und Summen für den aktuellen Monat:

  • 📉 Ø Verbrauch: Durchschnittlicher Energieverbrauch pro Kilometer (Wh/km)
  • 💶 Ø Kosten pro km: Durchschnittliche Kosten pro gefahrenem Kilometer
  • 💶 Ø Kosten pro kWh: Durchschnittspreis pro Kilowattstunde
  • 🚗 Gefahrene Kilometer: Gesamte Kilometer im Monat
  • 🔋 Gesamtladung: Summe aller geladenen kWh
  • 💰 Gesamtkosten: Gesamtkosten für alle Ladevorgänge

 Karte im Dashboard hinzufügen

Nun, da der Sensor eingerichtet ist, kannst du die Monatsstatistik im Dashboard anzeigen:

  1. Gehe in dein Dashboard und klicke oben rechts auf „Bearbeiten“.
  2. Wähle „Karte hinzufügen“ → „Manuell“.
  3. Kopiere den folgenden Code in das Eingabefeld der manuellen Karte
    (<Dein eben erstellten Sensor> muss ersetzt werden!)
type: markdown
content: |-
  <h3>Statistik für den aktuellen Monat</h3>
  <table width=100%>
    <tr>
      <th align=left>🏠 Adresse</th>
      <th align=left>⚡ Typ</th>
      <th align=right>🔋 Energy</th>
      <th align=right>💶 Kosten</th>
    </tr>
    {%- set auswertungs_Sensor = '<Dein eben erstellten Sensor>' -%}
    
    {%- macro blend_with_white(r, g, b, alpha=0.6) -%}
      {# alpha = Anteil Originalfarbe, 1-alpha = Anteil Weiß #}
      {%- set r_new = (r * alpha + 255 * (1 - alpha)) | round -%}
      {%- set g_new = (g * alpha + 255 * (1 - alpha)) | round -%}
      {%- set b_new = (b * alpha + 255 * (1 - alpha)) | round -%}
      {{ '#%02x%02x%02x' % (r_new, g_new, b_new) }}
    {%- endmacro -%}
    
    {%- set sessions = state_attr(auswertungs_Sensor, 'charging_sessions_json') -%}
    {%- if sessions and sessions | count > 0 -%}
      {%- set max_price = (sessions | map(attribute='avg_price_per_kwh') | max) | float -%}
      {%- for item in sessions -%}
        {%- set price = item.avg_price_per_kwh | float(0) -%}
        
        {%- if price == 0 -%}
          {%- set color = '#90EE90' %}  {# Blassgrün #}
        
        {%- elif price <= 0.40 -%}
          {# Verlauf Grün (#90EE90) bis Gelb (#FFFF00) #}
          {%- set ratio = (price / 0.40) | float(1) -%}
          {%- set r = (144 + (255 - 144) * ratio) | round -%}
          {%- set g = (238 + (255 - 238) * ratio) | round -%}
          {%- set b = (144 + (0 - 144) * ratio) | round -%}
          {%- set color = blend_with_white(r, g, b, 0.6) -%}
        
        {%- elif price < 0.50 -%}
          {# Verlauf Gelb (#FFFF00) bis Orange (#FFA500) #}
          {%- set ratio = ((price - 0.40) / 0.10) | float(1) -%}
          {%- set r = 255 -%}
          {%- set g = (255 + (165 - 255) * ratio) | round -%}
          {%- set b = 0 -%}
          {%- set color = blend_with_white(r, g, b, 0.6) -%}
        
        {%- else -%}
          {# Verlauf Orange (#FFA500) bis Rot (#FF0000) #}
          {%- set max_price_capped = max_price if max_price > 0 else 0.50 -%}
          {%- set capped_price = price if price <= max_price_capped else max_price_capped -%}
          {%- set ratio = ((capped_price - 0.50) / (max_price_capped - 0.50)) | float(1) -%}
          {%- set r = 255 -%}
          {%- set g = (165 + (0 - 165) * ratio) | round -%}
          {%- set b = 0 -%}
          {%- set color = blend_with_white(r, g, b, 0.6) -%}
        {%- endif -%}
        
        <tr>
          <td align=left>
            {{ item.address }}<br>
            <font color="{{ color }}">
              <small>Ø {{ '%.2f' | format(price) }} €/kWh</small>
            </font>
          </td>
          <td align=center>{{ item.charge_type }}</td>
          {%- if item.total_kwh is not none -%}
            <td align=right>{{ '%.0f' | format(item.total_kwh) }} kWh</td>
            <td align=right>{{ '%.2f' | format(item.total_cost | default(0, true)) }} €</td>
          {%- else -%}
            <td align=right>🔌 Lädt…</td>
            <td align=right>--.-- €</td>
          {%- endif -%}
        </tr>
      {%- endfor -%}
    {%- else -%}
      <tr>
        <td colspan=4 align=center><br><b>Keine Ladevorgänge verfügbar.</b></td>
      </tr>
    {%- endif -%}

    <tr><td colspan=4><hr></td></tr>
    <tr>
      <td colspan=2 align=left><strong>📉 Ø Verbrauch:</strong></td>
      <td colspan=2 align=right>{{ state_attr(auswertungs_Sensor, 'month_avg_consumption_wh_per_km') | int }} Wh/km</td>
    </tr>
    <tr>
      <td colspan=2 align=left><strong>💶 Ø Kosten per km:</strong></td>
      <td colspan=2 align=right>{{ state_attr(auswertungs_Sensor, 'month_cost_per_km_estimated') }} €/km</td>
    </tr>
    <tr>
      <td colspan=2 align=left><strong>💶 Ø Kosten per kWh:</strong></td>
      <td colspan=2 align=right>{{ state_attr(auswertungs_Sensor, 'month_avg_price_per_kwh') }} €/kWh</td>
    </tr>
    <tr>
      <td colspan=2 align=left><strong>🚗 Gefahrene Kilometer:</strong></td>
      <td colspan=2 align=right>{{ states(auswertungs_Sensor) }} km</td>
    </tr>
    <tr>
      <td colspan=2 align=left><strong>🔋 Gesamtladung:</strong></td>
      <td colspan=2 align=right>{{ '%.0f' | format(state_attr(auswertungs_Sensor, 'month_total_kwh')) }} kWh</td>
    </tr>
    <tr>
      <td colspan=2 align=left><strong>💰 Gesamtkosten:</strong></td>
      <td colspan=2 align=right>{{ state_attr(auswertungs_Sensor, 'month_total_cost') }} €</td>
    </tr>
  </table>
Expand
  1. Klicke auf Speichern die Monatsstatistik sollte nun direkt im Dashboard angezeigt werden.

Fertig! Jetzt hast du alle Ladevorgänge des aktuellen Monats übersichtlich mit Energie, Kosten und farblicher Kennzeichnung auf einen Blick verfügbar.