Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Battery health derived efficiency estimations and Fix error divided by zero #3593

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The documentation is available at [docs.teslamate.org](https://docs.teslamate.or
- [See when your car was online or asleep](https://docs.teslamate.org/docs/screenshots#states)
- [Lifetime driving map](https://docs.teslamate.org/docs/screenshots/#lifetime-driving-map)
- [Visited addresses](https://docs.teslamate.org/docs/screenshots/#visited-addresses)
- [Battery Health](https://docs.teslamate.org/docs/screenshots/#battery-health)

**General**

Expand Down
125 changes: 90 additions & 35 deletions grafana/dashboards/battery-health.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"__elements": [],
"__elements": {},
"__requires": [
{
"type": "panel",
Expand All @@ -17,7 +17,7 @@
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "8.5.26"
"version": "10.1.2"
},
{
"type": "panel",
Expand All @@ -36,6 +36,12 @@
"id": "stat",
"name": "Stat",
"version": ""
},
{
"type": "panel",
"id": "xychart",
"name": "XY Chart",
"version": ""
}
],
"annotations": {
Expand Down Expand Up @@ -141,7 +147,7 @@
},
"textMode": "value_and_name"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -286,7 +292,7 @@
},
"textMode": "value_and_name"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -369,7 +375,7 @@
},
"textMode": "value_and_name"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -503,7 +509,7 @@
},
"textMode": "value_and_name"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -631,7 +637,7 @@
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -724,9 +730,10 @@
"fields": "",
"values": false
},
"showUnfilled": true
"showUnfilled": true,
"valueMode": "color"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -809,7 +816,7 @@
},
"textMode": "value_and_name"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -917,8 +924,9 @@
"value"
],
"legend": {
"displayMode": "hidden",
"placement": "bottom"
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"pieType": "pie",
"reduceOptions": {
Expand Down Expand Up @@ -1030,9 +1038,10 @@
"values": false
},
"showUnfilled": true,
"text": {}
"text": {},
"valueMode": "color"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -1147,9 +1156,10 @@
"values": false
},
"showUnfilled": true,
"text": {}
"text": {},
"valueMode": "color"
},
"pluginVersion": "8.5.26",
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
Expand Down Expand Up @@ -1206,14 +1216,16 @@
"type": "bargauge"
},
{
"datasource": "TeslaMate",
"datasource": {
"type": "postgres",
"uid": "TeslaMate"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
"mode": "continuous-RdYlGr"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
Expand Down Expand Up @@ -1260,12 +1272,6 @@
{
"id": "custom.show",
"value": "lines"
},
{
"id": "custom.lineStyle",
"value": {
"fill": "solid"
}
}
]
}
Expand All @@ -1288,13 +1294,20 @@
},
"series": [
{
"name": "Odometer",
"pointColor": {
"field": "kWh"
},
"pointSize": {
"fixed": 5,
"max": 100,
"min": 1
},
"x": "odometer",
"y": "kWh"
},
{
"name": "Median Odometer",
"pointColor": {
"fixed": "dark-red"
},
Expand All @@ -1304,21 +1317,25 @@
],
"seriesMapping": "manual",
"tooltip": {
"mode": "multi",
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.5.6",
"targets": [
{
"alias": "",
"datasource": "TeslaMate",
"datasource": {
"type": "postgres",
"uid": "TeslaMate"
},
"editorMode": "code",
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT convert_km(AVG(p.odometer)::numeric,'$length_unit') AS odometer, \r\n\tAVG(c.rated_battery_range_km) * ('$aux'::json -> 'RatedEfficiency')::text::float / AVG(c.usable_battery_level) AS \"kWh\",\r\n\tMAX(cp.id) AS id,\r\n\tto_char(cp.end_date, 'YYYY-MM-dd') AS Title\r\n\tFROM charging_processes cp\r\n\t\tJOIN (SELECT charging_process_id, MAX(date) as date\tFROM charges GROUP BY charging_process_id) AS last_charges\tON cp.id = last_charges.charging_process_id\r\n\t\tINNER JOIN charges c\r\n\t\tON c.charging_process_id = cp.id AND c.date = last_charges.date\r\n\t\tINNER JOIN positions p ON p.id = cp.position_id\r\n\tWHERE cp.car_id = $car_id\r\n\t\tAND cp.end_date IS NOT NULL\r\n\t\tAND cp.end_rated_range_km > cp.start_rated_range_km\r\n\tGROUP BY 4",
"rawSql": "SELECT convert_km(AVG(p.odometer)::numeric,'$length_unit') AS odometer, \r\n\tAVG(c.rated_battery_range_km * ('$aux'::json -> 'RatedEfficiency')::text::float / c.usable_battery_level) AS \"kWh\",\r\n\tMAX(cp.id) AS id,\r\n\tto_char(cp.end_date, 'YYYY-MM-dd') AS Title\r\n\tFROM charging_processes cp\r\n\t\tJOIN (SELECT charging_process_id, MAX(date) as date\tFROM charges WHERE usable_battery_level > 0 GROUP BY charging_process_id) AS last_charges\tON cp.id = last_charges.charging_process_id\r\n\t\tINNER JOIN charges c\r\n\t\tON c.charging_process_id = cp.id AND c.date = last_charges.date\r\n\t\tINNER JOIN positions p ON p.id = cp.position_id\r\n\tWHERE cp.car_id = $car_id\r\n\t\tAND cp.end_date IS NOT NULL\r\n\t\tAND cp.charge_energy_added >= ('$aux'::json -> 'RatedEfficiency')::text::float\r\n\tGROUP BY 4",
"refId": "Projected Range",
"select": [
[
Expand All @@ -1330,6 +1347,23 @@
}
]
],
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"timeColumn": "time",
"where": [
{
Expand All @@ -1341,13 +1375,17 @@
},
{
"alias": "",
"datasource": "TeslaMate",
"datasource": {
"type": "postgres",
"uid": "TeslaMate"
},
"editorMode": "code",
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT \n ROUND(MIN(convert_km(p.odometer::numeric,'$length_unit')),0) AS \"M-Odometer\",\n\tROUND(PERCENTILE_CONT(0.5) WITHIN GROUP(ORDER BY c.rated_battery_range_km * ('$aux'::json -> 'RatedEfficiency')::text::float / c.usable_battery_level)::numeric,1) AS \"M-kWh\",\n\tto_char(cp.end_date, 'YYYYMM') || CASE WHEN to_char(cp.end_date, 'DD')::int <= 15 THEN '1' ELSE '2' END AS Title\n\tFROM charging_processes cp\n\t\tJOIN (SELECT charging_process_id, MAX(date) as date\tFROM charges GROUP BY charging_process_id) AS last_charges\tON cp.id = last_charges.charging_process_id\n\t\tINNER JOIN charges c\n\t\tON c.charging_process_id = cp.id AND c.date = last_charges.date\n\t\tINNER JOIN positions p ON p.id = cp.position_id\n\tWHERE cp.car_id = $car_id\n\t\tAND cp.end_date IS NOT NULL\n\t\tAND cp.end_rated_range_km > cp.start_rated_range_km\n\tGROUP BY 3",
"rawSql": "SELECT \n ROUND(MIN(convert_km(p.odometer::numeric,'$length_unit')),0) AS \"M-Odometer\",\n\tROUND(PERCENTILE_CONT(0.5) WITHIN GROUP(ORDER BY c.rated_battery_range_km * ('$aux'::json -> 'RatedEfficiency')::text::float / c.usable_battery_level)::numeric,1) AS \"M-kWh\",\n\tto_char(cp.end_date, 'YYYYMM') || CASE WHEN to_char(cp.end_date, 'DD')::int <= 15 THEN '1' ELSE '2' END AS Title\n\tFROM charging_processes cp\n\t\tJOIN (SELECT charging_process_id, MAX(date) as date\tFROM charges WHERE usable_battery_level > 0 GROUP BY charging_process_id) AS last_charges\tON cp.id = last_charges.charging_process_id\n\t\tINNER JOIN charges c\n\t\tON c.charging_process_id = cp.id AND c.date = last_charges.date\n\t\tINNER JOIN positions p ON p.id = cp.position_id\n\tWHERE cp.car_id = $car_id\n\t\tAND cp.end_date IS NOT NULL\n\t\tAND cp.charge_energy_added >= ('$aux'::json -> 'RatedEfficiency')::text::float\n\tGROUP BY 3",
"refId": "Median",
"select": [
[
Expand All @@ -1359,6 +1397,23 @@
}
]
],
"sql": {
"columns": [
{
"parameters": [],
"type": "function"
}
],
"groupBy": [
{
"property": {
"type": "string"
},
"type": "groupBy"
}
],
"limit": 50
},
"table": "drives",
"timeColumn": "start_date",
"timeColumnType": "timestamp",
Expand All @@ -1376,7 +1431,7 @@
}
],
"refresh": "",
"schemaVersion": 36,
"schemaVersion": 38,
"style": "dark",
"tags": [
"tesla"
Expand Down Expand Up @@ -1458,13 +1513,13 @@
"type": "postgres",
"uid": "TeslaMate"
},
"definition": "-- CONCATENATED JOIN QUERIES TO IMPROVE PERFORMANCE\n-- The following query is the result of many tests and hours of work. This panel is for your own personal use. \n-- If you think you can improve it and contribute, please create a pull request and do not take it to your repository, \n-- much less upload it to another repository as if the original idea were yours, nor do you share it on social media\n-- without mentioning the author. Respect the ingenuity and work of others. Cheers!\n-- 2023-07-21\n-- By @jheredianet - Twitter: @juanheredia",
"definition": "-- CONCATENATED JOIN QUERIES TO IMPROVE PERFORMANCE",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "aux",
"options": [],
"query": "WITH\naux\tAS\n(\n\tSELECT cp.charge_energy_added,\n\t\tcp.car_id, (SELECT efficiency FROM cars WHERE id = $car_id) * 100.0 AS rated_efficiency,\n\t\t(cp.end_rated_range_km - cp.start_rated_range_km) AS added_range_km\n\tFROM charging_processes cp\n\t\tJOIN (SELECT charging_process_id, MAX(date) as date\n\t\tFROM charges\n\t\tGROUP BY charging_process_id) AS last_charges\n\t\tON cp.id = last_charges.charging_process_id\n\t\tINNER JOIN charges c\n\t\tON c.charging_process_id = cp.id AND c.date = last_charges.date\n\tWHERE cp.car_id = $car_id\n\t\tAND cp.end_date IS NOT NULL\n\t\tAND cp.end_rated_range_km > cp.start_rated_range_km\n\tORDER BY cp.end_date DESC \n\tLIMIT 1\n), \nCurrentCapacity\t AS\n(\n\tSELECT AVG(Capacity) AS CurrentCapacity FROM\n (SELECT (100.0 * cp.charge_energy_added) / (GREATEST(1,MAX(usable_battery_level) - MIN(usable_battery_level))) AS Capacity\t\n FROM charging_processes cp\n\t INNER JOIN charges c\tON cp.id = c.charging_process_id\n INNER JOIN aux ON cp.car_id = aux.car_id\n\t WHERE cp.car_id = $car_id AND cp.charge_energy_added >= aux.rated_efficiency \n GROUP BY cp.charge_energy_added, cp.end_date\n ORDER BY cp.end_date DESC \n LIMIT 5\n ) AS lastEstimatedCapacity\n), \nMaxCapacity AS\n(\n SELECT AVG(c.rated_battery_range_km * aux.rated_efficiency / c.usable_battery_level) AS MaxCapacity, cp.id\n FROM charging_processes cp\n\tJOIN (SELECT charging_process_id, MAX(date) as date FROM charges \n WHERE usable_battery_level > 0 GROUP BY charging_process_id) AS last_charges\t\n ON cp.id = last_charges.charging_process_id\n\tINNER JOIN charges c ON c.charging_process_id = cp.id AND c.date = last_charges.date\n\tINNER JOIN positions p ON p.id = cp.position_id\n INNER JOIN aux ON cp.car_id = aux.car_id\n\tWHERE cp.car_id = $car_id\n\t\tAND cp.end_date IS NOT NULL\n\t\tAND cp.charge_energy_added >= aux.rated_efficiency \n\tGROUP BY cp.id \n ORDER BY 1 DESC \n LIMIT 1\n), \nCurrentRange AS\n(\n SELECT\n\t\tfloor(extract(epoch from date)/86400)*86400 AS timecurrent,\n\t\tsum(rated_battery_range_km) / sum(usable_battery_level) * 100 AS rated_range_km\n\tFROM (\n\t\tSELECT battery_level, usable_battery_level, date, rated_battery_range_km from charges c \n\t\tJOIN charging_processes p ON p.id = c.charging_process_id \n\t\tWHERE p.car_id = $car_id AND usable_battery_level IS NOT NULL) AS data\n\tGROUP BY 1\n\tORDER BY 1 DESC\n\tLIMIT 1\n), \nMaxRange AS\n(\n SELECT\n\t\tfloor(extract(epoch from date)/86400)*86400 AS time,\n\t\tsum(rated_battery_range_km) / sum(usable_battery_level) * 100 AS max_rated_range_km\n\tFROM (\n\t\tSELECT battery_level, usable_battery_level, date, rated_battery_range_km from charges c \n\t\tJOIN charging_processes p ON p.id = c.charging_process_id \n\t\tWHERE p.car_id = $car_id AND usable_battery_level IS NOT NULL) AS data\n\tGROUP BY 1\n\tORDER BY 2 DESC\n\tLIMIT 1\n) \nSELECT CONCAT('{\"LastChargekWhAdded\": ', aux.charge_energy_added, \n ', \"LastMileageAdded\" : ', convert_km(aux.added_range_km,'$length_unit'),\n ', \"MaxRange\": ', convert_km(MaxRange.max_rated_range_km,'$length_unit'),\n ', \"CurrentRange\": ',convert_km(CurrentRange.rated_range_km,'$length_unit'),\n ', \"MaxCapacity\": ', MaxCapacity.MaxCapacity,\n ', \"CurrentCapacity\": ', CASE WHEN CurrentCapacity.CurrentCapacity IS NULL THEN 1 ELSE CurrentCapacity.CurrentCapacity END,\n ', \"RatedEfficiency\": ',aux.rated_efficiency, '}') \nFROM MaxRange, CurrentRange, Aux, MaxCapacity, CurrentCapacity;",
"query": "WITH DerivatedEfficiency AS (\n SELECT (charge_energy_added / NULLIF(end_rated_range_km - start_rated_range_km, 0))::numeric * 100.0 as \"efficiency\",\n count(*) as count FROM charging_processes \n WHERE car_id = $car_id \n AND duration_min > 10 \n AND end_battery_level <= 95 \n AND start_rated_range_km IS NOT NULL \n AND end_rated_range_km IS NOT NULL \n AND charge_energy_added > 0\n GROUP BY 1\n ORDER BY 2 DESC\n LIMIT 1\n),\naux\tAS\n(\n\tSELECT cp.charge_energy_added,\n\t\tcp.car_id, (SELECT efficiency FROM DerivatedEfficiency) AS rated_efficiency,\n\t\t(cp.end_rated_range_km - cp.start_rated_range_km) AS added_range_km\n\tFROM charging_processes cp\n\t\tJOIN (SELECT charging_process_id, MAX(date) as date\n\t\tFROM charges\n\t\tGROUP BY charging_process_id) AS last_charges\n\t\tON cp.id = last_charges.charging_process_id\n\t\tINNER JOIN charges c\n\t\tON c.charging_process_id = cp.id AND c.date = last_charges.date\n\tWHERE cp.car_id = $car_id\n\t\tAND cp.end_date IS NOT NULL\n\t\tAND cp.end_rated_range_km > cp.start_rated_range_km\n\tORDER BY cp.end_date DESC \n\tLIMIT 1\n), \nCurrentCapacity\t AS\n(\n\tSELECT AVG(Capacity) AS CurrentCapacity FROM\n (SELECT (100.0 * cp.charge_energy_added) / (GREATEST(1,MAX(usable_battery_level) - MIN(usable_battery_level))) AS Capacity\t\n FROM charging_processes cp\n\t INNER JOIN charges c\tON cp.id = c.charging_process_id\n INNER JOIN aux ON cp.car_id = aux.car_id\n\t WHERE cp.car_id = $car_id AND cp.charge_energy_added >= aux.rated_efficiency \n GROUP BY cp.charge_energy_added, cp.end_date\n ORDER BY cp.end_date DESC \n LIMIT 5\n ) AS lastEstimatedCapacity\n), \nMaxCapacity AS\n(\n SELECT AVG(c.rated_battery_range_km * aux.rated_efficiency / c.usable_battery_level) AS MaxCapacity, cp.id\n FROM charging_processes cp\n\tJOIN (SELECT charging_process_id, MAX(date) as date FROM charges \n WHERE usable_battery_level > 0 GROUP BY charging_process_id) AS last_charges\t\n ON cp.id = last_charges.charging_process_id\n\tINNER JOIN charges c ON c.charging_process_id = cp.id AND c.date = last_charges.date\n\tINNER JOIN positions p ON p.id = cp.position_id\n INNER JOIN aux ON cp.car_id = aux.car_id\n\tWHERE cp.car_id = $car_id\n\t\tAND cp.end_date IS NOT NULL\n\t\tAND cp.charge_energy_added >= aux.rated_efficiency \n\tGROUP BY cp.id \n ORDER BY 1 DESC \n LIMIT 1\n), \nCurrentRange AS\n(\n SELECT\n\t\tfloor(extract(epoch from date)/86400)*86400 AS timecurrent,\n\t\tCASE WHEN sum(usable_battery_level) = 0 THEN sum(rated_battery_range_km) * 100\n\t\t ELSE sum(rated_battery_range_km) / sum(usable_battery_level) * 100\n END AS rated_range_km\n\tFROM (\n\t\tSELECT battery_level, usable_battery_level, date, rated_battery_range_km from charges c \n\t\tJOIN charging_processes p ON p.id = c.charging_process_id \n\t\tWHERE p.car_id = $car_id AND usable_battery_level IS NOT NULL) AS data\n\tGROUP BY 1\n\tORDER BY 1 DESC\n\tLIMIT 1\n), \nMaxRange AS\n(\n SELECT\n\t\tfloor(extract(epoch from date)/86400)*86400 AS time,\n\t CASE WHEN sum(usable_battery_level) = 0 THEN sum(rated_battery_range_km) * 100\n\t\t ELSE sum(rated_battery_range_km) / sum(usable_battery_level) * 100\n\t END AS max_rated_range_km\n FROM (\n\t\tSELECT battery_level, usable_battery_level, date, rated_battery_range_km from charges c \n\t\tJOIN charging_processes p ON p.id = c.charging_process_id \n\t\tWHERE p.car_id = $car_id AND usable_battery_level IS NOT NULL) AS data\n\tGROUP BY 1\n\tORDER BY 2 DESC\n\tLIMIT 1\n) \nSELECT CONCAT('{\"LastChargekWhAdded\": ', aux.charge_energy_added, \n ', \"LastMileageAdded\" : ', convert_km(aux.added_range_km,'$length_unit'),\n ', \"MaxRange\": ', convert_km(MaxRange.max_rated_range_km,'$length_unit'),\n ', \"CurrentRange\": ',convert_km(CurrentRange.rated_range_km,'$length_unit'),\n ', \"MaxCapacity\": ', MaxCapacity.MaxCapacity,\n ', \"CurrentCapacity\": ', CASE WHEN CurrentCapacity.CurrentCapacity IS NULL THEN 1 ELSE CurrentCapacity.CurrentCapacity END,\n ', \"RatedEfficiency\": ',aux.rated_efficiency, '}') \nFROM MaxRange, CurrentRange, Aux, MaxCapacity, CurrentCapacity;",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
Expand Down Expand Up @@ -1523,6 +1578,6 @@
"timezone": "browser",
"title": "Battery Health",
"uid": "jchmRiqUfXgTM",
"version": 10,
"version": 12,
"weekStart": ""
}
}
6 changes: 5 additions & 1 deletion website/docs/screenshots.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ import useBaseUrl from '@docusaurus/useBaseUrl';

## Visited addresses

<img alt="Visited addresses" src={useBaseUrl('screenshots/locations.png')} />
<img alt="Visited addresses" src={useBaseUrl('screenshots/locations.png')} />

## Battery Health

<img alt="Visited addresses" src={useBaseUrl('screenshots/battery-health.png')} />
Binary file added website/static/screenshots/battery-health.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading