Repository navigation
feat: histogram, scatter, bubble, heatmap and box plot charts - #106
Merged
Merged
Conversation
A new chart style, "Histogram" (style: histogram, in the editor's Distributions group), shows how often each entity's readings fell in each range: how long the living room sat below 18 °C this week, or the spread of a response time. Bins count time by default (count: time). History records changes, not samples: a room at 20 °C all day that flickers to 21 ten times in a minute has eleven rows, ten of them 21. Counting rows says it was mostly 21; adding up how long each value held says 20, which is what the room was. Each value counts until the next reading (the last until now or the end of the range), and time spent unavailable counts nothing. count: readings counts rows instead, for sensors where each reading is an event; the value carried in from before the range and the copy carried to its right edge are not counted. Long-term statistics rows count one period each, so a missing row is missing data rather than a long-held value. Grouped readings (group_by) count each group's length. Bin width is automatic (a round 1, 2 or 5 × 10ⁿ for a number of bins that suits the card's width), or set with bin_width (edges on its multiples) or bins (an exact split from the lowest to the highest reading), at most 100. x_min / x_max fix the range; readings outside it are left out. Bars show each entity's share of the whole (show_as: percent) or the hours / number of readings (show_as: total). Two or more entities are drawn side by side in each bin or over each other (bars: overlap, translucent with an outline). color_by: bands colours each bin with the colour band its middle falls in. It reuses the history layer and the shared frame, so periods and previous / next, statistics, grouping, axes, gridlines, compact and the header placement all work. Hovering or touching a bin shades it and lists each entity's share and time there. Every option is in the editor's Chart section; each entity has a bar colour. Value axes along the bottom, x_min / x_max and the pick-one-mark tooltip live in the new src/chart/xy.ts, shared with the other distribution charts; chartArea takes optional label formatters for axes that aren't time. Closes #19
A new "Scatter" style in the Distributions group. By default the first entity goes along the bottom and every other entity is plotted up the side against it, one coloured series each: outside temperature against heating power, say. Readings are paired in time: history records changes, not samples, so at every moment any of the sensors changed, each one's value is the last it reported. Moments when one of them was unavailable, or had not reported yet, are left out, and so is the reading the chart carries on to its right edge. With "Group readings by" set, each group (an hour's average outside against that hour's heating, say) is one dot, which is the better way to compare a fast sensor with a slow one. "Along the bottom: Time" plots each entity's readings over time as dots, on the usual time axis. Jitter (px) nudges each dot a steady random amount, worked out from the reading's own time so a dot stays put as new readings arrive, so readings that land on top of each other stay visible. Dot size, an optional fixed range along the bottom (x_min, x_max), the period, source, grouping, axes, gridlines, header and compact layout are all in the editor's Chart section; each entity's dot colour is in its own options. Hover or touch picks the nearest dot and shows its time and both readings. Fewer entities than the chart needs shows a message saying what to add; a missing or unavailable entity just has no dots. The value axes, picking and tooltip box live in src/chart/xy.ts, shared with the other charts that have values along the bottom, and the pairing and drawing in src/chart/scatter-plot.ts, shared with the bubble chart. Closes #20
A new "Bubble" style in the Distributions group: the first entity goes along the bottom, the second up the side, and the third sets the size of each bubble (temperature against humidity, sized by CO2, say). The three are paired in time as the scatter chart does: at every moment any of them changed, each one's value is the last it reported, leaving out moments when one was unavailable or had not reported yet. Grouping by hour or day gives one bubble per group, which keeps a busy sensor from burying the chart. A bubble's area, not its width, grows with the size reading, from the smallest bubble at the lowest reading in view to the largest at the highest, so twice as far up the range covers twice as much of the chart. Larger bubbles are drawn first so small ones stay on top, half see through. A legend under the chart shows the smallest and largest bubble with the readings they stand for; hover or touch shows all three readings and the time. Smallest and largest bubble (px), jitter, an optional fixed range along the bottom, and the usual period, source, grouping, axes and layout options are in the editor's Chart section; the bubble colour is the second entity's own colour option. Fewer than three entities shows a message saying what to add; a missing or unavailable size entity leaves no bubbles. Shares src/chart/xy.ts and src/chart/scatter-plot.ts, byte for byte, with the scatter chart. Closes #21
A new chart style, "Heatmap", in the Distributions group of the style picker. It shows when something happens: each cell is coloured by a value, so a house's power by hour of the day and day of the week shows the busy evenings at a glance, and a calendar of daily values shows a year. Two layouts (`grid`): - hours (the default): 7 rows of weekdays by 24 hours. Readings are grouped by hour in Home Assistant's time zone (the shared grouping), then every Tuesday 14:00 is averaged into one cell. Rows start on the user's first weekday. Without a period it covers the last 4 weeks of history. - calendar: a cell per day, a column per week, weekdays down the side and month names along the bottom, like a contribution graph. Without a period it covers the last 12 months of long-term statistics, since history rarely reaches back a year. Readings combine with the shared `aggregate` (mean by default; change for energy meters), and period, previous/next buttons and source work as on every history chart. `group_by` is set by the layout and ignored. Colour comes from a smooth scale of the entity's colour (faint for the lowest, full for the highest, or between `color_min` and `color_max`), or from the entity's colour bands (`color_by: bands`). A legend under each grid shows the gradient with its ends, or the bands. Cells can be rounded (default), square or hexagons, and keep their shape at any card width. Hours or days with no readings are drawn in the empty track colour, not as zero. Each entity gets its own grid, scale and legend; tapping its name runs its tap action. Hovering or touching a cell shows its time, value and, for the hours grid, how many days were averaged. Unavailable and missing entities draw an empty grid. The grid fades in, without motion when the browser asks for less. Every option is in the editor's Chart section: layout, period, source, how to combine, colour, cell shape, colour scale ends, legend and tooltip; the per-entity options hold the colour. Value-axis charts in this release share `src/chart/xy.ts` (picking a mark under the pointer and its tooltip, which the heatmap uses) and optional label formatters on `chartArea`. Closes #22
…ay, week or month A new chart style, "Box plot" (style: box), in the editor's Distributions group. Each box shows the median, the quartiles and the whiskers of an entity's readings over the period. Without grouping there is one box per entity; with group_by (hour, day, week, month) there is one box per group, and several entities sit side by side in their series colours. History records changes, so each value counts for as long as it held: a room that sat at 20 °C all day and flickered to 30 °C for a few minutes has a median of 20. The value carried in from before a group counts from its start, unavailable time doesn't count, and the current group stops at now. Long-term statistics are fetched as hourly rows (five-minute rows for hourly boxes); rows are equal length, so each counts once, and their own min and max widen the box's lowest and highest. Quantiles are weighted Hazen quantiles (four readings 1-4 have a median of 2.5). Whiskers reach 1.5 times the box's height with readings beyond drawn as outlier dots (whiskers: tukey, the default), or the lowest and highest (whiskers: range). orientation: horizontal turns the boxes on their side with the names up the side; readings: true draws each reading as a jittered dot over its box (at most 120 per box, always in the same place). Hovering or touching a box shows its highest, quartiles, median, lowest, average and reading count. It reuses the history charts' period, previous/next buttons, source, axes, gridlines, value range, compact layout and header. All options are in the editor's Chart section; each entity has a box colour. Grouping gives only the boundaries here, so there is no "combine with" choice. Also adds src/chart/xy.ts (value axes and a pick-one-mark tooltip shared with the other distribution charts) and lets chartArea take its own label text for each axis. Closes #23
The mocked history returned a reading an hour after the period start, which is in the future just after midnight, so the line ran past now. The mock now never returns a reading later than now, like the recorder.
github-actions Bot
pushed a commit
that referenced
this pull request
Nov 6, 2026
## [1.5.0](v1.4.0...v1.5.0) (2026-11-06) ### Features * histogram, scatter, bubble, heatmap and box plot charts ([#106](#106)) ([1af60ee](1af60ee)), closes [#19](#19) [#20](#20) [#21](#21) [#22](#22) [#23](#23) ### Documentation * list the pie, rings, pictograph and waffle charts in the README ([#107](#107)) ([886b1fc](886b1fc))
github-actions Bot
pushed a commit
that referenced
this pull request
Nov 6, 2026
## [2.0.0](v1.3.0...v2.0.0) (2026-11-06) ### ⚠ BREAKING CHANGES * on/off states now read as 1 and 0 on number charts, so a binary sensor on a line, area or column chart draws a 0/1 step instead of nothing. ### Features * Chartdeck 2.0, every remaining roadmap chart ([#111](#111)) ([3aa1f9b](3aa1f9b)), closes [#13](#13) [#14](#14) [#24](#24) [#25](#25) [#26](#26) [#27](#27) [#28](#28) [#29](#29) [#30](#30) [#31](#31) [#32](#32) [#33](#33) [#34](#34) [#35](#35) [#36](#36) [#37](#37) [#38](#38) [#40](#40) [#41](#41) [#42](#42) [#43](#43) [#44](#44) [#45](#45) [#46](#46) [#57](#57) [#58](#58) [#59](#59) [#82](#82) [#83](#83) [#84](#84) [#110](#110) * histogram, scatter, bubble, heatmap and box plot charts ([#106](#106)) ([1af60ee](1af60ee)), closes [#19](#19) [#20](#20) [#21](#21) [#22](#22) [#23](#23) * pie and donut, rings, pictograph and waffle charts ([#104](#104)) ([e596014](e596014)), closes [#15](#15) [#16](#16) [#17](#17) [#18](#18) ### Documentation * list the histogram, scatter, bubble, heatmap and box plot charts in the README ([bd6116f](bd6116f)) * list the pie, rings, pictograph and waffle charts in the README ([#107](#107)) ([886b1fc](886b1fc))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 1.5.0, milestone 1.5.0 Patterns over time. It adds five history charts in a new "Distributions" group in the style picker. They reuse the history layer and the shared frame, so period, previous/next buttons, source, grouping, axes, gridlines, compact layout and the header work as on the other history charts. Every option is in the editor's Chart section.
Histogram (
style: histogram)Shows how often each entity's readings fell in each range, such as how long the living room sat below 18 °C this week. Bins count time by default (
count: time): each value counts until the next reading, and unavailable time counts nothing.count: readingscounts rows instead. Bin width is automatic, or set withbin_widthorbins(at most 100).x_min/x_maxfix the range.show_as: percent | total,bars: overlapfor several entities, andcolor_by: bands.Scatter (
style: scatter)The first entity goes along the bottom and every other entity is plotted up the side against it. Readings are paired in time: at every moment any sensor changed, each one's value is the last it reported. With grouping set, each group is one dot. "Along the bottom: Time" plots readings over time as dots. Options include jitter, dot size and
x_min/x_max. Hover picks the nearest dot.Bubble (
style: bubble)A scatter where a third entity sets each bubble's area. A legend under the chart shows the smallest and largest bubble with the readings they stand for. Fewer than three entities shows a message saying what to add.
Heatmap (
style: heatmap)grid: hours(default) is 7 weekday rows by 24 hours, averaged over the last 4 weeks.grid: calendaris one cell per day for the last 12 months of statistics, like a contribution graph. Colour is a smooth scale of the entity's colour (orcolor_min/color_max), or its colour bands. Cells can be rounded, square or hexagons. Hours or days with no readings are drawn empty, not as zero.Box plot (
style: box)Median, quartiles and whiskers of each entity's readings, one box per entity or one per hour, day, week or month with
group_by. Each value counts for as long as it held.whiskers: tukey | range,orientation: horizontal,readings: truefor jittered reading dots.Also in this PR
src/chart/xy.ts(value axes along the bottom, picking one mark, its tooltip) andsrc/chart/scatter-plot.ts(scatter and bubble).chartAreatakes optionalxLabel/yLabelformatters, merged alongside main's right-hand axis andaxes.label.period-card.test.tsfailed between midnight and 1 am, because its mocked history returned a reading in the future. The mock now never returns one later than now. Checked with the clock pinned at 00:20.Checked on ha-dev during the build, and again after merging onto main with the screenshot harness in light and dark. 550 tests pass, typecheck, lint and build are clean.
Closes #19
Closes #20
Closes #21
Closes #22
Closes #23