mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
feat(docs): enhance period calculation documentation and add detailed guides for best/peak price periods
This commit is contained in:
parent
817658f230
commit
3dc1a49465
5 changed files with 783 additions and 5 deletions
26
.github/copilot-instructions.md
vendored
26
.github/copilot-instructions.md
vendored
|
|
@ -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).
|
||||
|
||||
**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:**
|
||||
|
||||
- **Informal address**: Always use informal "you" forms (German: "du" not "Sie", Dutch: "je/jouw" not "u/uw"). This applies to all translations.
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -15,6 +15,7 @@ A Home Assistant integration that provides advanced price information and rating
|
|||
## 📖 Documentation
|
||||
|
||||
- **[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
|
||||
- **[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 |
|
||||
| -------------------------- | -------------------------------------------------------------- |
|
||||
| Peak Price Interval | ON when current interval is in the highest 20% of day's prices |
|
||||
| Best Price Interval | ON when current interval is in the lowest 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 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 |
|
||||
| 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
|
||||
|
||||
> **Note:** See the [full automation examples guide](docs/user/automation-examples.md) for more advanced recipes.
|
||||
|
||||
### 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
|
||||
automation:
|
||||
- alias: "Run Dishwasher During Cheap Hours"
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id: binary_sensor.tibber_best_price_interval
|
||||
entity_id: binary_sensor.tibber_best_price_period
|
||||
to: "on"
|
||||
condition:
|
||||
- condition: time
|
||||
|
|
@ -172,6 +175,8 @@ automation:
|
|||
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
|
||||
|
||||
Get notified when prices reach the VERY_EXPENSIVE level:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
- **[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
|
||||
- **[Services](services.md)** - Custom services and how to use them
|
||||
- **[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
|
||||
- **Statistical analysis** - Trailing/leading 24h averages for context
|
||||
- **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
|
||||
- **Multi-currency support** - EUR, NOK, SEK with proper minor units (ct, øre, öre)
|
||||
|
||||
|
|
|
|||
734
docs/user/period-calculation.md
Normal file
734
docs/user/period-calculation.md
Normal 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)!
|
||||
|
|
@ -2,6 +2,18 @@
|
|||
|
||||
> **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
|
||||
|
||||
Coming soon...
|
||||
|
|
|
|||
Loading…
Reference in a new issue