-
Notifications
You must be signed in to change notification settings - Fork 3
Reference
- 🧮 Aggregation Functions
- 🕐 Date Formats
- 〰️ Bounds
- ⚡ Auto Scale Points
- 📈 Rise/Fall Colors
- 🎨 Color Thresholds
- 👆 Tap Actions
- 🔣 State Map
Controls how raw data points within each time bucket (interval / hour / date / week / month / year) are combined into a single graph value. Set per-entity via aggregate_func.
| Value | Formula | Best for |
|---|---|---|
avg (default)
|
Σ(values) ÷ count — the mean | Smooth lines for measurement sensors (temperature, power, humidity) |
min |
Lowest value in the bucket | Coldest temperature, minimum pressure, etc. |
max |
Highest value in the bucket | Peak temperature, max demand |
first |
Value at the start of the bucket (earliest) | Opening price, state at midnight |
last |
Value at the end of the bucket (latest) | Closing price, current state of a slowly-changing sensor |
median |
Middle value when sorted | Noise-resistant center (better than avg for data with outliers) |
sum |
Σ(values) | Totals over already-rate quantities (e.g. €/hour pricing × hours) |
delta |
max − min | Spread / range — how much the value fluctuated inside the bucket |
change |
Σ of positive step differences | Total accumulated increase over the bucket — ideal for monotonic counters (energy meters, water meters) because counter resets (jumps to zero) are ignored |
diff |
last − first — signed net change across the bucket | Values where a decrease is real movement rather than a counter reset: ratios, prices, temperatures, input_numbers. Offered in the editor's Aggregation dropdown as Diff. Returns nothing for a bucket with fewer than 2 samples |
| Function | Result | How |
|---|---|---|
avg |
19.6 | (10 + 25 + 5 + 30 + 28) / 5 |
min |
5 | Smallest |
max |
30 | Largest |
first |
10 | First in time |
last |
28 | Last in time |
median |
25 | Middle of sorted [5, 10, 25, 28, 30]
|
sum |
98 | 10 + 25 + 5 + 30 + 28 |
delta |
25 | 30 − 5 (max − min) |
change |
40 | Positive step differences only: (10→25 = +15) + (25→5 ignored) + (5→30 = +25) + (30→28 ignored) = 15 + 25 |
diff |
18 | Signed net change: 28 − 10 (last − first) |
Note: change adds up only the positive jumps between consecutive points. If your counter goes 0 → 5 → 3 → 8, the result is 5 + 5 = 10, not 8. The 5 → 3 drop is treated as a reset and ignored, the 3 → 8 rise counts.
Use aggregate_func: change with group_by: date, week, or month. This triggers an additional optimization: the card uses HA's native change field from long-term statistics, which is both faster and more accurate across counter resets than computing from raw history.
entities:
- entity: sensor.energy_meter
aggregate_func: change
graph_type: bar
group_by: date
hours_to_show: 720 # 30 days of daily consumption barsSet datetime_format at the card level to control how timestamps appear on the X-axis, tooltips, and extrema labels. system follows HA's locale setting. All other formats are applied regardless of locale — useful when your dashboard is shared across regions or when you need a more compact display.
Note: In earlier versions,
datetime_formatwas an entity-level option. It has been promoted to a card-level setting. Entity-level values still work for backward compatibility and override the card setting when present.
When using the default system format, the X-axis automatically adapts to the visible time range. If the graph spans multiple calendar days, midnight ticks (00:00) display the date (e.g. "28 Mar") instead of the time — making it easy to identify day boundaries at a glance. All other ticks show HH:mm as usual. This behavior is inspired by ApexCharts.
When a custom datetime_format is set, all ticks use that format uniformly. (Exception: a weekday-only format such as dddd labels the day boundaries but leaves the intraday clock ticks showing the time — see the note below the table.)
The number of ticks on the X-axis is calculated dynamically based on the label width, which depends on font size (x_axis_font_size) and the chosen format. Shorter formats like HH:mm fit more ticks; longer formats like DD/MM HH:mm produce fewer ticks automatically.
| Value | Example output |
|---|---|
system |
Follows HA locale |
DD/MM |
24/01 |
MM/DD |
01/24 |
DD/MM HH:mm |
24/01 14:35 |
MM/DD HH:mm |
01/24 14:35 |
HH:mm |
14:35 |
DD/MM hh:mm A |
24/01 02:35 PM |
MM/DD hh:mm A |
01/24 02:35 PM |
hh:mm A |
02:35 PM |
YYYY-MM-DD |
2026-01-24 |
YYYY-MM-DD HH:mm |
2026-01-24 14:35 |
YYYY-MM |
2026-01 |
MMM YY |
Jan 26 |
DD MMM YYYY |
24 Jan 2026 |
DD-MM-YYYY |
24-01-2026 |
MM-YYYY |
01-2026 |
ddd |
Mon (Mo / Pzt …) |
dddd |
Monday |
ddd DD MMM |
Mon 24 Jan |
ddd DD/MM |
Mon 24/01 |
Custom patterns can also be written directly in YAML using these tokens: dddd / ddd (full / short weekday name, localized), YYYY / YY (year), MMMM / MMM (full / short month name, localized), MM (month number), DD (day), HH / hh (24h / 12h hour), mm (minute), ss (second), A / a (AM/PM). Month names follow the dashboard language — e.g. MMM YY shows Jan 26 in English and Jan 26 / Jän 26 in German depending on locale.
Weekday names on the X-axis: a format containing
dddorddddalso drives the X-axis date labels on multi-day and "last N days" views — useddddon its own to show just the day name (e.g. Monday). Because day names follow your dashboard language, you get Mon / Mo / Pzt automatically. The intraday clock ticks keep showing the time, so the weekday appears only at the day boundaries, not on every hour tick.
Localization: the card ships with 16 languages — all renderer tooltip labels (Peak, Low, Range, Share, Progress, Level, Count, Change, Altitude), the Scatter and unknown-chart-mode messages are translated, and the Calendar mode's weekday headers and the Box Plot's month names follow the card language automatically.
Both entity-level (lower_bound, upper_bound) and card-level axis options (lower_bound, upper_bound for primary; lower_bound_secondary, upper_bound_secondary for secondary) support three value types:
| Format | Behavior |
|---|---|
0 |
Hard bound — axis edge is fixed at this value regardless of data |
"~0" |
Soft bound — axis prefers this value but expands if data exceeds it |
"sensor.entity_id" |
Dynamic bound — tracks the live state of another entity |
Card-level bounds set the baseline for the axis; entity-level bounds can further tighten or extend the range. When both are present, the most restrictive hard bound or the widest soft bound wins.
Use y_axis_ticks to control how many divisions appear on the Y axis. The axis range is automatically snapped to clean round numbers so labels always look tidy.
type: custom:statistics-graph-chart-card
lower_bound: 40
upper_bound: 100
y_axis_ticks: 6
entities:
- entity: sensor.cpu_temperatureThis produces labels at 40, 50, 60, 70, 80, 90, 100 — similar to setting tickAmount: 6 in Apex Charts or an interval of 10 in Excel.
When using the interval picker to switch between time ranges (1H → 7D → 90D), the default points_per_hour and group_by can be too dense for long periods or too sparse for short ones. Enable auto_scale_points to let the card pick a sensible bucket size automatically based on the visible window.
type: custom:statistics-graph-chart-card
auto_scale_points: true
show_interval_picker: true
hours_to_show: 24
entities:
- entity: sensor.power_consumption
- entity: sensor.solar_production
- entity: sensor.grid_exportThe card chooses both bucket size and group_by from a fixed table, optimized to keep around 100 visible points at every zoom level:
| Visible window | Bucket | One point every |
|---|---|---|
| ≤ 4 hours | interval | 5 min |
| ≤ 12 hours | interval | 15 min |
| ≤ 48 hours | interval | 30 min |
| ≤ 6 days | hour | 1 hour |
| ≤ 59 days | date | 1 day |
| ≤ 180 days | week | 1 week |
| > 180 days | month | 1 month |
The day bands compare on rounded days, so a DST-shortened 167-hour "7 day" window still lands in the daily band.
Note: Entity-level
points_per_houroverrides are not affected — only entities inheriting the card-level value are scaled.
Safe fallback for advanced setups. When any entity uses offset, forecast_horizon, or data_attribute (attribute-based forecast data), Auto-Scale steps aside and keeps your configured points_per_hour and group_by exactly as-is. These features create their own time windows that don't align with automatic re-bucketing, so the card stays out of the way to avoid breaking them.
Auto Scale can also follow your thresholds instead of the built-in table. Each rule says: when the visible period is up to N hours, use this Group By (and optionally a Points/Hour). Only active when auto_scale_points: true.
type: custom:statistics-graph-chart-card
auto_scale_points: true
auto_scale_rules:
- up_to_hours: 28 # up to ~a day → hourly buckets
group_by: hour
- up_to_hours: 56 # up to ~two days → 2-hour buckets
group_by: 2h
- up_to_hours: 8784 # up to a year → monthly buckets
group_by: month
entities:
- entity: sensor.power_consumption| Rule field | Type | Default | Description |
|---|---|---|---|
up_to_hours |
number | required | The rule applies when the visible period is up to this many hours. |
group_by |
string | interval |
Bucketing to use — same values as the main group_by: interval, hour, 2h, 3h, 4h, 6h, 12h (any Nh works, e.g. 5h), date, week, month, year, raw. day is accepted as an alias of date. |
points_per_hour |
number | null |
Optional bucket density for this rule — only meaningful with group_by: interval. The editor offers the standard divisor-of-60 presets. |
x_axis_interval |
string | null |
Optional X-axis tick interval while this rule is active (same syntax as the card-level option, e.g. 4h). YAML only. (v3.29)
|
datetime_format |
string | null |
Optional date/time format while this rule is active (same patterns as the card-level option, e.g. HH:mm). YAML only. (v3.29)
|
Presentation follows the active rule (v3.29): while a rule rescales the chart, the card-level x_axis_interval and datetime_format are suspended — they were tuned for your base scale, and keeping them left a Day view with a near-empty axis and time-less tooltips (#308). Automatic ticks and locale date+time formatting take over, unless the rule provides its own overrides above.
How the rules resolve:
- The smallest matching threshold wins — with the rules above, a 30-hour view matches the
56rule, not the8784one. -
Leave a small buffer above nominal periods (the examples use
28for "a day"): a DST fall-back day is 25 hours, andup_to_hours: 24would skip your hourly rule exactly that day. - A rule that mirrors your base scale (same
group_byas the card, nopoints_per_houror presentation overrides) doesn't count as a rescale — your explicit axis settings stay in force on those views. (v3.29) - A period beyond every threshold falls back to the built-in auto-scale table above.
- An empty or absent list means pure built-in behavior — exactly as before.
-
Date-picker aware. A full month or year selection matches by its nominal length even while the period is still running — pick a year in July and the
8784rule still applies, exactly like the built-in auto scale. - The same safety guard applies: when any entity uses
offset,forecast_horizon, ordata_attribute, the configuredgroup_byis kept untouched.
Editor: with Auto Scale on, a Custom Scale Rules list appears — an Add Rule button adds rows with Up to (hours), Group By (the same options as the main Group By select), and a Points/Hour dropdown that is enabled only for Interval rules.
Colors each graph segment based on its slope relative to the previous point. Unlike color thresholds (which react to absolute values), rise/fall coloring reacts to direction of change — making it easy to spot momentum shifts at a glance. The trend_period_hours setting on the entity controls the smoothing window used to determine whether a segment counts as rising, falling, or stable.
rise_fall_colors:
enabled: true
increase: "#2ecc71" # color when value is rising
decrease: "#e74c3c" # color when value is falling
stable: "#95a5a6" # color when value is flat
⚠️ Cannot be combined withcolor_thresholdson the same entity.
color_thresholds:
enabled: true
direction: vertical # vertical or horizontal
transition: smooth # smooth or hard
values:
- value: 0 # at or above this value → use this color
color: "#3498db"
- value: 20
color: "#2ecc71"
- value: 35
color: "#e74c3c"Thresholds are sorted by value automatically. The color of the lowest threshold applies to everything below it.
Each value and color also accepts an entity reference — sensor.x (state) or sensor.x.attribute (attribute, nested paths supported) — so thresholds can track live entities and update as those change.
Setting color: threshold, state_color: threshold, icon_color: threshold, or point_colors: threshold on the entity makes those elements also reflect the threshold color.
direction controls which axis the colors are painted along:
-
vertical(default) — Y-axis gradient. Colors map to value height on the chart. -
horizontal— per-segment coloring along the time axis. Each segment gets the color of its data value.
transition controls how color changes between bands:
-
smooth— gradual interpolation as values pass through thresholds -
hard— instant color switch exactly at the threshold value
⚠️ Cannot be combined withrise_fall_colorson the same entity.
| Action | Description |
|---|---|
none |
No action (default) |
more-info |
Open entity detail dialog |
navigate |
Navigate to a dashboard path (requires navigation_path) |
url |
Open an external URL (requires url) |
call-service |
Call an HA service (requires service and optional service_data) |
Maps non-numeric state strings to integer values for graphing. Order determines the number (0-based index). Each entry takes a value plus an optional label and an optional color. The Y-axis automatically shows the original state names (or the label) instead of the numeric index, and color sets the segment colour in state_timeline mode (it also drives the state row tint when state_adaptive_color is on).
state_map:
- value: "off" # → 0, axis shows "off"
- value: "idle" # → 1, axis shows "idle"
- value: "on" # → 2, axis shows "on"With optional friendly labels:
state_map:
- value: "off"
label: Stopped # axis shows "Stopped"
- value: "on"
label: Running # axis shows "Running"The state row always displays the original string (or label if provided), not the number.
⚠️ State values are case-sensitive and must match exactly what HA reports (always lowercase forbinary_sensor). Auto-detected forbinary_sensor,input_boolean, andinput_selectentities — nostate_mapneeded in step mode.