feat(docs): enhance period calculation documentation and add detailed guides for best/peak price periods

This commit is contained in:
Julian Pawlowski 2025-11-10 11:54:06 +00:00
parent 817658f230
commit 3dc1a49465
5 changed files with 783 additions and 5 deletions

View file

@ -1169,6 +1169,32 @@ Public entry points → direct helpers (call order) → pure utilities. Prefix p
**Translation sync:** When updating `/translations/en.json`, update ALL language files (`de.json`, etc.) with same keys (placeholder values OK). **Translation sync:** When updating `/translations/en.json`, update ALL language files (`de.json`, etc.) with same keys (placeholder values OK).
**Documentation language:**
- **CRITICAL**: All user-facing documentation (`README.md`, `/docs/user/`, `/docs/development/`) MUST be written in **English**
- **Code comments**: Always use English for code comments and docstrings
- **UI translations**: Multi-language support exists in `/translations/` and `/custom_translations/` (de, en, nb, nl, sv) for UI strings only
- **Why English-only docs**: Ensures maintainability, accessibility to global community, and consistency with Home Assistant ecosystem
- **Entity names in documentation**: Use **translated display names** from `/translations/en.json` (what users see), not internal entity IDs. Example: "Best Price Period" not "sensor.tibber_home_best_price_period" (add entity ID as comment if needed for clarity).
**Examples and use cases:**
- **Regional context**: Tibber operates primarily in European markets (Norway, Sweden, Germany, Netherlands). Examples should reflect European context:
- ✅ Use cases: Heat pump, dishwasher, washing machine, electric vehicle charging, water heater
- ✅ Appliances: Common in European homes (heat pumps for heating/cooling, instantaneous water heaters)
- ✅ Energy patterns: European pricing structures (often lower overnight rates, higher daytime rates)
- ✅ Optimization strategies: ECO programs with long run times, heat pump defrost cycles, smart water heating
- ❌ Avoid: US-centric examples (central air conditioning as primary cooling, 240V dryers, different voltage standards)
- ❌ Avoid: US appliance behavior assumptions (e.g., dishwashers requiring hot water connection due to 120V limitations)
- **Technical differences**: European appliances operate differently due to 230V power supply:
- Dishwashers: Built-in heaters, ECO programs (long duration, low energy), cold water connection standard
- Washing machines: Fast heating cycles, higher temperature options (60°C, 90°C programs common)
- Heat pumps: Primary heating source (not just cooling), complex defrost cycles, weather-dependent operation
- **Units and formats**: Use European conventions where appropriate:
- Prices: ct/kWh or øre/kWh (as provided by Tibber API)
- Time: 24-hour format (00:00-23:59)
- Dates: ISO 8601 format (YYYY-MM-DD)
**Language style and tone:** **Language style and tone:**
- **Informal address**: Always use informal "you" forms (German: "du" not "Sie", Dutch: "je/jouw" not "u/uw"). This applies to all translations. - **Informal address**: Always use informal "you" forms (German: "du" not "Sie", Dutch: "je/jouw" not "u/uw"). This applies to all translations.

View file

