Skip to content
cataseven edited this page Jul 29, 2026 · 1 revision

Reference

Jump to

📖 Reference

🧮 Aggregation Functions

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

Worked example — 1-hour bucket with values [10, 25, 5, 30, 28] recorded in this order

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.

When your sensor is a counter (state_class: total_increasing)

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 bars

🕐 Date Formats

Set 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_format was 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.

Smart X-Axis Labels

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 ddd or dddd also drives the X-axis date labels on multi-day and "last N days" views — use dddd on 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.


〰️ Bounds

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.

Y Axis Tick Control

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_temperature

This 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.


⚡ Auto Scale Points

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_export

The 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_hour overrides 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.

Custom Scale Rules (auto_scale_rules) (new in v3.26)

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 56 rule, not the 8784 one.
  • Leave a small buffer above nominal periods (the examples use 28 for "a day"): a DST fall-back day is 25 hours, and up_to_hours: 24 would skip your hourly rule exactly that day.
  • A rule that mirrors your base scale (same group_by as the card, no points_per_hour or 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 8784 rule still applies, exactly like the built-in auto scale.
  • The same safety guard applies: when any entity uses offset, forecast_horizon, or data_attribute, the configured group_by is 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.


📈 Rise/Fall Colors

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 with color_thresholds on the same entity.


🎨 Color Thresholds

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 with rise_fall_colors on the same entity.


👆 Tap Actions

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)

🔣 State Map

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 for binary_sensor). Auto-detected for binary_sensor, input_boolean, and input_select entities — no state_map needed in step mode.

Clone this wiki locally