Skip to content

feat: new dashboard that shows available temperatures historical - #5457

Draft
slayer01 wants to merge 6 commits into
teslamate-org:mainfrom
slayer01:slayer01/temperatures-dashboard
Draft

feat: new dashboard that shows available temperatures historical#5457
slayer01 wants to merge 6 commits into
teslamate-org:mainfrom
slayer01:slayer01/temperatures-dashboard

Conversation

@slayer01

@slayer01 slayer01 commented Jul 1, 2026

Copy link
Copy Markdown

Add a Grafana dashboard with a historical overview of all temperatures logged by TeslaMate:

  • Inside & outside temperature time series
  • Stat tiles for current values and min/max over the selected range
  • Climate setpoints (driver/passenger) while climate control is on
  • Outside temperature during charging
  • Heatmap of the outside-temperature frequency distribution

@netlify

netlify Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploy Preview for teslamate ready!

Name Link
🔨 Latest commit 274e6ff
🔍 Latest deploy log https://app.netlify.com/projects/teslamate/deploys/6a44efbb679cdf00081704d8
😎 Deploy Preview https://deploy-preview-5457--teslamate.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@slayer01 slayer01 changed the title new dashboard that shows available temperatures historical feat: new dashboard that shows available temperatures historical Jul 1, 2026
@JakobLichterfeld JakobLichterfeld added the area:dashboard Related to a Grafana dashboard label Jul 1, 2026
@JakobLichterfeld

Copy link
Copy Markdown
Member

Thank you for your contribution, Daniel. And congratulations on creating your first ever PR on GitHub!