@ -15,6 +15,7 @@ A Home Assistant integration that provides advanced price information and rating
## 📖 Documentation ## 📖 Documentation
- **[User Guide](docs/user/)** - Installation, configuration, and usage guides - **[User Guide](docs/user/)** - Installation, configuration, and usage guides
- **[Period Calculation](docs/user/period-calculation.md)** - How Best/Peak Price periods are calculated
- **[Developer Guide](docs/development/)** - Contributing, architecture, and release process - **[Developer Guide](docs/development/)** - Contributing, architecture, and release process
- **[Changelog](https://github.com/jpawlowski/hass.tibber_prices/releases)** - Release history and notes - **[Changelog](https://github.com/jpawlowski/hass.tibber_prices/releases)** - Release history and notes
@ -126,8 +127,8 @@ The integration provides **30+ sensors** across different categories. Key sensor
| Entity | Description | | Entity | Description |
| -------------------------- | -------------------------------------------------------------- | | -------------------------- | -------------------------------------------------------------- |
| Peak Price Interval | ON when current interval is in the highest 20% of day's prices | | Peak Price Period | ON when in a detected peak price period ([how it works](docs/user/period-calculation.md)) |
| Best Price Interval | ON when current interval is in the lowest 20% of day's prices | | Best Price Period | ON when in a detected best price period ([how it works](docs/user/period-calculation.md)) |
| Tibber API Connection | Connection status to Tibber API | | Tibber API Connection | Connection status to Tibber API |
| Tomorrow's Data Available | Whether tomorrow's price data is available | | Tomorrow's Data Available | Whether tomorrow's price data is available |
@ -151,16 +152,18 @@ The following sensors are available but disabled by default. Enable them in `Set
## Automation Examples ## Automation Examples
> **Note:** See the [full automation examples guide](docs/user/automation-examples.md) for more advanced recipes.
### Run Appliances During Cheap Hours ### Run Appliances During Cheap Hours
Use the `binary_sensor.tibber_best_price_interval` to automatically start appliances during the cheapest 15-minute periods: Use the `binary_sensor.tibber_best_price_period` to automatically start appliances during detected best price periods:
```yaml ```yaml
automation: automation:
- alias: "Run Dishwasher During Cheap Hours" - alias: "Run Dishwasher During Cheap Hours"
trigger: trigger:
- platform: state - platform: state
entity_id: binary_sensor.tibber_best_price_interval entity_id: binary_sensor.tibber_best_price_period
to: "on" to: "on"
condition: condition:
- condition: time - condition: time
@ -172,6 +175,8 @@ automation:
entity_id: switch.dishwasher entity_id: switch.dishwasher
``` ```
> **Learn more:** The [period calculation guide](docs/user/period-calculation.md) explains how Best/Peak Price periods are identified and how you can configure filters (flexibility, minimum volatility, price level filters with gap tolerance).
### Notify on Extremely High Prices ### Notify on Extremely High Prices
Get notified when prices reach the VERY_EXPENSIVE level: Get notified when prices reach the VERY_EXPENSIVE level:

View file

@ -6,6 +6,7 @@ Welcome to Tibber Prices! This integration provides enhanced electricity price d
- **[Installation](installation.md)** - How to install via HACS and configure the integration - **[Installation](installation.md)** - How to install via HACS and configure the integration
- **[Configuration](configuration.md)** - Setting up your Tibber API token and price thresholds - **[Configuration](configuration.md)** - Setting up your Tibber API token and price thresholds
- **[Period Calculation](period-calculation.md)** - How Best/Peak Price periods are calculated and configured
- **[Sensors](sensors.md)** - Available sensors, their states, and attributes - **[Sensors](sensors.md)** - Available sensors, their states, and attributes
- **[Services](services.md)** - Custom services and how to use them - **[Services](services.md)** - Custom services and how to use them
- **[Automation Examples](automation-examples.md)** - Ready-to-use automation recipes - **[Automation Examples](automation-examples.md)** - Ready-to-use automation recipes
@ -24,7 +25,7 @@ Welcome to Tibber Prices! This integration provides enhanced electricity price d
- **Quarter-hourly precision** - 15-minute intervals for accurate price tracking - **Quarter-hourly precision** - 15-minute intervals for accurate price tracking
- **Statistical analysis** - Trailing/leading 24h averages for context - **Statistical analysis** - Trailing/leading 24h averages for context
- **Price ratings** - LOW/NORMAL/HIGH classification based on your thresholds - **Price ratings** - LOW/NORMAL/HIGH classification based on your thresholds
- **Best hour detection** - Automatic detection of cheapest/peak hours - **Best/Peak hour detection** - Automatic detection of cheapest/peak periods with configurable filters ([learn how](period-calculation.md))
- **ApexCharts integration** - Custom services for beautiful price charts - **ApexCharts integration** - Custom services for beautiful price charts
- **Multi-currency support** - EUR, NOK, SEK with proper minor units (ct, øre, öre) - **Multi-currency support** - EUR, NOK, SEK with proper minor units (ct, øre, öre)

View file

@ -0,0 +1,734 @@
# Period Calculation
A detailed explanation of how Best Price and Peak Price periods are calculated and how you can influence the calculation with configuration options.
## Table of Contents
- [Overview](#overview)
- [Calculation Flow](#calculation-flow)
- [Configuration Options in Detail](#configuration-options-in-detail)
- [Filter Pipeline](#filter-pipeline)
- [Gap Tolerance for Level Filters](#gap-tolerance-for-level-filters)
- [Relaxation Mechanism](#relaxation-mechanism)
- [Practical Examples](#practical-examples)
- [Troubleshooting](#troubleshooting)
---
## Overview
### What are Price Periods?
The integration automatically calculates **Best Price Periods** (cheap time windows) and **Peak Price Periods** (expensive time windows) for each day. These periods help you:
- **Best Price**: Shift electricity consumption to cheap times (e.g., charge electric car, run dishwasher, washing machine, heat pump water heater)
- **Peak Price**: Avoid high consumption during expensive times (e.g., reduce heating temporarily, defer non-essential loads)
### Basic Principle
The calculation happens in **two main steps**:
1. **Period Identification**: Find contiguous time ranges that differ significantly from the daily average
2. **Filter Application**: Apply various filters to keep only relevant periods
Both steps can be influenced by configuration options.
---
## Calculation Flow
### Step 1: Data Preparation
**What happens:**
- Fetch all price intervals for today (96 x 15-minute intervals = 24 hours)
- Calculate daily average price
- Calculate trailing 24h average for each interval
**Example:**
```
Today: 96 intervals from 00:00 to 23:59
Average price today: 25.5 ct/kWh
```
### Step 2: Period Identification (Flexibility)
**What happens:**
- Search for contiguous intervals that are **significantly cheaper** (Best Price) or **more expensive** (Peak Price) than the average
- "Significant" is defined by the **flexibility** setting
**Configuration:**
- `best_price_flex` (default: 15%) - How much cheaper than average?
- `peak_price_flex` (default: -15%) - How much more expensive than average?
**Example (Best Price with 15% flexibility):**
```
Average price: 25.0 ct/kWh
Flexibility: -15%
Threshold: 25.0 - (25.0 × 0.15) = 21.25 ct/kWh
Intervals that cost ≤ 21.25 ct/kWh are grouped into periods:
00:00-00:15: 20.5 ct ✓ │
00:15-00:30: 19.8 ct ✓ ├─ Period 1 (1h)
00:30-00:45: 21.0 ct ✓ │
00:45-01:00: 20.2 ct ✓ │
01:00-01:15: 26.5 ct ✗ (too expensive, period ends)
```
### Step 3: Minimum Period Length
**What happens:**
- Periods that are too short are discarded (not practical to use)
**Configuration:**
- `best_price_min_period_length` (default: 60 minutes)
- `peak_price_min_period_length` (default: 60 minutes)
**Example:**
```
Found periods:
- 00:00-01:00 (60 min) ✓ Keep
- 03:00-03:30 (30 min) ✗ Discard (too short)
- 14:00-15:15 (75 min) ✓ Keep
```
### Step 4: Minimum Distance from Average
**What happens:**
- Periods must have **additional** distance from the daily average beyond flexibility
- Prevents marking "almost normal" prices as "Best/Peak" on days with small price spread
**Configuration:**
- `best_price_min_distance_from_avg` (default: 2%) - Additional distance below average
- `peak_price_min_distance_from_avg` (default: 2%) - Additional distance above average
**Example (Best Price):**
```
Daily average: 25.0 ct/kWh
Flexibility threshold: 21.25 ct/kWh (from Step 2)
Minimum distance: 2%
Final check for each interval:
1. Price ≤ flexibility threshold? (21.25 ct)
2. AND price ≤ average × (1 - 0.02)? (24.5 ct)
Interval with 23.0 ct:
✗ Meets flexibility (23.0 > 21.25)
✓ Meets minimum distance (23.0 < 24.5)
→ REJECTED (both conditions must be met)
```
### Step 5: Filter Application
**What happens:**
- Apply optional filters (volatility, price level)
- See [Filter Pipeline](#filter-pipeline) for details
---
## Configuration Options in Detail
### Best Price Period Settings
| Option | Default | Description | Acts in Step |
|--------|---------|-------------|--------------|
| `best_price_flex` | 15% | How much cheaper than average must a period be? | 2 (Identification) |
| `best_price_min_period_length` | 60 min | Minimum length of a period | 3 (Length filter) |
| `best_price_min_distance_from_avg` | 2% | Additional minimum distance below daily average | 4 (Quality filter) |
| `best_price_min_volatility` | LOW | Minimum volatility within the period (optional) | 5 (Volatility filter) |
| `best_price_max_level` | ANY | Maximum price level (optional, e.g., only CHEAP or better) | 5 (Level filter) |
| `best_price_max_level_gap_count` | 0 | Tolerance for level deviations (see [Gap Tolerance](#gap-tolerance-for-level-filters)) | 5 (Level filter) |
| `enable_min_periods_best` | Off | Enables relaxation mechanism | - (Relaxation) |
| `min_periods_best` | 2 | Minimum number of periods to achieve | - (Relaxation) |
| `relaxation_step_best` | 25% | Step size for filter relaxation | - (Relaxation) |
### Peak Price Period Settings
| Option | Default | Description | Acts in Step |
|--------|---------|-------------|--------------|
| `peak_price_flex` | -15% | How much more expensive than average must a period be? | 2 (Identification) |
| `peak_price_min_period_length` | 60 min | Minimum length of a period | 3 (Length filter) |
| `peak_price_min_distance_from_avg` | 2% | Additional minimum distance above daily average | 4 (Quality filter) |
| `peak_price_min_volatility` | LOW | Minimum volatility within the period (optional) | 5 (Volatility filter) |
| `peak_price_min_level` | ANY | Minimum price level (optional, e.g., only EXPENSIVE or higher) | 5 (Level filter) |
| `peak_price_max_level_gap_count` | 0 | Tolerance for level deviations (see [Gap Tolerance](#gap-tolerance-for-level-filters)) | 5 (Level filter) |
| `enable_min_periods_peak` | Off | Enables relaxation mechanism | - (Relaxation) |
| `min_periods_peak` | 2 | Minimum number of periods to achieve | - (Relaxation) |
| `relaxation_step_peak` | 25% | Step size for filter relaxation | - (Relaxation) |
---
## Filter Pipeline
After basic period identification (Steps 1-4), two optional **additional filters** can be applied:
### Volatility Filter
**Purpose:** Only show periods when the price spread within the period is large enough.
**Use case:**
- **Best Price**: "I only want to optimize when it's really worth it" (high volatility)
- **Peak Price**: "Only warn me about large price swings" (high volatility)
**How it works:**
```
Period: 00:00-01:00
Intervals: 20.5 | 19.8 | 21.0 | 20.2 ct/kWh
Min: 19.8 ct, Max: 21.0 ct
Volatility (spread): 21.0 - 19.8 = 1.2 ct/kWh
Volatility thresholds:
- LOW: < 5.0 ct This period: LOW
- MODERATE: 5-15 ct
- HIGH: 15-30 ct
- VERY_HIGH: ≥ 30 ct
best_price_min_volatility = "MODERATE" (5 ct)
→ Period is REJECTED (1.2 ct < 5.0 ct)
```
**Configuration:**
- `best_price_min_volatility`: `low` | `moderate` | `high` | `very_high`
- `peak_price_min_volatility`: `low` | `moderate` | `high` | `very_high`
**Default:** `low` (filter disabled, all periods shown)
### Level Filter (Price Level)
**Purpose:** Only show periods that are actually cheap/expensive in absolute terms, not just relative to the daily average.
**Use case:**
- **Best Price**: "Only show best price when there's at least one CHEAP interval" (not just "less expensive than usual today")
- **Peak Price**: "Only show peak price when there's at least one EXPENSIVE interval" (not just "more expensive than average")
**Price levels (from Tibber API):**
- `VERY_CHEAP` (-2)
- `CHEAP` (-1)
- `NORMAL` (0)
- `EXPENSIVE` (+1)
- `VERY_EXPENSIVE` (+2)
**How it works (Best Price example):**
```
best_price_max_level = "CHEAP"
Period: 00:00-01:00
Intervals with levels:
00:00: 20.5 ct → CHEAP ✓
00:15: 19.8 ct → VERY_CHEAP ✓
00:30: 21.0 ct → NORMAL ✗
00:45: 20.2 ct → CHEAP ✓
Filter logic (without gap tolerance):
→ Does the period have at least ONE interval with level ≤ CHEAP?
→ YES (three intervals are CHEAP or better)
→ Period is KEPT
But: One NORMAL interval in the middle!
→ Without gap tolerance: Period is split into two parts
→ With gap tolerance: Period stays together (see next section)
```
**Configuration:**
- `best_price_max_level`: `any` | `very_cheap` | `cheap` | `normal` | `expensive`
- `peak_price_min_level`: `any` | `expensive` | `normal` | `cheap` | `very_cheap`
**Default:** `any` (filter disabled)
---
## Gap Tolerance for Level Filters
### Problem Without Gap Tolerance
When you activate a level filter (e.g., `best_price_max_level = "CHEAP"`), periods are **strictly filtered**:
```
Period: 00:00-02:00 (2 hours)
Intervals:
00:00-01:30: CHEAP, CHEAP, CHEAP, CHEAP, CHEAP, CHEAP
01:30-01:45: NORMAL ← A single deviating interval!
01:45-02:00: CHEAP
Without gap tolerance:
→ Period is split into TWO periods:
1. 00:00-01:30 (1.5h)
2. 01:45-02:00 (0.25h) ✗ too short, discarded!
→ Result: Only 1.5h best price instead of 2h
```
### Solution: Gap Tolerance
**Gap tolerance** allows a configurable number of intervals that deviate by **exactly one level step** from the required level.
**How it works:**
1. **"Gap" definition:** An interval that deviates by exactly 1 level step
```
Best Price filter: CHEAP (-1)
NORMAL (0) is +1 step → GAP ✓
EXPENSIVE (+1) is +2 steps → NOT A GAP, too far away
```
2. **Gap counting:** Max X gaps allowed per period (configurable: 0-8)
3. **Minimum distance between gaps:** Gaps must not be too close together
```
Dynamic formula: max(2, (interval_count / max_gaps) / 2)
Example: 16 intervals, max 2 gaps allowed
→ Minimum distance: max(2, (16/2)/2) = max(2, 4) = 4 intervals
CHEAP, CHEAP, CHEAP, CHEAP, NORMAL, CHEAP, CHEAP, CHEAP, NORMAL, CHEAP
↑ GAP1 ↑ GAP2
└─────── 4 intervals ──────────────┘
→ OK, minimum distance maintained
```
4. **25% cap:** Maximum 25% of a period's intervals can be gaps
```
Period: 12 intervals, user configured 5 gaps
→ Effective: min(5, 12/4) = min(5, 3) = 3 gaps allowed
```
5. **Minimum period length:** Gap tolerance only applies to periods ≥ 1.5h (6 intervals)
```
Period < 1.5h: Strict filtering (0 tolerance)
Period ≥ 1.5h: Gap tolerance as configured
```
### Gap Cluster Splitting
If a period would still be rejected **despite gap tolerance** (too many gaps or too dense), the integration tries to **intelligently split** it:
```
Period: 00:00-04:00 (16 intervals)
CHEAP, CHEAP, CHEAP, NORMAL, NORMAL, NORMAL, CHEAP, CHEAP, ..., CHEAP
└─ Gap cluster (3×) ─┘
Gap cluster = 2+ consecutive deviating intervals
→ Splitting at gap cluster:
1. 00:00-00:45 (3 intervals) ✗ too short
2. 01:30-04:00 (10 intervals) ✓ kept
→ Result: 2.5h best price instead of complete rejection
```
### Configuration
**Best Price:**
```yaml
best_price_max_level: "cheap" # Enable level filter
best_price_max_level_gap_count: 2 # Allow 2 NORMAL intervals per period
```
**Peak Price:**
```yaml
peak_price_min_level: "expensive" # Enable level filter
peak_price_max_level_gap_count: 1 # Allow 1 NORMAL interval per period
```
**Default:** `0` (no tolerance, strict filtering)
### Example Scenarios
#### Scenario 1: Conservative (0 gaps)
```yaml
best_price_max_level: "cheap"
best_price_max_level_gap_count: 0 # Default
```
**Behavior:**
- Every interval MUST be CHEAP or better
- A single NORMAL interval → period is split
**Good for:** Users who want absolute price guarantees
#### Scenario 2: Moderate (2-3 gaps)
```yaml
best_price_max_level: "cheap"
best_price_max_level_gap_count: 2
```
**Behavior:**
- Up to 2 NORMAL intervals per period tolerated
- Minimum distance between gaps dynamically calculated
- 25% cap protects against too many gaps
**Good for:** Most users - balance between quality and period length
#### Scenario 3: Aggressive (5-8 gaps)
```yaml
best_price_max_level: "cheap"
best_price_max_level_gap_count: 5
```
**Behavior:**
- Up to 5 NORMAL intervals (but max 25% of period)
- Longer periods possible
- Quality may suffer (more "not-quite-so-cheap" intervals)
**Good for:** Users with flexible devices that need long run times
---
## Relaxation Mechanism
If **too few periods** are found despite all filters, the integration can automatically **gradually relax** filters.
### When is Relaxation Applied?
Only when **both conditions** are met:
1. `enable_min_periods_best/peak` is enabled
2. Fewer than `min_periods_best/peak` periods found
### Relaxation Levels
The integration tries to relax filters in this order:
#### Level 1: Relax Flexibility
```
Original: best_price_flex = 15%
Step 1: 15% + (15% × 0.25) = 18.75%
Step 2: 18.75% + (18.75% × 0.25) = 23.44%
Step 3: ...
```
**Calculation:** `new_flexibility = old_flexibility × (1 + relaxation_step / 100)`
#### Level 2: Disable Volatility Filter
```
If flexibility relaxation isn't enough:
→ best_price_min_volatility = "any" (filter off)
```
#### Level 3: Disable All Filters
```
If still too few periods:
→ Volatility = "any"
→ Level filter = "any"
→ Only flexibility and minimum length active
```
### Relaxation Status
The sensors show the **relaxation status** as an attribute:
```yaml
Best Price Period: # sensor.tibber_home_best_price_period
state: "on"
attributes:
relaxation_level: "volatility_any" # Volatility filter was disabled
```
**Possible values:**
- `none` - No relaxation, normal filters
- `volatility_any` - Volatility filter disabled
- `all_filters_off` - All optional filters disabled
### Example Configuration
```yaml
# Best Price: Try to find at least 2 periods
enable_min_periods_best: true
min_periods_best: 2
relaxation_step_best: 25 # 25% per step
best_price_flex: 15
best_price_min_volatility: "moderate"
```
**Process on a day with little price spread:**
1. Try with 15% flex + MODERATE volatility → 0 periods
2. Relax to 18.75% flex → 1 period
3. Relax to 23.44% flex → 1 period (still < 2)
4. Disable volatility filter → 2 periods ✓
**Result:** User sees 2 periods with `relaxation_level: "volatility_any"`
---
## Practical Examples
### Example 1: Standard Configuration (Best Price)
**Configuration:**
```yaml
best_price_flex: 15
best_price_min_period_length: 60
best_price_min_distance_from_avg: 2
best_price_min_volatility: "low" # Filter disabled
best_price_max_level: "any" # Filter disabled
```
**Daily prices:**
```
Average: 25.0 ct/kWh
00:00-02:00: 19-21 ct (cheap)
06:00-08:00: 28-30 ct (expensive)
12:00-14:00: 24-26 ct (normal)
18:00-20:00: 20-22 ct (cheap)
```
**Calculation:**
1. Flexibility threshold: 25.0 - (25.0 × 0.15) = 21.25 ct
2. Minimum distance threshold: 25.0 × (1 - 0.02) = 24.5 ct
3. Both conditions: Price ≤ 21.25 ct
**Result:**
- ✓ 00:00-02:00 (19-21 ct, all ≤ 21.25)
- ✗ 06:00-08:00 (too expensive)
- ✗ 12:00-14:00 (24-26 ct, not cheap enough)
- ✓ 18:00-20:00 (20-22 ct, all ≤ 21.25)
**2 Best Price periods found!**
### Example 2: Strict Level Filter Without Gap Tolerance
**Configuration:**
```yaml
best_price_flex: 15
best_price_max_level: "cheap"
best_price_max_level_gap_count: 0 # No tolerance
```
**Period candidate:**
```
00:00-02:00:
00:00-01:30: CHEAP, CHEAP, CHEAP, CHEAP, CHEAP, CHEAP
01:30-01:45: NORMAL ← Deviation!
01:45-02:00: CHEAP
```
**Result:**
- ✗ Period is split into 00:00-01:30 and 01:45-02:00
- ✗ 01:45-02:00 too short (15 min < 60 min) discarded
- ✓ Only 00:00-01:30 (1.5h) remains
### Example 3: Level Filter With Gap Tolerance
**Configuration:**
```yaml
best_price_flex: 15
best_price_max_level: "cheap"
best_price_max_level_gap_count: 2 # 2 gaps allowed
```
**Period candidate (same as above):**
```
00:00-02:00:
00:00-01:30: CHEAP, CHEAP, CHEAP, CHEAP, CHEAP, CHEAP
01:30-01:45: NORMAL ← Gap (1 of 2 allowed)
01:45-02:00: CHEAP
```
**Gap tolerance check:**
- Gaps found: 1 (NORMAL)
- Max allowed: 2
- 25% cap: min(2, 8/4) = 2 (8 intervals)
- Minimum distance: N/A (only 1 gap)
**Result:**
- ✓ Period stays as a whole: 00:00-02:00 (2h)
- 1 NORMAL interval is tolerated
### Example 4: Gap Cluster Gets Split
**Configuration:**
```yaml
best_price_flex: 15
best_price_max_level: "cheap"
best_price_max_level_gap_count: 2
```
**Period candidate:**
```
00:00-04:00 (16 intervals):
00:00-01:00: CHEAP, CHEAP, CHEAP, CHEAP (4)
01:00-02:00: NORMAL, NORMAL, NORMAL, NORMAL (4) ← Gap cluster!
02:00-04:00: CHEAP, CHEAP, CHEAP, ..., CHEAP (8)
```
**Gap tolerance check:**
- Gaps found: 4 (too many)
- Max allowed: 2
- → Normal check fails
**Gap cluster splitting:**
- Detect cluster: 4× consecutive NORMAL intervals
- Split period at cluster boundaries:
1. 00:00-01:00 (4 intervals = 60 min) ✓
2. 02:00-04:00 (8 intervals = 120 min) ✓
**Result:**
- ✓ Two separate periods: 00:00-01:00 and 02:00-04:00
- Total 3h best price (instead of complete rejection)
### Example 5: Relaxation in Action
**Configuration:**
```yaml
enable_min_periods_best: true
min_periods_best: 2
relaxation_step_best: 25
best_price_flex: 10 # Very strict!
best_price_min_volatility: "high" # Very strict!
```
**Day with little price spread:**
```
Average: 25.0 ct/kWh
All prices between 23-27 ct (low volatility)
```
**Relaxation process:**
1. **Attempt 1:** 10% flex + HIGH volatility
```
Threshold: 22.5 ct
No period meets both conditions
→ 0 periods (< 2 required)
```
2. **Attempt 2:** 12.5% flex + HIGH volatility
```
Threshold: 21.875 ct
Still 0 periods
```
3. **Attempt 3:** Disable volatility filter
```
12.5% flex + ANY volatility
→ 1 period found (< 2)
```
4. **Attempt 4:** 15.625% flex + ANY volatility
```
Threshold: 21.09 ct
→ 2 periods found ✓
```
**Result:**
- Sensor shows 2 periods with `relaxation_level: "volatility_any"`
- User knows: "Filters were relaxed to reach minimum count"
---
## Troubleshooting
### Problem: No Periods Found
**Possible causes:**
1. **Too strict flexibility**
```
best_price_flex: 5 # Only 5% cheaper than average
```
**Solution:** Increase to 10-15%
2. **Too strict level filter without gap tolerance**
```
best_price_max_level: "very_cheap"
best_price_max_level_gap_count: 0
```
**Solution:** Relax level to "cheap" or enable gap tolerance (1-2)
3. **Too high volatility requirement**
```
best_price_min_volatility: "very_high"
```
**Solution:** Reduce to "moderate" or "low"
4. **Too long minimum period length**
```
best_price_min_period_length: 180 # 3 hours
```
**Solution:** Reduce to 60-90 minutes
5. **Day with very small price spread**
```
All prices between 24-26 ct (hardly any differences)
```
**Solution:** Enable relaxation mechanism:
```yaml
enable_min_periods_best: true
min_periods_best: 1
```
### Problem: Too Many Periods
**Solution:** Make filters stricter:
```yaml
best_price_flex: 20 # Reduce to 10-15
best_price_min_volatility: "moderate" # Require higher volatility
best_price_max_level: "cheap" # Only truly cheap times
```
### Problem: Periods Are Too Short
**Solution:** Increase minimum length and use gap tolerance:
```yaml
best_price_min_period_length: 90 # 1.5 hours
best_price_max_level_gap_count: 2 # Tolerate deviations
```
### Problem: Periods With "Mediocre" Prices
**Solution:** Increase minimum distance:
```yaml
best_price_min_distance_from_avg: 5 # Must be 5% below average
```
### Problem: Relaxation Applied Too Aggressively
**Solution:** Reduce step size:
```yaml
relaxation_step_best: 10 # Smaller steps (instead of 25)
```
Or disable relaxation completely:
```yaml
enable_min_periods_best: false
```
### Problem: Gap Tolerance Not Working As Expected
**Possible causes:**
1. **Period too short (< 1.5h)**
```
Gap tolerance only applies to periods ≥ 1.5h
```
**Solution:** Reduce `best_price_min_period_length` or adjust flexibility
2. **25% cap limiting effective gaps**
```
Period: 8 intervals, configured 4 gaps
→ Effective: min(4, 8/4) = 2 gaps
```
**Solution:** Accept limitation or relax level filter
3. **Gaps too close together**
```
Minimum distance between gaps not maintained
```
**Solution:** Increase gap count or accept splitting
---
## Further Documentation
- **[Configuration Guide](configuration.md)** - UI screenshots and step-by-step guide
- **[Sensors](sensors.md)** - All available sensors and attributes
- **[Automation Examples](automation-examples.md)** - Practical automation recipes with periods
- **[Developer Documentation](../development/)** - Code architecture and algorithm details
---
**Questions or feedback?** Open an [issue on GitHub](https://github.com/jpawlowski/hass.tibber_prices/issues)!

View file

@ -2,6 +2,18 @@
> **Note:** This guide is under construction. For now, please refer to the [main README](../../README.md) for available sensors. > **Note:** This guide is under construction. For now, please refer to the [main README](../../README.md) for available sensors.
## Binary Sensors
### Best Price Period & Peak Price Period
These binary sensors indicate when you're in a detected best or peak price period. See the **[Period Calculation Guide](period-calculation.md)** for a detailed explanation of how these periods are calculated and configured.
**Quick overview:**
- **Best Price Period**: Turns ON during periods with significantly lower prices than the daily average
- **Peak Price Period**: Turns ON during periods with significantly higher prices than the daily average
Both sensors include rich attributes with period details, intervals, relaxation status, and more.
## Core Price Sensors ## Core Price Sensors
Coming soon... Coming soon...