Live dashboard: show what the car sends, compute pack energy
The car publishes no battery, cabin or outside temperature and no pack
energy (checked against the fields that reached InfluxDB). Instead of
panels that stay empty:
- Battery temperature tile -> Climate setpoint.
- Outside temperature tile -> Last update ("2 minutes ago").
- Energy in pack = SOC x usable capacity, from the hidden dashboard
constant usableKwh = 50.8 (51 kWh battery).
- Temperatures chart -> Battery voltage and current.
Checked with a simulated drive carrying exactly the car's fields: all
11 panels return data, energy matches 61 % x 50.8 kWh. SPECS.md lists
the missing signals and why.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
@@ -44,6 +44,22 @@ car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP
|
|||||||
- [ ] Check Grafana's host in `k8s/ingress.yaml` (placeholder `car-grafana.k8s.stinnesbeck.com`).
|
- [ ] Check Grafana's host in `k8s/ingress.yaml` (placeholder `car-grafana.k8s.stinnesbeck.com`).
|
||||||
- [ ] First pairing and drive with 2.4.0: telemetry arrives in InfluxDB, Grafana and ABRP.
|
- [ ] First pairing and drive with 2.4.0: telemetry arrives in InfluxDB, Grafana and ABRP.
|
||||||
|
|
||||||
|
### Signals the car does not provide
|
||||||
|
|
||||||
|
Checked 2026-10-17 against what actually reached InfluxDB (`schema.measurementFieldKeys`). The car
|
||||||
|
sends soc, est_battery_range, odometer, power, voltage, current, speed, hvac_setpoint, the four
|
||||||
|
tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does **not** send:
|
||||||
|
|
||||||
|
- **Battery temperature:** read from `EV_BATTERY_AVERAGE_TEMPERATURE`, an Android 14 property; the
|
||||||
|
head unit runs Android 9, and the SAIC services offer none.
|
||||||
|
- **Cabin temperature:** `HVAC_TEMPERATURE_CURRENT` is not published; only the setpoint is.
|
||||||
|
- **Outside temperature:** not published by the vehicle (upstream noted the same). Upstream used
|
||||||
|
the head unit's weather service as a stand-in (`HeadUnitWeather`, removed in `ea8032a`);
|
||||||
|
restoring it was considered and declined.
|
||||||
|
- **Pack energy (`soe`) and capacity:** not published. The Live dashboard computes energy in pack
|
||||||
|
as SOC × usable capacity, from the hidden dashboard constant `usableKwh` (50.8 kWh for the
|
||||||
|
51 kWh battery) in `k8s/grafana/dashboards/live.json`.
|
||||||
|
|
||||||
## To do
|
## To do
|
||||||
|
|
||||||
- [ ] **About dialog link and QR code → https://git.stinnesbeck.com/nils/mg4.**
|
- [ ] **About dialog link and QR code → https://git.stinnesbeck.com/nils/mg4.**
|
||||||
@@ -98,6 +114,9 @@ car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP
|
|||||||
INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in
|
INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in
|
||||||
the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside
|
the now gitignored `k8s/secrets.yaml` (since moved to `k8s/secrets/secrets.yaml`, outside
|
||||||
`kubectl apply -f k8s/`). Filled-in secrets live only there from now on.
|
`kubectl apply -f k8s/`). Filled-in secrets live only there from now on.
|
||||||
|
- 2026-10-17 — Live dashboard reworked around the signals the car sends: Climate setpoint, Last
|
||||||
|
update and a computed Energy in pack replace the empty temperature tiles; Battery voltage and
|
||||||
|
current replaces the Temperatures chart.
|
||||||
- 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb;
|
- 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb;
|
||||||
removed again (1002962) once values showed on the car. Revert 1002962 to bring them back.
|
removed again (1002962) once values showed on the car. Revert 1002962 to bring them back.
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,14 @@
|
|||||||
"current": {},
|
"current": {},
|
||||||
"hide": 0,
|
"hide": 0,
|
||||||
"sort": 1
|
"sort": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "usableKwh",
|
||||||
|
"label": "Usable battery capacity (kWh)",
|
||||||
|
"type": "constant",
|
||||||
|
"query": "50.8",
|
||||||
|
"hide": 2,
|
||||||
|
"description": "MG4 51 kWh battery: 50.8 kWh usable."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -187,7 +195,7 @@
|
|||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"type": "stat",
|
"type": "stat",
|
||||||
"title": "Battery temperature",
|
"title": "Climate setpoint",
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
@@ -205,7 +213,7 @@
|
|||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
},
|
},
|
||||||
"query": "from(bucket: \"mg4\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and r._field == \"batt_temp\")\n |> last()"
|
"query": "from(bucket: \"mg4\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and r._field == \"hvac_setpoint\")\n |> last()"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
@@ -235,7 +243,7 @@
|
|||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"type": "stat",
|
"type": "stat",
|
||||||
"title": "Outside temperature",
|
"title": "Last update",
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
@@ -253,17 +261,16 @@
|
|||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
},
|
},
|
||||||
"query": "from(bucket: \"mg4\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and r._field == \"ext_temp\")\n |> last()"
|
"query": "from(bucket: \"mg4\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and r._field == \"soc\")\n |> last()\n |> map(fn: (r) => ({_value: int(v: r._time) / 1000000}))"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"unit": "celsius",
|
"unit": "dateTimeFromNow",
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed",
|
"mode": "fixed",
|
||||||
"fixedColor": "#E0424C"
|
"fixedColor": "#E0424C"
|
||||||
},
|
}
|
||||||
"decimals": 0
|
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
},
|
},
|
||||||
@@ -278,7 +285,8 @@
|
|||||||
"colorMode": "value",
|
"colorMode": "value",
|
||||||
"graphMode": "none",
|
"graphMode": "none",
|
||||||
"textMode": "value"
|
"textMode": "value"
|
||||||
}
|
},
|
||||||
|
"description": "When the car last reported."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 6,
|
"id": 6,
|
||||||
@@ -301,7 +309,7 @@
|
|||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
},
|
},
|
||||||
"query": "from(bucket: \"mg4\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and r._field == \"soe\")\n |> last()"
|
"query": "from(bucket: \"mg4\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and r._field == \"soc\")\n |> last()\n |> map(fn: (r) => ({_value: r._value / 100.0 * float(v: \"${usableKwh}\")}))"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
@@ -326,7 +334,8 @@
|
|||||||
"colorMode": "value",
|
"colorMode": "value",
|
||||||
"graphMode": "none",
|
"graphMode": "none",
|
||||||
"textMode": "value"
|
"textMode": "value"
|
||||||
}
|
},
|
||||||
|
"description": "Computed: state of charge × usable capacity (dashboard constant usableKwh, 50.8 kWh for the 51 kWh battery). The car publishes no pack energy itself."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
@@ -527,7 +536,7 @@
|
|||||||
{
|
{
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"type": "timeseries",
|
"type": "timeseries",
|
||||||
"title": "Temperatures",
|
"title": "Battery voltage and current",
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
@@ -545,12 +554,12 @@
|
|||||||
"type": "influxdb",
|
"type": "influxdb",
|
||||||
"uid": "mg4-influxdb"
|
"uid": "mg4-influxdb"
|
||||||
},
|
},
|
||||||
"query": "from(bucket: \"mg4\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and (r._field == \"batt_temp\" or r._field == \"cabin_temp\" or r._field == \"ext_temp\" or r._field == \"hvac_setpoint\"))\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> keep(columns: [\"_time\", \"_field\", \"_value\"])"
|
"query": "from(bucket: \"mg4\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"telemetry\" and r.installationId == \"${installationId}\" and (r._field == \"voltage\" or r._field == \"current\"))\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> keep(columns: [\"_time\", \"_field\", \"_value\"])"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"unit": "celsius",
|
"unit": "volt",
|
||||||
"custom": {
|
"custom": {
|
||||||
"lineWidth": 2,
|
"lineWidth": 2,
|
||||||
"fillOpacity": 8,
|
"fillOpacity": 8,
|
||||||
@@ -558,7 +567,24 @@
|
|||||||
"showPoints": "never"
|
"showPoints": "never"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byRegexp",
|
||||||
|
"options": ".*current.*"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "amp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom.axisPlacement",
|
||||||
|
"value": "right"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"legend": {
|
"legend": {
|
||||||
@@ -568,7 +594,8 @@
|
|||||||
"tooltip": {
|
"tooltip": {
|
||||||
"mode": "multi"
|
"mode": "multi"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"description": "Traction battery. Current is negative while charging or regenerating."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": 11,
|
||||||
|
|||||||
Reference in New Issue
Block a user