From a user`s perspective, I can see the benefit of this dashboard. As a maintainer, I love that you have already added a screenshot and the relevant link in readme.

May I ask you to review the dashboard, @swiffer? I can't see anything related to performance, but I'd be happy to be proved wrong and see your magic there as well. :-)

@JakobLichterfeld JakobLichterfeld added this to the v4.1.0 milestone Jul 1, 2026
@JakobLichterfeld JakobLichterfeld added the kind:idea Idea for new feature or some form of enhancement label Jul 1, 2026
@sdwalker

sdwalker commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Seems to be hardcoded to celsius only?

@swiffer swiffer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this contribution — a multi-day temperature history is a real gap in the default set (Overview is “now”, Drive Details is per-drive). Screenshot + docs update are appreciated.

A few change requests before we can merge:

1. Units (temp_unit / convert_celsius)

All panels hardcode Grafana unit celsius, axis labels °C, and Celsius thresholds. Please follow the existing pattern (Overview, Efficiency, Projected Range, Drive Details, …):

  • Hidden template var temp_unit from settings.unit_of_temperature
  • Wrap values with convert_celsius(..., '$temp_unit')
  • Make field units / thresholds / axis labels unit-aware (°F must not keep 10/25/30/40 thresholds)

2. Dashboard shell (consistency)

Please align with other provisioned dashboards:

  • base_url (settings.base_url)
  • Header links: TeslaMate → ${base_url:raw} + Dashboards dropdown (tag tesla)
  • car_id as elsewhere

3. Drop / replace the “Distribution” heatmap

The heatmap counts raw positions samples. Logging is much denser while driving than when parked, so this is sampling-biased (same class of issue we handle carefully on Charge Level) and is easy to misread as “time at temperature.”

It also doesn’t answer a clear user question that the history + extremes don’t already cover.

Prefer: a daily outside-temp candlestick (or equivalent band):

Field Source
high max
low min
open p15 (e.g. percentile_cont(0.15))
close p85

Compute on time-debiased inputs (e.g. average into fixed bins first, then daily aggregates)—not raw row counts. Days with no samples can simply have no candle.

4. Structure from a user perspective

Suggested focus for this dashboard:

  1. At a glance — last inside/outside + outside min/max (stats only; see §5)
  2. Hero — inside & outside over time; optional driver setpoint while is_climate_on
  3. Daily ambient envelope — candlestick (§3)
  4. Optional secondary — outside while charging; keep light

Passenger setpoint is usually redundant with driver; one setpoint series is enough.

5. Stats and how to draw “mostly complete” history

Last-value stats: Prefer graphMode: none and a dedicated last reading (ORDER BY date DESC LIMIT 1), as on Overview. Avoid graphMode: area sparklines over long, sparse ranges — they look patchy and don’t add much next to the hero chart.

Main history timeseries: Aim for real data that still reads as continuous when the car was actually reporting:

  • Aggregate with $__timeGroup / date_bin only where samples exist (avg of temps in the bucket). Avoid filling every interval in the range with NULL — that turns a normal 30d view into a comb of tiny segments.
  • Set Connect null values → Threshold (not Always). e.g. connect gaps shorter than ~30–60 minutes (or ~1–2× $__interval), and break the line for longer sleeps/offline periods. Today spanNulls: true draws multi-day slopes through periods with no readings.
  • Prefer not carrying outdoor temp forward across long gaps (LOCF); a visible break is more honest than inventing overnight ambient.

That combination stays faithful to the log while keeping the chart readable for “mostly complete” awake/driving stretches.

6. Query reuse / cost

Several panels re-scan positions for overlapping stats (last, min/max, history, heatmap). Prefer one or few shared queries + transformations / shared query results for last, min/max, history, and daily aggregates—cheaper and less drift between panels. Avoid shipping every raw position row for long ranges (30d default).


Happy to re-review after a revision. Happy to help iterate on the daily candlestick or connect-threshold if useful.


🤖 Assisted by Grok 4.5 (xAI) via Grok Build (planning, review drafting).

@JakobLichterfeld
JakobLichterfeld marked this pull request as draft August 13, 2026 08:45
@JakobLichterfeld JakobLichterfeld modified the milestones: v4.1.0, v4.2.0 Aug 13, 2026
…hboards

Review feedback teslamate-org#1 and teslamate-org#2 of PR teslamate-org#5457:

- add hidden temp_unit variable (settings.unit_of_temperature) and wrap
  all temperature values in convert_celsius(); unit is carried in the
  field names ([°C]/[°F]) like in the other provisioned dashboards
- drop Celsius-specific value thresholds (not convertible per unit) and
  the hardcoded °C axis labels; add decimals: 1
- add base_url variable, TeslaMate header link and Dashboards dropdown
  to match the other provisioned dashboards

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Aug 19, 2026

Copy link
Copy Markdown

Deploy Preview for teslamate ready!

Name Link
🔨 Latest commit d1e8704
🔍 Latest deploy log https://app.netlify.com/projects/teslamate/deploys/6a85a66cbe167700080ebb6a
😎 Deploy Preview https://deploy-preview-5457--teslamate.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

slayer01 and others added 4 commits August 19, 2026 14:05
…stick

Review feedback teslamate-org#3 of PR teslamate-org#5457: the frequency heatmap counted raw
positions samples, which over-weights driving (dense logging) vs
parking and reads misleadingly as "time at temperature".

Replace it with a daily candlestick computed on time-debiased inputs:
readings are first averaged into fixed 15-minute bins, then aggregated
per day (wick = min/max, body = 15th-85th percentile). Days without
samples have no candle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ro chart

Review feedback teslamate-org#5 of PR teslamate-org#5457 plus iteration on the setpoint display:

- last-value stats fetch the single latest reading (ORDER BY date DESC
  LIMIT 1) instead of scanning the whole selected range, and drop the
  patchy area sparkline (graphMode: none)
- history chart breaks its lines on gaps longer than 1h (insertNulls)
  instead of interpolating through sleep/offline periods; short gaps
  still connect (spanNulls threshold); rows without any temperature
  reading no longer create empty buckets
- fold the driver setpoint into the main history chart as a thin dashed
  step line and remove the separate climate setpoints panel (passenger
  setpoint dropped as redundant); charging panel widens to full width

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…processes

Review feedback teslamate-org#6 of PR teslamate-org#5457:

- the two min/max stats now share a single positions scan: the outside
  panel computes all four values, the inside panel consumes its result
  via a dashboard (shared query) datasource
- the charging panel reads the pre-aggregated
  charging_processes.outside_temp_avg (one row per charge) instead of
  joining and grouping raw charges samples; drawn as points with a
  dotted trend line
- combined with the LIMIT-1 last-value stats and the setpoint folded
  into the history query, the dashboard is down to three range scans
  over positions (min/max, history, daily candlestick)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@slayer01

Copy link
Copy Markdown
Author

Thanks for the thorough review!

Addressed in the latest commits:

  1. Units: all values go through convert_celsius(..., '$temp_unit') with the hidden temp_unit variable; unit is carried in the field names ([°C]/[°F]).
  2. Shell: base_url variable, TeslaMate link and Dashboards dropdown added.
  3. Heatmap replaced by the suggested daily candlestick (15-min debiased bins, wick = min/max, body = p15–p85).
  4. Passenger setpoint was dropped; the driver setpoint now lives in the main history chart as a dashed step line. It is shown whenever the car reports data (same gap rules as the measured series) rather than only while is_climate_on — filtered to climate-on it collapsed to isolated dots at 30d ranges. I kept the inside min/max stat intentionally — interesting on its own (e.g. cabin heat buildup) and it now costs nothing extra (see 6).
  5. Last-value stats use ORDER BY date DESC LIMIT 1 with graphMode: none; history lines disconnect on gaps > 1h (insertNulls) instead of interpolating through sleep.
  6. Down to three range scans over positions: the two min/max stats share one query via a dashboard datasource, and the charging panel reads charging_processes.outside_temp_avg (one row per charge).

Screenshot updated to the reworked layout.

Of course, happy to make further changes if needed.
Thanks again for the detailed review!

@slayer01
slayer01 marked this pull request as ready for review August 19, 2026 12:56
@slayer01
slayer01 requested a review from swiffer August 19, 2026 12:57

@JakobLichterfeld JakobLichterfeld left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the thorough rework, Daniel — what I said when you opened this PR still stands, and you've since proven it: you took on every single point of @swiffer's detailed review and addressed the core of all of them — units, dashboard shell, the candlestick replacing the heatmap, shared queries. That's more than many seasoned contributors manage in one revision.

A few things surfaced in my final pass — one blocker, a couple of fixes, and some polish. Don't be discouraged by the list; most of these are subtle TeslaMate-specifics you couldn't have known, and I'm happy to help with any of them.

Blocking (CI will fail and is failing)

  1. The two "last value" queries need ideal_battery_range_km IS NOT NULL. We have a schema-contract test (test/teslamate/grafana/dashboard_queries_test.exs) that requires this predicate on every FROM positions … ORDER BY date DESC LIMIT 1 query — it's what makes our partial index from #5438 usable; without it each tile scans the car's entire position history. Easiest fix: copy the Overview pattern (overview.json), which adds the predicate plus date >= (TIMEZONE('UTC', NOW()) - INTERVAL '60m') — the freshness bound also makes a week-old reading render as "No data" instead of posing as the current temperature.

Should be fixed before merge

  1. Daily candles are cut at UTC midnight. date_trunc('day', bin) truncates in the server timezone while the dashboard renders in browser time — for a user at UTC+10 every candle spans 10:00–10:00 local and the overnight minimum lands in the wrong day. Please use the repo's 3-arg pattern, e.g. date_trunc('day', timezone('UTC', date), '$__timezone') (see statistics.json / drive-stats.json, documented in our development docs - Queries involving timestamp columns).

  2. The main history panel goes blank at long ranges. With insertNulls: 3600000 + spanNulls: 3600000, once $__interval exceeds 1 h (roughly ≥ 6-month ranges), a break is inserted between every pair of buckets and can never be re-connected — combined with showPoints: "never" the chart renders essentially nothing. The 30d default hides this, so it's easy to miss in testing. One option that scales with zoom: drop insertNulls, use $__timeGroup(date, $__interval, NULL) to null-fill only truly empty buckets, and keep the spanNulls threshold — @swiffer, you had the clearest picture here, what combination would you go for?

  3. Please export the JSON through the bundled Grafana (13.1.3 atm, "Save JSON to file", as described in our development docs - Making Changes to Grafana Dashboards). The file is currently hand-formatted with pluginVersion: "13.0.1+security-01" and a custom uid — the next re-export round-trip would rewrite the whole file and bury real changes in a formatting diff.

Polish (quick wins)

  1. README.md lists all bundled dashboards alphabetically — please add Temperatures between Statistics and Timeline.
  2. Candlestick colors: with open = p15 and close = p85, close >= open always holds, so every candle renders "up"/red — a fixed single color would be more honest (red reads as "hot" on a temperature chart).
  3. The four stat tiles use unit: "none" while the graphs use "degree" — using "degree" like the Overview temperature stats gives the tiles the ° suffix too.

One design question (no action needed from you yet)

The driver setpoint is now drawn whenever the car reports data, including while climate is off — a parked car draws a continuous setpoint line while the cabin drifts to ambient. Your reasoning for dropping the is_climate_on filter (line collapsing to dots at 30d) makes sense to me; @swiffer, are you fine keeping it as is, perhaps renaming the series to make clear it's the reported setting rather than active conditioning?

The dashboard fills a real gap in our default set and is very close now. Thanks for sticking with it through the review rounds — hope we'll see more contributions from you after this one! 🧡


🤖 Review drafted with Claude Code (Fable 5 high) — sponsored by Claude for Open Source

@JakobLichterfeld JakobLichterfeld modified the milestones: v4.2.0, v4.3.0 Aug 21, 2026
@JakobLichterfeld
JakobLichterfeld marked this pull request as draft August 21, 2026 09:18
@swiffer

swiffer commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Hey @slayer01,

Some minor changes for the Daily Candles, see the screenshot for what i would suggest to change in addition:

grafik

New tooltip text:

For each day the thin line runs from the lowest to the highest temperature recorded, while the thicker bar shows the 15th–85th percentile range (the temperatures between which 70% of the day’s readings fell). Days with no readings are left blank.

Note: On days with only partial coverage (for example just a few hours of data), the shown range only reflects the periods that were actually measured and may not represent the full day. The red line shows the data coverage for each day.

Why the coverage line was added
Even with the 15-minute binning, a day can still have very sparse data (e.g. only 1–2 hours measured). Without a visual indicator it is easy to misread a short candle as “the full day’s temperature range”. The red line at the bottom shows the actual data coverage for each day so users can immediately see how complete the underlying measurements are.

How it is placed
I put the coverage series on a separate Y-axis (right side, hidden) and styled it as a thin line so it stays out of the way of the temperature envelopes while remaining readable. Screenshot of the Grafana panel settings is attached.

grafik grafik

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:dashboard Related to a Grafana dashboard kind:idea Idea for new feature or some form of enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants