Revised the descriptions and names for the previous interval price rank entities across multiple language translation files to enhance clarity and consistency. Impact: Users will see improved terminology in the interface, making it clearer that the ranks refer to the previous interval's prices.
18 KiB
Volatility Sensors
:::tip Entity ID tip
<home_name> is a placeholder for your Tibber home display name in Home Assistant. Entity IDs are derived from the displayed name (localized), so the exact slug may differ. Can't find a sensor? Use the Entity Reference (All Languages) to search by name in your language.
:::
Volatility sensors help you understand how much electricity prices fluctuate over a given period. Instead of just looking at the absolute price, they measure the relative price variation, which is a great indicator of whether it's a good day for price-based energy optimization.
The calculation is based on the Coefficient of Variation (CV), a standardized statistical measure defined as:
CV = (Standard Deviation / Arithmetic Mean) * 100%
This results in a percentage that shows how much prices deviate from the average. A low CV means stable prices, while a high CV indicates significant price swings and thus, a high potential for saving money by shifting consumption.
The sensor's state can be low, moderate, high, or very_high, based on configurable thresholds.
Available Volatility Sensors
| Sensor | Description | Time Window |
|---|---|---|
| Today's Price Volatility | Volatility for the current calendar day | 00:00 - 23:59 today |
| Tomorrow's Price Volatility | Volatility for the next calendar day | 00:00 - 23:59 tomorrow |
Next 24h Price Volatility (next_24h_volatility) |
Volatility for the next 24 hours from now | Rolling 24h forward |
| Today + Tomorrow Price Volatility | Volatility across both today and tomorrow | Up to 48 hours |
Configuration
You can adjust the CV thresholds that determine the volatility level:
- Go to Settings → Devices & Services → Tibber Prices.
- Click Configure.
- Go to the Price Volatility Thresholds step.
Default thresholds are:
- Moderate: 15%
- High: 30%
- Very High: 50%
Key Attributes
All volatility sensors provide these attributes:
| Attribute | Description | Example |
|---|---|---|
price_volatility |
Volatility level (language-independent, always English) | "moderate" |
price_coefficient_variation_% |
The calculated Coefficient of Variation | 23.5 |
price_spread |
The difference between the highest and lowest price | 12.3 |
price_min |
The lowest price in the period | 10.2 |
price_max |
The highest price in the period | 22.5 |
price_mean |
The arithmetic mean of all prices in the period | 15.1 |
price_median |
Median price (50th percentile, robust to outliers) | 14.8 |
price_q25 |
25th percentile — lower quartile price | 11.0 |
price_q75 |
75th percentile — upper quartile price | 19.5 |
price_typical_spread |
Typical price band width — IQR (Q75 − Q25, the middle 50% of prices) | 8.5 |
price_typical_spread_% |
Typical price band as a percentage of the median (IQR%) | 57.4 |
price_spike_count |
Intervals outside the Tukey fence (Q25−1.5×IQR … Q75+1.5×IQR) — spikes/dips | 3 |
interval_count |
Number of price intervals included in the calculation | 96 |
Usage in Automations & Best Practices
You can use the volatility sensor to decide if a price-based optimization is worth it. For example, if your solar battery has conversion losses, you might only want to charge and discharge it on days with high volatility.
Best Practice: Use the price_volatility Attribute
For automations, it is strongly recommended to use the price_volatility attribute instead of the sensor's main state.
- Why? The main
stateof the sensor is translated into your Home Assistant language (e.g., "Hoch" in German). If you change your system language, automations based on this state will break. Theprice_volatilityattribute is always in lowercase English ("low","moderate","high","very_high") and therefore provides a stable, language-independent value.
Good Example (Robust Automation):
This automation triggers only if the volatility is classified as high or very_high, respecting your central settings and working independently of the system language.
Show YAML: Good Example (Robust Automation)
automation:
- alias: "Enable battery optimization only on volatile days"
trigger:
- platform: template
value_template: >
{{ state_attr('sensor.<home_name>_today_s_price_volatility', 'price_volatility') in ['high', 'very_high'] }}
action:
- service: input_boolean.turn_on
entity_id: input_boolean.battery_optimization_enabled
Avoid Hard-Coding Numeric Thresholds
You might be tempted to use the numeric price_coefficient_variation_% attribute directly in your automations. This is not recommended.
- Why? The integration provides central configuration options for the volatility thresholds. By using the classified
price_volatilityattribute, your automations automatically adapt if you decide to change what you consider "high" volatility (e.g., changing the threshold from 30% to 35%). Hard-coding values means you would have to find and update them in every single automation.
Bad Example (Brittle Automation): This automation uses a hard-coded value. If you later change the "High" threshold in the integration's options to 35%, this automation will not respect that change and might trigger at the wrong time.
Show YAML: Bad Example (Brittle Automation)
automation:
- alias: "Brittle - Enable battery optimization"
trigger:
#
# BAD: Avoid hard-coding numeric values
#
- platform: numeric_state
entity_id: sensor.<home_name>_today_s_price_volatility
attribute: price_coefficient_variation_%
above: 30
action:
- service: input_boolean.turn_on
entity_id: input_boolean.battery_optimization_enabled
By following the "Good Example", your automations become simpler, more readable, and much easier to maintain.
Typical Price Band Statistics (IQR)
In addition to the CV-based volatility level, every volatility sensor provides typical price band statistics as attributes. These are derived from the IQR (Interquartile Range) — the spread of the middle 50% of prices — making them more robust to isolated price spikes than the CV.
| Metric | CV (state) | IQR attributes |
|---|---|---|
| Sensitive to spikes? | ✅ Yes — spikes inflate CV | ❌ No — IQR ignores the outer 25% |
| Use for optimization? | "Is today worth optimizing?" | "How wide is the core price band?" |
| Best for | Triggering battery/EV charging strategies | Understanding price structure |
The price_typical_spread_% attribute (IQR as a percentage of the median) tells you how wide the core price band is relative to the median. Even on a high-CV day with isolated spikes, a low price_typical_spread_% means most of the day has stable prices — only a few intervals are outliers.
The price_spike_count attribute (Tukey fence method: Q25 − 1.5×IQR to Q75 + 1.5×IQR) tells you how many intervals fall outside the normal range. A high price_spike_count day with a high CV is a classic "spiky" day: mostly stable prices with a few expensive or cheap peaks.
Price Rank Sensors (Percentile Rank)
The price rank sensors answer the simple question: "Is this price cheap or expensive compared to the rest of the day?"
Unlike the volatility sensors (which measure the shape of the entire price distribution), price rank sensors place a specific price within that distribution — technically its percentile rank. A value of 0% means cheapest interval of the reference set, while a value near 99% means most expensive.
Each sensor ranks a different subject price against a reference window:
- Subject — Which price is being ranked: current interval, next interval, previous interval, or the rolling hourly average
- Reference window — Which pool of slots to compare against: today only, tomorrow only, or today+tomorrow combined
How It Works (Percentile Rank Formula)
Price rank (percentile rank) = (number of intervals strictly cheaper than subject) ÷ total intervals × 100
The cheapest interval always returns 0% — you can use state == 0 to detect the absolute cheapest moment.
:::note 100% is never reached By design, the most expensive interval of the day will never show 100%. The formula counts how many intervals are strictly cheaper than the subject price. For the daily maximum, every other interval is cheaper — but the interval itself is not counted. With 96 quarter-hour intervals per day, the maximum value is 95 ÷ 96 × 100 = 99.0%.
This means:
state == 0→ cheapest interval of the reference set ✅state == 100→ never true ❌state >= 99→ most expensive interval of the day ✅ (use this instead) :::
Available Sensors
Current interval (price of the active quarter-hour):
| Sensor | Reference Set | Enabled by Default |
|---|---|---|
| Current Price Rank (Today) | Today's 96 quarter-hour intervals | ✅ Yes |
| Current Price Rank (Tomorrow) | Tomorrow's 96 intervals (once avail.) | ❌ No |
| Current Price Rank (Today+Tomorrow) | Combined pool (up to 192 intervals) | ❌ No |
Next interval (price of the upcoming quarter-hour):
| Sensor | Reference Set | Enabled by Default |
|---|---|---|
| Next Price Rank (Today) | Today's 96 quarter-hour intervals | ❌ No |
| Next Price Rank (Today+Tomorrow) | Combined pool (up to 192 intervals) | ❌ No |
Previous interval (price of the just-ended quarter-hour):
| Sensor | Reference Set | Enabled by Default |
|---|---|---|
| Previous Price Rank (Today) | Today's 96 quarter-hour intervals | ❌ No |
| Previous Price Rank (Today+Tomorrow) | Combined pool (up to 192 intervals) | ❌ No |
Rolling hourly average (5-interval window, ~1 hour):
| Sensor | Reference Set | Enabled by Default |
|---|---|---|
| ⌀ Hourly Price Current Rank (Today) | Today's 96 quarter-hour intervals | ❌ No |
| ⌀ Hourly Price Current Rank (Today+Tomorrow) | Combined pool (up to 192 intervals) | ❌ No |
| ⌀ Hourly Price Next Rank (Today) | Today's 96 quarter-hour intervals | ❌ No |
| ⌀ Hourly Price Next Rank (Today+Tomorrow) | Combined pool (up to 192 intervals) | ❌ No |
Key Attributes
All price rank sensors share most of these attributes. The price attribute key reflects the subject:
| Attribute | Description | Subject |
|---|---|---|
current_price |
The price being ranked (current interval) | Current interval |
next_price |
The price being ranked (next interval) | Next interval |
previous_price |
The price being ranked (previous interval) | Previous interval |
current_hour_avg_price |
The rolling average being ranked (current hour) | Current hour avg |
next_hour_avg_price |
The rolling average being ranked (next hour) | Next hour avg |
prices_below_count |
How many reference intervals are strictly cheaper | All sensors |
interval_count |
Total intervals in the reference set | All sensors |
reference_min |
The cheapest price in the reference set | All sensors |
reference_max |
The most expensive price in the reference set | All sensors |
reference_mean |
Average price of the reference set | All sensors |
When to Use Which Sensor
- Current (Today) — Same-day scheduling. "Is the active quarter-hour within the cheapest 25% of today?"
- Next (Today) — Prepare for the next interval. "Should I pre-heat now so the device runs in the coming cheap slot?"
- Current (Today+Tomorrow) — Broadest view for flexible tasks. "Is this among the cheapest moments of a 48-hour window?"
- Current (Tomorrow) — Decide whether to wait until tomorrow. "Is today's price worse than what tomorrow offers?"
- ⌀ Hourly Current (Today) — For tasks that take about an hour. "Is this hour cheap enough to start a 60-minute cycle?"
- ⌀ Hourly Next (Today) — One-hour look-ahead. "Will the upcoming hour be cheap enough to start now?"
Usage in Automations
Show YAML: Start dishwasher in bottom quartile
automation:
- alias: "Start dishwasher at cheapest time of day"
trigger:
- platform: numeric_state
entity_id: sensor.<home_name>_current_price_rank_today
below: 25
condition:
- condition: state
entity_id: binary_sensor.<home_name>_best_price_period
state: "on"
action:
- service: switch.turn_on
entity_id: switch.dishwasher
Show YAML: Postpone task if tomorrow is cheaper
automation:
- alias: "Skip charging tonight if tomorrow is cheaper"
trigger:
- platform: time
at: "21:00:00"
condition:
# Only postpone if tomorrow's cheapest quartile is better than the current price
- condition: template
value_template: >
{{ states('sensor.<home_name>_current_price_rank_tomorrow') | float(100) < 25 }}
action:
- service: input_boolean.turn_off
entity_id: input_boolean.ev_charge_tonight
Show YAML: Avoid running devices at the most expensive time of day
automation:
- alias: "Pause non-essential devices at peak price"
trigger:
- platform: numeric_state
entity_id: sensor.<home_name>_current_price_rank_today
above: 99 # 100 is never reached — use >= 99 to catch the daily maximum
action:
- service: switch.turn_off
entity_id: switch.dishwasher
Show YAML: Pre-heat when the next interval is cheap
automation:
- alias: "Pre-heat if next interval is top quartile cheapest"
trigger:
- platform: time_pattern
minutes: "/15"
condition:
- condition: numeric_state
entity_id: sensor.<home_name>_next_price_rank_today
below: 25
action:
- service: climate.set_hvac_mode
entity_id: climate.living_room
data:
hvac_mode: heat