Dashboard configuratie
Documentatie van de aangepaste TeslaMate-dashboards en bijbehorende Grafana-instellingen.
Let op: de SQL-query's in deze handleiding zijn specifiek afgestemd op deze TeslaMate-database en dashboardconfiguratie. Pas query's niet zomaar aan. Bij wijzigingen eerst controleren welke andere dashboards of panelen dezelfde logica gebruiken.
1. Dashboardinstellingen
1.1 Battery Health
Waarden
| Instelling | Waarde |
|---|---|
| Battery Capacity – Usable (new) | 49.0 kWh |
| Ranges – Max range (new) | 320.0 km |
Deze waarden worden gebruikt als referentie voor de batterijcapaciteit en de maximale actieradius van de auto.
2. Charges
2.1 Estimated Fuel Costs
Doel
Toont wat de gereden afstand naar schatting zou hebben gekost wanneer dezelfde afstand met een benzineauto was gereden.
De berekening gebruikt:
-
de gereden afstand;
-
het ingestelde benzineverbruik in L/100 km;
-
de ingestelde benzineprijs in €/L.
Hiermee kan de laadprijs van de Tesla worden vergeleken met een geschatte brandstofprijs.
Positie
Plaats het paneel:
tussen Total Charging Cost en Duration.
Query
SELECT
(MAX(odometer) - MIN(odometer)) / 100.0 *
COALESCE(
NULLIF(
REPLACE(
REGEXP_REPLACE('${fuel_consumption:text}', '[^0-9.,]', '', 'g'),
',',
'.'
),
''
)::numeric,
6.99
) *
COALESCE(
NULLIF(
REGEXP_REPLACE('${fuel_price:text}', '[^0-9.]', '', 'g'),
''
)::numeric,
1.87
) AS "Estimated Fuel Costs"
FROM positions
WHERE
car_id = $car_id
AND ideal_battery_range_km IS NOT NULL
AND (
drive_id IN (SELECT id FROM drives WHERE $__timeFilter(start_date))
OR (drive_id IS NULL AND $__timeFilter(date))
)
Variabelen
De query gebruikt de dashboardvariabelen:
-
$fuel_consumption -
$fuel_price
Hiermee kan de gebruiker het benzineverbruik en de benzineprijs vanuit het dashboard aanpassen.
De standaardwaarden die in de query als fallback worden gebruikt zijn:
-
6,99 L/100 km
-
€1,87/L
Let op: de fallbackwaarden in de query zijn niet automatisch gelijk aan de waarden van de Grafana-variabelen. Wanneer de variabelen worden aangepast, controleer daarom ook of de fallbackwaarden nog gewenst zijn.
3. Charging Stats
3.1 Total Range Added
Doel
Geeft aan hoeveel theoretische actieradius tijdens de geselecteerde laadperiode is toegevoegd.
Positie
Plaats het paneel:
tussen Total Energy Added en Ø Cost per kWh.
Query
SELECT
convert_km(
SUM(end_${preferred_range}_range_km - start_${preferred_range}_range_km)::numeric,
'$length_unit'
) AS "Total Range Added"
FROM
charging_processes cp
WHERE
$__timeFilter(end_date)
AND duration_min >= $min_duration
AND car_id = $car_id
AND ('${geofence:pipe}' = '-1' OR cp.geofence_id in ($geofence));
3.2 Ø Cost per km (charging stats)
Doel
Berekent de gemiddelde laadkosten per gereden kilometer.
De berekening combineert:
-
het geschatte energieverbruik van de gereden kilometers;
-
de gemiddelde laadprijs per kWh;
-
de totale gereden afstand.
De uitkomst wordt weergegeven als kosten per kilometer.
Positie
Plaats het paneel tussen Ø Cost per 100 km en Total Charging Cost.
Query
Belangrijk: deze query gebruikt dezelfde onderliggende berekeningslogica als onderdelen van de Charging Stats-, Efficiency-, Statistics- en Trip-dashboards. Wanneer deze gedeelde logica ooit wordt aangepast, controleer dan ook de andere panelen waarin dezelfde berekening voorkomt.
-- Query shared between Charging Stats, Efficiency, Statistics & Trip Dashboards (with minor changes) - ensure to modify in all places when necessary
with drives_start_event as (
select
'drive_start' as event, start_date as date, start_${preferred_range}_range_km as range, start_km as odometer, car_id, distance is null as is_incomplete
from drives
where car_id = $car_id and $__timeFilter(start_date) and 48 <= ((${__to:date:seconds} - ${__from:date:seconds})::numeric / 3600)
),
drives_end_event as (
select
'drive_end' as event, case when end_date is null then start_date + interval '1 second' else end_date end as date, end_${preferred_range}_range_km as range, end_km as odometer, car_id, distance is null as is_incomplete
from drives
where car_id = $car_id and $__timeFilter(start_date) and 48 <= ((${__to:date:seconds} - ${__from:date:seconds})::numeric / 3600)
),
charging_processes_start_event as (
select
'charging_process_start' as event, start_date as date, start_${preferred_range}_range_km as range, p.odometer, cp.car_id, end_date is null as is_incomplete
from charging_processes cp
inner join positions p on cp.position_id = p.id
where cp.car_id = $car_id and $__timeFilter(end_date) and 48 <= ((${__to:date:seconds} - ${__from:date:seconds})::numeric / 3600)
),
charging_processes_end_event as (
select
'charging_process_end' as event, case when end_date is null then start_date + interval '1 second' else end_date end as date, end_${preferred_range}_range_km as range, p.odometer, cp.car_id, end_date is null as is_incomplete
from charging_processes cp
inner join positions p on cp.position_id = p.id
where cp.car_id = $car_id and $__timeFilter(end_date) and 48 <= ((${__to:date:seconds} - ${__from:date:seconds})::numeric / 3600)
),
positions as (
select
case
when drive_id is not null and lead(drive_id) over w is not null then 'drive_start'
else 'something'
end as event,
date, ${preferred_range}_battery_range_km as range, p.odometer, p.car_id, false as is_incomplete
from positions p
where ideal_battery_range_km is not null and car_id = $car_id and 48 > ((${__to:date:seconds} - ${__from:date:seconds})::numeric / 3600)
and (drive_id in (select id from drives where $__timeFilter(start_date)) or drive_id is null and $__timeFilter(date))
window w as (order by date)
),
combined as (
select * from drives_start_event
union all
select * from drives_end_event
union all
select * from charging_processes_start_event
union all
select * from charging_processes_end_event
union all
select * from positions
),
final as (
select
car_id,
case when is_incomplete then 0 else lead(odometer) over w - odometer end as distance,
case when is_incomplete then 0 else case when event != 'drive_start' then greatest(range - lead(range) over w, 0) else range - lead(range) over w end end as range_loss
from combined
window w as (order by date asc)
),
derived as (
select
convert_km(sum(distance)::numeric, '$length_unit') as distance,
sum(range_loss) * c.efficiency as consumption
from final
inner join cars c on car_id = c.id
group by c.efficiency
),
charges as (
SELECT
sum(cost) / sum(charge_energy_added) as cost_per_kwh
FROM charging_processes cp
where cp.car_id = $car_id and $__timeFilter(end_date)
and ('${geofence:pipe}' = '-1' OR cp.geofence_id in ($geofence))
)
select
consumption / distance * cost_per_kwh as cost_per_km
from derived cross join charges
3.3 Top Charging Stations (Cheapest)
Doel
Toont de laadlocaties met de laagste gemiddelde laadkosten per kWh.
Belangrijk: dit is niet de goedkoopste individuele laadsessie. De prijs wordt per locatie berekend als een gewogen gemiddelde op basis van de totale geladen energie. Een locatie met meerdere sessies wordt dus als één gemiddelde weergegeven.
Query
SELECT
COALESCE(
geofence.name,
CONCAT_WS(
', ',
COALESCE(address.name, NULLIF(CONCAT_WS(' ', address.road, address.house_number), '')),
COALESCE(address.city, address.neighbourhood)
)
) AS location,
SUM(cost) / NULLIF(SUM(GREATEST(charge_energy_added, charge_energy_used)), 0) AS cost_per_kwh
FROM
charging_processes c
LEFT JOIN addresses address ON c.address_id = address.id
LEFT JOIN geofences geofence ON geofence_id = geofence.id
WHERE
$__timeFilter(end_date)
AND duration_min >= $min_duration
AND car_id = $car_id
AND cost IS NOT NULL
AND ('${geofence:pipe}' = '-1' OR c.geofence_id IN ($geofence))
GROUP BY
1
HAVING
SUM(GREATEST(charge_energy_added, charge_energy_used)) > 0
ORDER BY
2 ASC NULLS LAST
LIMIT 17;
4. Efficiency
4.1 Temperature – Driving Efficiency
Doel
Toont het energieverbruik en de efficiëntie van de auto per buitentemperatuur.
De temperatuur kan worden gegroepeerd in stappen van:
-
1 °C;
-
5 °C;
-
10 °C.
Hierdoor kunnen kleine temperatuurverschillen worden samengevoegd en ontstaat bij grotere datasets een beter leesbare grafiek.
Temperatuurstap
De gebruikte variabele is:
| Instelling | Waarde |
|---|---|
| Type | Custom |
| Naam | temp_step |
| Label | Temp step |
| Display | Above dashboard |
| Waarden | 1, 5, 10 |
Query
WITH t AS (
SELECT
CASE
WHEN '$temp_unit' = 'C' THEN ROUND(cast(outside_temp_avg AS numeric) / $temp_step, 0) * $temp_step
WHEN '$temp_unit' = 'F' THEN ROUND(cast(convert_celsius(outside_temp_avg, '$temp_unit') AS numeric) / $temp_step, 0) * $temp_step
END AS outside_temp,
sum(start_ideal_range_km - end_ideal_range_km) AS total_ideal_range,
sum(start_rated_range_km - end_rated_range_km) AS total_rated_range,
sum(distance) AS total_distance,
sum(duration_min) as duration,
car_id
FROM
drives
WHERE
distance IS NOT NULL
AND car_id = $car_id
AND convert_km(distance::numeric, '$length_unit') >= $min_distance
AND $__timeFilter(start_date)
AND start_${preferred_range}_range_km - end_${preferred_range}_range_km > 0.1
GROUP BY
1,
car_id
)
SELECT
outside_temp as outside_temp_$temp_unit,
total_distance / total_${preferred_range}_range AS efficiency,
total_${preferred_range}_range / convert_km(total_distance::numeric, '$length_unit') * c.efficiency * 1000 AS consumption_$length_unit,
convert_km(total_distance::numeric, '$length_unit') as total_distance_$length_unit,
(convert_km(total_distance::numeric, '$length_unit') / duration) * 60 as avg_speed_$length_unit
FROM
t
JOIN cars c ON t.car_id = c.id
WHERE outside_temp IS NOT NULL
ORDER BY 1 DESC
5. Statistics
5.1 Statistics
Doel
Dit paneel toont de statistieken per geselecteerde periode.
Standaard worden bij weinig ritten en wanneer er geen laadgegevens beschikbaar zijn niet alle gemiddelden berekend.
De query is daarom aangepast zodat ontbrekende gegevens waar mogelijk worden aangevuld met een berekende waarde op basis van het gereden energieverbruik.
Let op: deze aanpassing is specifiek bedoeld om ook bij weinig ritten of zonder directe laadgegevens bruikbare statistieken te tonen. Hierdoor kunnen sommige waarden een berekende schatting zijn in plaats van rechtstreeks gemeten laadgegevens.
Query
WITH periods AS (
SELECT
CASE
WHEN '$period' = 'total' THEN TIMESTAMPTZ 'epoch'
ELSE date_trunc('$period', timezone('UTC', start_date), '$__timezone')
END AS date,
SUM(GREATEST(start_${preferred_range}_range_km - end_${preferred_range}_range_km, 0)) AS range_diff
FROM drives
WHERE
car_id = $car_id
AND $__timeFilter(start_date)
GROUP BY 1
),
charges AS (
SELECT
CASE
WHEN '$period' = 'total' THEN TIMESTAMPTZ 'epoch'
ELSE date_trunc('$period', timezone('UTC', start_date), '$__timezone')
END AS date,
SUM(GREATEST(charge_energy_added, charge_energy_used)) AS sum_energy_used_kwh,
SUM(charge_energy_added) AS sum_energy_added_kwh,
SUM(GREATEST(charge_energy_added, charge_energy_used)) / COUNT(*) AS avg_energy_charged_kwh,
SUM(cost) AS cost_charges,
COUNT(*) AS cnt_charges
FROM charging_processes
WHERE
car_id = $car_id
AND $__timeFilter(start_date)
AND (charge_energy_added IS NULL OR charge_energy_added > 0)
GROUP BY 1
),
avg_cost AS (
SELECT
SUM(cost) / NULLIF(SUM(GREATEST(charge_energy_added, charge_energy_used)), 0) AS cost_per_kwh
FROM charging_processes
WHERE
car_id = $car_id
AND $__timeFilter(start_date)
AND (charge_energy_added IS NULL OR charge_energy_added > 0)
AND cost IS NOT NULL
),
car AS (
SELECT efficiency
FROM cars
WHERE id = $car_id
)
SELECT
CASE WHEN '$period' = 'total' THEN $__from ELSE EXTRACT(EPOCH FROM p.date) * 1000 END AS date_from,
CASE
WHEN '$period' = 'total' THEN $__to
ELSE EXTRACT(EPOCH FROM timezone('$__timezone', timezone('$__timezone', p.date) + ('1 ' || '$period')::interval)) * 1000
END AS date_to,
CASE '$period'
WHEN 'total' THEN 'Total'
WHEN 'month' THEN to_char(timezone('$__timezone', p.date), 'YYYY Month')
WHEN 'year' THEN to_char(timezone('$__timezone', p.date), 'YYYY')
WHEN 'week' THEN 'week ' || to_char(timezone('$__timezone', p.date), 'WW') || ' starting ' || to_char(timezone('$__timezone', p.date), 'YYYY-MM-DD')
ELSE to_char(timezone('$__timezone', p.date), 'YYYY-MM-DD')
END AS display,
p.date,
COALESCE(c.sum_energy_used_kwh, p.range_diff * car.efficiency) AS sum_energy_used_kwh,
COALESCE(c.sum_energy_added_kwh, p.range_diff * car.efficiency) AS sum_energy_added_kwh, -- nodig tegen ∞
c.avg_energy_charged_kwh, -- blijft leeg zonder charge
COALESCE(c.cost_charges, p.range_diff * car.efficiency * avg_cost.cost_per_kwh) AS cost_charges,
c.cnt_charges
FROM periods p
LEFT JOIN charges c ON c.date = p.date
CROSS JOIN avg_cost
CROSS JOIN car
6. Trip
6.1 Ø Cost per km (trip)
Doel
Geeft de gemiddelde kosten per gereden kilometer voor de geselecteerde trip/periode.
Dit paneel kan worden gebruikt in plaats van Ø Cost per 100 km.
Voor een individuele trip is kosten per kilometer vaak logischer dan kosten per 100 km, omdat de daadwerkelijke rit centraal staat.
De query gebruikt de volgende volgorde voor het bepalen van de laadprijs:
-
gemiddelde €/kWh binnen de geselecteerde periode;
-
gemiddelde €/kWh van alle beschikbare laadgegevens;
-
een vaste fallback van €0,38/kWh.
Hierdoor blijft het paneel bruikbaar wanneer er in de geselecteerde periode geen laadsessie aanwezig is.
Query
WITH charges AS (
SELECT
COALESCE(
-- 1. €/kWh in de geselecteerde periode
SUM(cost) FILTER (WHERE $__timeFilter(start_date))
/ NULLIF(SUM(charge_energy_added) FILTER (WHERE $__timeFilter(start_date)), 0),
-- 2. overall €/kWh (buiten de periode)
SUM(cost) / NULLIF(SUM(charge_energy_added), 0),
-- 3. vaste fallback
0.38
) AS cost_per_kwh
FROM charging_processes
WHERE
car_id = $car_id
AND cost IS NOT NULL
AND cost > 0
),
mileage AS (
SELECT
convert_km((MAX(odometer) - MIN(odometer))::numeric, '$length_unit') AS distance
FROM positions
WHERE
car_id = $car_id
AND ideal_battery_range_km IS NOT NULL
AND (
drive_id IN (SELECT id FROM drives WHERE $__timeFilter(start_date))
OR (drive_id IS NULL AND $__timeFilter(date))
)
)
SELECT
'Total Energy consumed (gross)' AS metric,
cost_per_kwh / NULLIF(distance, 0) AS cost_mileage
FROM mileage
CROSS JOIN charges
Transformation
| Transformation | Alias |
|---|---|
| Transformation | Alias |
| Alias | Ø Cost per $length_unit |
7. Estimated Fuel Costs
7.1 Query
Deze berekening is dezelfde basisberekening als het paneel Estimated Fuel Costs binnen Charges, maar wordt hier toegepast op het betreffende dashboard/paneel.
SELECT
(MAX(odometer) - MIN(odometer)) / 100.0 *
COALESCE(
NULLIF(
REPLACE(
REGEXP_REPLACE('${fuel_consumption:text}', '[^0-9.,]', '', 'g'),
',',
'.'
),
''
)::numeric,
6.99
) *
COALESCE(
NULLIF(
REGEXP_REPLACE('${fuel_price:text}', '[^0-9.]', '', 'g'),
''
)::numeric,
1.87
) AS "Estimated Fuel Costs"
FROM positions
WHERE
car_id = $car_id
AND ideal_battery_range_km IS NOT NULL
AND (
drive_id IN (SELECT id FROM drives WHERE $__timeFilter(start_date))
OR (drive_id IS NULL AND $__timeFilter(date))
)
7.2 Eenheid instellen
Wanneer het resultaat als eurobedrag moet worden weergegeven:
-
Verwijder de bestaande override.
-
Voeg een nieuwe override toe.
-
Selecteer het veld:
Estimated Fuel Costs -
Stel de eenheid in op:
Euro (€).
Override
| Instelling | Waarde |
|---|---|
| Velden met naam | Estimated Fuel Costs |
| Overschrijvende eigenschap | Eenheid |
| Eenheid | Euro |
8. Dashboardvariabelen
De brandstofberekeningen gebruiken twee variabelen.
8.1 Brandstofprijs
| Instelling | Waarde |
|---|---|
| Type | Custom (of Text box) |
| Name | fuel_price |
| Label | Fuel price (€/L) |
| Values | 1.87, 2.00, 2.20, 2.40, 2.411, 2.60 |
| Default | 1.87 |
De waarde bepaalt de gebruikte benzineprijs in euro per liter.
8.2 Brandstofverbruik
| Instelling | Waarde |
|---|---|
| Type | Custom (of Text box) |
| Name | fuel_consumption |
| Label | Fuel consumption (L/100km) |
| Values | 6.0, 6.99, 7.0, 8.0 |
| Default | 6.99 |
De waarde bepaalt het gebruikte brandstofverbruik in liter per 100 kilometer.
9. Total Trip Costs
Doel
Berekent de totale geschatte energiekosten van alle ritten binnen de geselecteerde periode.
Wanneer voor een rit voldoende nauwkeurige batterijgegevens beschikbaar zijn, wordt het werkelijke geschatte energieverbruik van die rit gebruikt.
Wanneer de gegevens onvoldoende nauwkeurig zijn, wordt het energieverbruik geschat op basis van het gemiddelde energieverbruik van de ritten waarvoor wel voldoende nauwkeurige gegevens beschikbaar zijn.
Query
WITH charges AS (
SELECT
COALESCE(
-- 1. €/kWh in de geselecteerde periode
SUM(cost) FILTER (WHERE $__timeFilter(start_date))
/ NULLIF(SUM(charge_energy_added) FILTER (WHERE $__timeFilter(start_date)), 0),
-- 2. fallback: overall €/kWh (alle charges met cost)
SUM(cost) / NULLIF(SUM(charge_energy_added), 0),
-- 3. laatste fallback
0.37
) AS cost_per_kwh
FROM charging_processes
WHERE
car_id = $car_id
AND cost IS NOT NULL
AND cost > 0
),
data AS (
SELECT
drives.distance,
start_${preferred_range}_range_km - end_${preferred_range}_range_km AS range_diff,
car.efficiency AS car_efficiency,
duration_min > 1
AND drives.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 drives.start_position_id = start_position.id
LEFT JOIN positions end_position
ON drives.end_position_id = end_position.id
LEFT JOIN cars car
ON drives.car_id = car.id
WHERE
drives.car_id = $car_id
AND $__timeFilter(drives.start_date)
),
stats AS (
SELECT
SUM(
CASE
WHEN is_sufficiently_precise
THEN range_diff * car_efficiency
END
) AS total_energy,
SUM(
CASE
WHEN is_sufficiently_precise
THEN convert_km(distance::numeric, '$length_unit')
END
) AS total_distance
FROM data
)
SELECT
SUM(
CASE
WHEN d.is_sufficiently_precise THEN
d.range_diff * d.car_efficiency * c.cost_per_kwh
ELSE
convert_km(d.distance::numeric, '$length_unit') / 100.0
* (
s.total_energy
/ NULLIF(s.total_distance, 0)
* 100.0
* c.cost_per_kwh
)
END
) AS "Total Trip Costs"
FROM data d
CROSS JOIN charges c
CROSS JOIN stats s;
10. Savings vs Fuel
Doel
Vergelijkt de geschatte kosten van de Tesla met de geschatte brandstofkosten van een benzineauto.
De berekening is:
Estimated Fuel Costs − Total Trip Costs = Savings vs Fuel
Een positief resultaat betekent dat de berekende brandstofkosten hoger zijn dan de berekende Tesla-kosten.
Query
WITH charges AS (
SELECT
COALESCE(
SUM(cost) FILTER (WHERE $__timeFilter(start_date))
/ NULLIF(SUM(charge_energy_added) FILTER (WHERE $__timeFilter(start_date)), 0),
SUM(cost) / NULLIF(SUM(charge_energy_added), 0),
0.30
) AS cost_per_kwh
FROM charging_processes
WHERE
car_id = $car_id
AND cost IS NOT NULL
AND cost > 0
),
data AS (
SELECT
drives.distance,
start_${preferred_range}_range_km - end_${preferred_range}_range_km AS range_diff,
car.efficiency AS car_efficiency,
duration_min > 1
AND drives.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 drives.start_position_id = start_position.id
LEFT JOIN positions end_position
ON drives.end_position_id = end_position.id
LEFT JOIN cars car
ON drives.car_id = car.id
WHERE
drives.car_id = $car_id
AND $__timeFilter(drives.start_date)
),
stats AS (
SELECT
SUM(
CASE WHEN is_sufficiently_precise
THEN range_diff * car_efficiency
END
) AS total_energy,
SUM(
CASE WHEN is_sufficiently_precise
THEN convert_km(distance::numeric, '$length_unit')
END
) AS total_distance
FROM data
),
trip_costs AS (
SELECT
SUM(
CASE
WHEN d.is_sufficiently_precise THEN
d.range_diff * d.car_efficiency * c.cost_per_kwh
ELSE
convert_km(d.distance::numeric, '$length_unit') / 100.0
* (
s.total_energy
/ NULLIF(s.total_distance, 0)
* 100.0
* c.cost_per_kwh
)
END
) AS total_trip_costs
FROM data d
CROSS JOIN charges c
CROSS JOIN stats s
),
fuel_costs AS (
SELECT
(MAX(odometer) - MIN(odometer)) / 100.0 *
COALESCE(
NULLIF(
REPLACE(
REGEXP_REPLACE('${fuel_consumption:text}', '[^0-9.,]', '', 'g'),
',',
'.'
),
''
)::numeric,
6.99
) *
COALESCE(
NULLIF(
REGEXP_REPLACE('${fuel_price:text}', '[^0-9.]', '', 'g'),
''
)::numeric,
1.83
) AS estimated_fuel_costs
FROM positions
WHERE
car_id = $car_id
AND ideal_battery_range_km IS NOT NULL
AND (
drive_id IN (SELECT id FROM drives WHERE $__timeFilter(start_date))
OR (drive_id IS NULL AND $__timeFilter(date))
)
)
SELECT
COALESCE(f.estimated_fuel_costs, 0) - COALESCE(t.total_trip_costs, 0) AS "Savings vs Fuel"
FROM fuel_costs f
CROSS JOIN trip_costs t;
11. Drives
11.1 Extra kolommen
Aan het Drives-paneel zijn drie extra kolommen toegevoegd:
-
Trip cost
-
Estimated Fuel Costs
-
Savings vs Fuel
Hiermee kan per afzonderlijke rit worden bekeken:
-
wat de geschatte energiekosten van de Tesla waren;
-
wat dezelfde rit met de ingestelde benzineauto zou hebben gekost;
-
het verschil tussen beide.
Query
WITH cost AS (
SELECT
COALESCE(
SUM(cost) FILTER (WHERE $__timeFilter(start_date))
/ NULLIF(SUM(charge_energy_added) FILTER (WHERE $__timeFilter(start_date)), 0),
SUM(cost) / NULLIF(SUM(charge_energy_added), 0),
0.30
) AS cost_per_kwh
FROM charging_processes
WHERE
car_id = $car_id
AND cost IS NOT NULL
AND cost > 0
),
data AS (
SELECT
floor(extract(epoch FROM start_date)) * 1000 AS start_date_ts,
ceil(extract(epoch FROM end_date)) * 1000 AS end_date_ts,
car.id as car_id,
CASE WHEN start_geofence.id IS NULL THEN CONCAT('new?lat=', start_position.latitude, '&lng=', start_position.longitude)
WHEN start_geofence.id IS NOT NULL THEN CONCAT(start_geofence.id, '/edit')
END as start_path,
CASE WHEN end_geofence.id IS NULL THEN CONCAT('new?lat=', end_position.latitude, '&lng=', end_position.longitude)
WHEN end_geofence.id IS NOT NULL THEN CONCAT(end_geofence.id, '/edit')
END as end_path,
TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,
drives.id as drive_id,
start_date,
COALESCE(start_geofence.name, CONCAT_WS(', ', COALESCE(start_address.name, nullif(CONCAT_WS(' ', start_address.road, start_address.house_number), '')), COALESCE(start_address.city, start_address.neighbourhood))) AS start_address,
COALESCE(end_geofence.name, CONCAT_WS(', ', COALESCE(end_address.name, nullif(CONCAT_WS(' ', end_address.road, end_address.house_number), '')), COALESCE(end_address.city, end_address.neighbourhood))) AS end_address,
duration_min,
distance,
start_position.usable_battery_level as start_usable_battery_level,
start_position.battery_level as start_battery_level,
end_position.usable_battery_level as end_usable_battery_level,
end_position.battery_level as end_battery_level,
start_position.battery_level != start_position.usable_battery_level OR end_position.battery_level != end_position.usable_battery_level as reduced_range,
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,
start_${preferred_range}_range_km - end_${preferred_range}_range_km as range_diff,
car.efficiency as car_efficiency,
outside_temp_avg,
distance / NULLIF(duration_min, 0) * 60 AS avg_speed
FROM drives
LEFT JOIN addresses start_address ON start_address_id = start_address.id
LEFT JOIN addresses end_address ON end_address_id = end_address.id
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 geofences start_geofence ON start_geofence_id = start_geofence.id
LEFT JOIN geofences end_geofence ON end_geofence_id = end_geofence.id
LEFT JOIN cars car ON car.id = drives.car_id
WHERE $__timeFilter(start_date) AND drives.car_id = $car_id
ORDER BY start_date DESC
)
SELECT
start_date_ts,
end_date_ts,
car_id,
start_path,
end_path,
duration_str,
drive_id,
start_date,
start_address,
end_address,
duration_min,
convert_km(distance::numeric, '$length_unit') AS distance_$length_unit,
start_battery_level as "% Start",
end_battery_level as "% End",
CASE WHEN is_sufficiently_precise THEN range_diff * car_efficiency / convert_km(distance::numeric, '$length_unit') * 1000
END AS consumption_kWh_$length_unit,
-- Trip cost (energy)
CASE WHEN is_sufficiently_precise THEN
GREATEST(range_diff, 0) * car_efficiency * cost.cost_per_kwh
END AS "Trip cost",
-- Estimated Fuel Costs
distance / 100.0 *
COALESCE(
NULLIF(
REPLACE(
REGEXP_REPLACE('${fuel_consumption:text}', '[^0-9.,]', '', 'g'),
',',
'.'
),
''
)::numeric,
6.99
) *
COALESCE(
NULLIF(
REGEXP_REPLACE('${fuel_price:text}', '[^0-9.]', '', 'g'),
''
)::numeric,
1.87
) AS "Estimated Fuel Costs",
-- Savings vs Fuel
(
distance / 100.0 *
COALESCE(
NULLIF(
REPLACE(
REGEXP_REPLACE('${fuel_consumption:text}', '[^0-9.,]', '', 'g'),
',',
'.'
),
''
)::numeric,
6.99
) *
COALESCE(
NULLIF(
REGEXP_REPLACE('${fuel_price:text}', '[^0-9.]', '', 'g'),
''
)::numeric,
1.87
)
)
-
COALESCE(
CASE WHEN is_sufficiently_precise THEN
GREATEST(range_diff, 0) * car_efficiency * cost.cost_per_kwh
END,
0
) AS "Savings vs Fuel"
FROM data
CROSS JOIN cost;
12. Dashboardvolgorde
De elementen worden in onderstaande volgorde weergegeven.
12.1 Kosten en vergelijking
-
Mileage
-
Estimated Fuel Costs
-
Total Charging Cost
-
Savings vs Fuel
-
Total Trip Costs
12.2 Rijtijd en efficiëntie
-
Time spent (driving + charging)
-
Ø Cost per km
-
Ø Speed excl. breaks
-
Ø Speed incl. DC charging
-
Ø Consumption (net)
-
Ø Consumption (gross)
13. Belangrijke aandachtspunten
13.1 Queries niet los van elkaar aanpassen
Een aantal berekeningen gebruikt dezelfde uitgangspunten, maar is niet noodzakelijk letterlijk dezelfde query.
Vooral bij:
-
Ø Cost per km -
Total Trip Costs -
Savings vs Fuel -
Drives
moet bij een wijziging worden gecontroleerd of dezelfde berekeningslogica ook elders moet worden aangepast.
13.2 Laadkosten
Wanneer binnen de geselecteerde periode geen bruikbare laadprijs beschikbaar is, gebruiken verschillende berekeningen een fallback:
€0,38/kWh
Dit voorkomt dat panelen leeg blijven wanneer er geen laadsessie met kosten beschikbaar is.
13.3 Brandstofvergelijking
De brandstofvergelijking is een schatting.
De uitkomst is afhankelijk van:
-
ingesteld brandstofverbruik;
-
ingestelde benzineprijs;
-
gereden afstand;
-
beschikbare TeslaMate-data;
-
gebruikte laadprijs.
De waarde moet daarom worden gezien als een vergelijkingswaarde en niet als een exact bedrag dat daadwerkelijk met een benzineauto zou zijn uitgegeven.
13.4 Eenheden
De dashboards maken gebruik van TeslaMate/Grafana-variabelen zoals:
-
$length_unit -
$temp_unit -
$preferred_range -
$car_id -
$geofence -
$min_duration -
$min_distance
Wanneer deze variabelen of de onderliggende TeslaMate-configuratie veranderen, kunnen meerdere panelen hierdoor worden beïnvloed.
14. Wijzigingslog
Gebruik deze sectie om toekomstige wijzigingen aan het dashboard bij te houden.
| Datum | Wijziging | Opmerking |
|---|---|---|
| 2026-10-03 | Documentatie opgeschoond | Structuur, spelling en toelichtingen bijgewerkt |
15. Snelle referentie
| Paneel | Functie |
|---|---|
| Estimated Fuel Costs | Geschatte benzinekosten voor dezelfde afstand |
| Total Range Added | Toegevoegde actieradius tijdens laden |
| Ø Cost per km | Gemiddelde laad-/energiekosten per km |
| Top Charging Stations | Laadlocaties met laagste gewogen gemiddelde €/kWh |
| Temperature – Driving Efficiency | Efficiëntie uitgesplitst naar buitentemperatuur |
| Statistics | Statistieken per periode, inclusief berekende fallbackwaarden |
| Ø Cost per km (Trip) | Kosten per kilometer voor een trip/periode |
| Total Trip Costs | Totale geschatte energiekosten van de ritten |
| Savings vs Fuel | Geschat verschil tussen benzinekosten en Tesla-kosten |
| Drives – Trip cost | Geschatte energiekosten per individuele rit |
| Drives – Estimated Fuel Costs | Geschatte benzinekosten per individuele rit |
| Drives – Savings vs Fuel | Geschat verschil per individuele rit |

No comments to display
No comments to display