feat: optimize reduced_range query and always show consumption kwh in drives dashboard (#5089)

* optimize reduced_range query and always show consumption kwh

* docs: update changelog

---------

Co-authored-by: Jakob Lichterfeld <jakob-lichterfeld@gmx.de>
This commit is contained in:
Matthias Wirtz
2026-01-08 14:41:32 +01:00
committed by GitHub
parent 0bf84dfc2b
commit 2ec0d930d5
2 changed files with 2 additions and 13 deletions

View File

@@ -29,6 +29,7 @@
#### Dashboards
- feat(overview): show battery heating on overview dashboard as well (#5090 - @kaistian)
- feat: optimize reduced_range query and always show consumption kwh in drives dashboard (#5089 - @swiffer)
#### Translations

View File

@@ -944,18 +944,6 @@
}
]
},
{
"matcher": {
"id": "byName",
"options": "id"
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
@@ -1289,7 +1277,7 @@
"editorMode": "code",
"format": "table",
"rawQuery": true,
"rawSql": "WITH data AS (\n SELECT\n round(extract(epoch FROM start_date)) * 1000 AS start_date_ts,\n round(extract(epoch FROM end_date)) * 1000 AS end_date_ts,\n car.id as car_id,\n CASE\n WHEN start_geofence.id IS NULL THEN CONCAT('new?lat=', start_position.latitude, '&lng=', start_position.longitude)\n WHEN start_geofence.id IS NOT NULL THEN CONCAT(start_geofence.id, '/edit')\n END as start_path,\n CASE\n WHEN end_geofence.id IS NULL THEN CONCAT('new?lat=', end_position.latitude, '&lng=', end_position.longitude)\n WHEN end_geofence.id IS NOT NULL THEN CONCAT(end_geofence.id, '/edit')\n END as end_path,\n TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,\n drives.id as drive_id,\n -- Columns\n start_date,\n COALESCE(start_geofence.name, CONCAT_WS(', ', COALESCE(start_address.name, nullif(CONCAT_WS(' ', start_address.road, start_address.house_number), '')), start_address.city)) AS start_address,\n COALESCE(end_geofence.name, CONCAT_WS(', ', COALESCE(end_address.name, nullif(CONCAT_WS(' ', end_address.road, end_address.house_number), '')), end_address.city)) AS end_address,\n duration_min,\n distance,\n start_position.usable_battery_level as start_usable_battery_level,\n start_position.battery_level as start_battery_level,\n end_position.usable_battery_level as end_usable_battery_level,\n end_position.battery_level as end_battery_level,\n case when (start_position.battery_level != start_position.usable_battery_level OR end_position.battery_level != end_position.usable_battery_level) = true then true else false end as reduced_range,\n duration_min > 1 AND distance > 1 AND ( \n start_position.usable_battery_level IS NULL OR end_position.usable_battery_level IS NULL\tOR\n (end_position.battery_level - end_position.usable_battery_level) = 0 \n ) as is_sufficiently_precise,\n start_${preferred_range}_range_km - end_${preferred_range}_range_km as range_diff,\n car.efficiency as car_efficiency,\n outside_temp_avg,\n distance / coalesce(NULLIF(duration_min, 0) * 60, extract(epoch from end_date - start_date)) * 3600 AS avg_speed,\n speed_max,\n power_max,\n ascent,\n descent\n FROM drives\n LEFT JOIN addresses start_address ON start_address_id = start_address.id\n LEFT JOIN addresses end_address ON end_address_id = end_address.id\n LEFT JOIN positions start_position ON start_position_id = start_position.id\n LEFT JOIN positions end_position ON end_position_id = end_position.id\n LEFT JOIN geofences start_geofence ON start_geofence_id = start_geofence.id\n LEFT JOIN geofences end_geofence ON end_geofence_id = end_geofence.id\n LEFT JOIN cars car ON car.id = drives.car_id\n WHERE $__timeFilter(start_date) AND drives.car_id = $car_id \n AND convert_km(distance::numeric, '$length_unit') >= $min_dist \n AND convert_km(distance::numeric, '$length_unit') / coalesce(NULLIF(duration_min, 0) * 60, extract(epoch from end_date - start_date)) * 3600 >= $min_speed \n AND ('${geofence:pipe}' = '-1' OR start_geofence.id in ($geofence) OR end_geofence.id in ($geofence)) \n ORDER BY start_date DESC\n)\nSELECT\n start_date_ts,\n end_date_ts,\n car_id,\n start_path,\n end_path,\n duration_str,\n drive_id,\n -- Columns\n start_date,\n start_address,\n end_address,\n duration_min,\n convert_km(distance::numeric, '$length_unit') AS distance_$length_unit,\n start_battery_level as \"% Start\",\n end_battery_level as \"% End\",\n convert_celsius(outside_temp_avg, '$temp_unit') AS outside_temp_$temp_unit,\n convert_km(avg_speed::numeric, '$length_unit') AS speed_avg_$length_unit,\n convert_km(speed_max::numeric, '$length_unit') AS speed_max_$length_unit,\n power_max,\n reduced_range as has_reduced_range,\n CASE\n WHEN is_sufficiently_precise and range_diff > 0 and 'by distance' = '$efficiency' THEN distance / range_diff\n WHEN is_sufficiently_precise and 'slope-adjusted' = '$efficiency' THEN\n distance * car_efficiency -- Energy at 100% efficiency\n / nullif((\n (range_diff) * car_efficiency -- Actual Energy\n + 2100 * 0.85 * 9.81 * descent / 3600 / 1000 -- Potential energy recovered from descent\n - 2100 * 9.81 * ascent / 3600 / 1000 -- Potential energy for ascent\n ), 0)\n ELSE NULL\n END as efficiency,\n range_diff * car_efficiency as \"consumption_kWh\",\n CASE WHEN is_sufficiently_precise THEN range_diff * car_efficiency / convert_km(distance::numeric, '$length_unit') * 1000\n END AS consumption_kWh_$length_unit\nFROM data\nWHERE\n start_address ILIKE '%$location%' OR end_address ILIKE '%$location%';",
"rawSql": "WITH data AS (\n SELECT\n round(extract(epoch FROM start_date)) * 1000 AS start_date_ts,\n round(extract(epoch FROM end_date)) * 1000 AS end_date_ts,\n car.id as car_id,\n CASE\n WHEN start_geofence.id IS NULL THEN CONCAT('new?lat=', start_position.latitude, '&lng=', start_position.longitude)\n WHEN start_geofence.id IS NOT NULL THEN CONCAT(start_geofence.id, '/edit')\n END as start_path,\n CASE\n WHEN end_geofence.id IS NULL THEN CONCAT('new?lat=', end_position.latitude, '&lng=', end_position.longitude)\n WHEN end_geofence.id IS NOT NULL THEN CONCAT(end_geofence.id, '/edit')\n END as end_path,\n TO_CHAR((duration_min * INTERVAL '1 minute'), 'HH24:MI') as duration_str,\n drives.id as drive_id,\n -- Columns\n start_date,\n COALESCE(start_geofence.name, CONCAT_WS(', ', COALESCE(start_address.name, nullif(CONCAT_WS(' ', start_address.road, start_address.house_number), '')), start_address.city)) AS start_address,\n COALESCE(end_geofence.name, CONCAT_WS(', ', COALESCE(end_address.name, nullif(CONCAT_WS(' ', end_address.road, end_address.house_number), '')), end_address.city)) AS end_address,\n duration_min,\n distance,\n start_position.battery_level as start_battery_level,\n end_position.battery_level as end_battery_level,\n start_${preferred_range}_range_km - end_${preferred_range}_range_km as range_diff,\n car.efficiency as car_efficiency,\n outside_temp_avg,\n distance / coalesce(NULLIF(duration_min, 0) * 60, extract(epoch from end_date - start_date)) * 3600 AS avg_speed,\n speed_max,\n power_max,\n ascent,\n descent\n FROM drives\n LEFT JOIN addresses start_address ON start_address_id = start_address.id\n LEFT JOIN addresses end_address ON end_address_id = end_address.id\n LEFT JOIN positions start_position ON start_position_id = start_position.id\n LEFT JOIN positions end_position ON end_position_id = end_position.id\n LEFT JOIN geofences start_geofence ON start_geofence_id = start_geofence.id\n LEFT JOIN geofences end_geofence ON end_geofence_id = end_geofence.id\n LEFT JOIN cars car ON car.id = drives.car_id\n WHERE $__timeFilter(start_date) AND drives.car_id = $car_id \n AND convert_km(distance::numeric, '$length_unit') >= $min_dist \n AND convert_km(distance::numeric, '$length_unit') / coalesce(NULLIF(duration_min, 0) * 60, extract(epoch from end_date - start_date)) * 3600 >= $min_speed \n AND ('${geofence:pipe}' = '-1' OR start_geofence.id in ($geofence) OR end_geofence.id in ($geofence)) \n),\n\nreduced_range_info as (\n\n select\n drive_id,\n case\n when sum(case when battery_level - usable_battery_level > 0 then 1 else 0 end)::numeric / count(*) > 0.25 then true\n else false\n end as reduced_range\n from positions p where $__timeFilter(date) AND car_id = $car_id and p.ideal_battery_range_km is not null group by p.drive_id \n\n)\n\nSELECT\n start_date_ts,\n end_date_ts,\n car_id,\n start_path,\n end_path,\n duration_str,\n data.drive_id,\n -- Columns\n start_date,\n start_address,\n end_address,\n duration_min,\n convert_km(distance::numeric, '$length_unit') AS distance_$length_unit,\n start_battery_level as \"% Start\",\n end_battery_level as \"% End\",\n convert_celsius(outside_temp_avg, '$temp_unit') AS outside_temp_$temp_unit,\n convert_km(avg_speed::numeric, '$length_unit') AS speed_avg_$length_unit,\n convert_km(speed_max::numeric, '$length_unit') AS speed_max_$length_unit,\n power_max,\n reduced_range as has_reduced_range,\n CASE\n WHEN range_diff > 0 and 'by distance' = '$efficiency' THEN distance / range_diff\n WHEN 'slope-adjusted' = '$efficiency' THEN\n distance * car_efficiency -- Energy at 100% efficiency\n / nullif((\n (range_diff) * car_efficiency -- Actual Energy\n + 2100 * 0.85 * 9.81 * descent / 3600 / 1000 -- Potential energy recovered from descent\n - 2100 * 9.81 * ascent / 3600 / 1000 -- Potential energy for ascent\n ), 0)\n ELSE NULL\n END as efficiency,\n range_diff * car_efficiency as \"consumption_kWh\",\n range_diff * car_efficiency / convert_km(distance::numeric, '$length_unit') * 1000 as consumption_kWh_$length_unit\nFROM data\n left join reduced_range_info on data.drive_id = reduced_range_info.drive_id\nWHERE\n start_address ILIKE '%$location%' OR end_address ILIKE '%$location%'\nORDER BY data.drive_id DESC;",
"refId": "A",
"sql": {
"columns": [