Volatility Sensors
<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.
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
๐ฌ Comments are page-specific. For a new question or idea, open a dedicated Discussion on GitHub so it gets its own thread and proper visibility.