From cbbfadbf4fd04bd11051d1956759199d43df68a3 Mon Sep 17 00:00:00 2001 From: Julian Pawlowski Date: Sat, 11 Apr 2026 10:33:58 +0000 Subject: [PATCH] refactor(docs): restructure navigation and split large pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split sensors.md (1,693 lines) into 7 focused pages: - sensors-overview: binary sensors, core price, min/max, diagnostics - sensors-average: median/mean, automation examples, key attributes - sensors-ratings-levels: 3-level ratings + 5-level system - sensors-volatility: CV formula, 4 sensors, configuration - sensors-trends: outlook, trajectory, current/next, decision guide - sensors-timing: period timing state diagram + examples - sensors-energy-tax: energy/tax breakdown + use cases Extract relaxation deep-dive from period-calculation.md into dedicated period-relaxation.md. Remove duplicate ApexCharts section from automation-examples.md (cross-references chart-examples.md). Reorganize sidebar into semantic categories: - Sensors (7 pages), Price Periods (2), Dashboards & Charts (4), Reference (sensor-reference + actions) Update all cross-references across 10 pages, EntitySearch DOC_NAMES, and generator template for new page slugs. Impact: Users can find information faster with shorter, focused pages and a clearer navigation structure. No content was removed — only reorganized and deduplicated. --- docs/user/docs/actions.md | 2 +- docs/user/docs/automation-examples.md | 99 +- docs/user/docs/chart-examples.md | 2 +- docs/user/docs/concepts.md | 4 +- docs/user/docs/configuration.md | 4 +- docs/user/docs/dynamic-icons.md | 2 +- docs/user/docs/glossary.md | 2 +- docs/user/docs/icon-colors.md | 2 +- docs/user/docs/intro.md | 2 +- docs/user/docs/period-calculation.md | 110 +-- docs/user/docs/period-relaxation.md | 129 +++ docs/user/docs/sensor-reference.md | 120 +-- docs/user/docs/sensors-average.md | 194 ++++ docs/user/docs/sensors-energy-tax.md | 110 +++ docs/user/docs/sensors-overview.md | 183 ++++ docs/user/docs/sensors-ratings-levels.md | 140 +++ docs/user/docs/sensors-timing.md | 95 ++ docs/user/docs/sensors-trends.md | 266 ++++++ docs/user/docs/sensors-volatility.md | 99 ++ docs/user/docs/sensors.md | 1038 --------------------- docs/user/sidebars.ts | 32 +- docs/user/src/components/EntitySearch.tsx | 14 +- scripts/docs/generate-sensor-reference | 47 +- 23 files changed, 1373 insertions(+), 1323 deletions(-) create mode 100644 docs/user/docs/period-relaxation.md create mode 100644 docs/user/docs/sensors-average.md create mode 100644 docs/user/docs/sensors-energy-tax.md create mode 100644 docs/user/docs/sensors-overview.md create mode 100644 docs/user/docs/sensors-ratings-levels.md create mode 100644 docs/user/docs/sensors-timing.md create mode 100644 docs/user/docs/sensors-trends.md create mode 100644 docs/user/docs/sensors-volatility.md delete mode 100644 docs/user/docs/sensors.md diff --git a/docs/user/docs/actions.md b/docs/user/docs/actions.md index cdcfbb2..13e4b37 100644 --- a/docs/user/docs/actions.md +++ b/docs/user/docs/actions.md @@ -166,7 +166,7 @@ Returns data points like: } ``` -**Use case — Solar feed-in chart:** Overlay the energy price (what you earn by exporting) alongside the total price to visualize the best export windows. See [Sensors — Energy Price & Tax Breakdown](sensors.md#energy-price--tax-breakdown) for more use cases. +**Use case — Solar feed-in chart:** Overlay the energy price (what you earn by exporting) alongside the total price to visualize the best export windows. See [Energy & Tax Attributes](sensors-energy-tax.md) for more use cases. --- diff --git a/docs/user/docs/automation-examples.md b/docs/user/docs/automation-examples.md index 3eb59b0..ecc949f 100644 --- a/docs/user/docs/automation-examples.md +++ b/docs/user/docs/automation-examples.md @@ -7,7 +7,7 @@ - [Price-Based Automations](#price-based-automations) - [Volatility-Aware Automations](#volatility-aware-automations) - [Best Hour Detection](#best-hour-detection) -- [ApexCharts Cards](#apexcharts-cards) +- [Charts & Visualizations](#charts--visualizations) --- @@ -269,7 +269,7 @@ automation: :::tip Why "rising" means "act now" -A common misconception: **"rising" does NOT mean "too late"**. It means your current price is **lower** than the future average — so right now is actually a good time. See [How to Use Trend Sensors for Decisions](sensors.md#how-to-use-trend-sensors-for-decisions) in the sensor documentation for details. +A common misconception: **"rising" does NOT mean "too late"**. It means your current price is **lower** than the future average — so right now is actually a good time. See [How to Use Trend Sensors for Decisions](sensors-trends.md#how-to-use-trend-sensors-for-decisions) in the sensor documentation for details. ::: ### Sensor Combination Quick Reference @@ -502,97 +502,6 @@ automation: --- -## ApexCharts Cards +## Charts & Visualizations -> ⚠️ **IMPORTANT:** The `tibber_prices.get_apexcharts_yaml` service generates a **basic example configuration** as a starting point. It is NOT a complete solution for all ApexCharts features. -> -> This integration is primarily a **data provider**. Due to technical limitations (segmented time periods, service API usage), many advanced ApexCharts features require manual customization or may not be compatible. -> -> **For advanced customization:** Use the `get_chartdata` service directly to build charts tailored to your specific needs. Community contributions with improved configurations are welcome! - -The `tibber_prices.get_apexcharts_yaml` service generates basic ApexCharts card configuration examples for visualizing electricity prices. - -:::info Finding your Entry ID (`entry_id`) -The examples below contain `entry_id: YOUR_CONFIG_ENTRY_ID`. This value identifies which Tibber home (integration instance) the action targets. - -**In the Action UI (Developer Tools → Actions or the automation editor):** The `entry_id` field is a **dropdown** — just select your Tibber home and HA fills in the correct ID automatically. - -**In YAML:** Go to **Settings → Devices & Services**, find the **Tibber Prices** card, open the **⋮** (three-dot) menu, and choose **"Copy Config Entry ID"**. Paste the copied value in place of `YOUR_CONFIG_ENTRY_ID`. -::: - -### Prerequisites - -**Required:** - -- [ApexCharts Card](https://github.com/RomRider/apexcharts-card) - Install via HACS - -**Optional (for rolling window mode):** - -- [Config Template Card](https://github.com/iantrich/config-template-card) - Install via HACS - -### Installation - -1. Open HACS → Frontend -2. Search for "ApexCharts Card" and install -3. (Optional) Search for "Config Template Card" and install if you want rolling window mode - -### Example: Fixed Day View - -```yaml -# Generate configuration via automation/script -service: tibber_prices.get_apexcharts_yaml -data: - entry_id: YOUR_CONFIG_ENTRY_ID - day: today # or "yesterday", "tomorrow" - level_type: rating_level # or "level" for 5-level view -response_variable: apexcharts_config -``` - -Then copy the generated YAML into your Lovelace dashboard. - -### Example: Rolling 48h Window - -For a dynamic chart that automatically adapts to data availability: - -```yaml -service: tibber_prices.get_apexcharts_yaml -data: - entry_id: YOUR_CONFIG_ENTRY_ID - day: rolling_window # Or omit for same behavior (default) - level_type: rating_level -response_variable: apexcharts_config -``` - -**Behavior:** - -- **When tomorrow data available** (typically after ~13:00): Shows today + tomorrow -- **When tomorrow data not available**: Shows yesterday + today -- **Fixed 48h span:** Always shows full 48 hours - -**Auto-Zoom Variant:** - -For progressive zoom-in throughout the day: - -```yaml -service: tibber_prices.get_apexcharts_yaml -data: - entry_id: YOUR_CONFIG_ENTRY_ID - day: rolling_window_autozoom - level_type: rating_level -response_variable: apexcharts_config -``` - -- Same data loading as rolling window -- **Progressive zoom:** Graph span starts at ~26h in the morning and decreases to ~14h by midnight -- **Updates every 15 minutes:** Always shows 2h lookback + remaining time until midnight - -**Note:** Rolling window modes require Config Template Card to dynamically adjust the time range. - -### Features - -- Color-coded price levels/ratings (green = cheap, yellow = normal, red = expensive) -- Best price period highlighting (semi-transparent green overlay) -- Automatic NULL insertion for clean gaps -- Translated labels based on your Home Assistant language -- Interactive zoom and pan -- Live marker showing current time +> **Looking for chart configurations?** See the **[Chart Examples Guide](chart-examples.md)** for ApexCharts card configurations, rolling window modes, and more. diff --git a/docs/user/docs/chart-examples.md b/docs/user/docs/chart-examples.md index 49ad602..17db73d 100644 --- a/docs/user/docs/chart-examples.md +++ b/docs/user/docs/chart-examples.md @@ -291,7 +291,7 @@ cards: ## Next Steps - **[Actions Guide](actions.md)**: Complete documentation of `get_apexcharts_yaml` parameters -- **[Chart Metadata Sensor](sensors.md#chart-metadata)**: Learn about dynamic Y-axis scaling +- **[Chart Metadata Sensor](sensors-overview.md#chart-metadata)**: Learn about dynamic Y-axis scaling - **[Period Calculation Guide](period-calculation.md)**: Configure best price period detection --- diff --git a/docs/user/docs/concepts.md b/docs/user/docs/concepts.md index f0befd2..e225143 100644 --- a/docs/user/docs/concepts.md +++ b/docs/user/docs/concepts.md @@ -93,7 +93,7 @@ Some days show distinctive price curve shapes: - **V-shaped**: Prices drop sharply, hit a brief minimum, then rise sharply again (common during short midday solar surplus) - **U-shaped**: Prices drop to a low level and stay there for an extended period before rising (common during nighttime or extended low-demand periods) -**Why this matters:** On these days, the Best Price Period may be short (1–2 hours, covering only the absolute minimum), but prices can remain favorable for 4–6 hours. By combining [trend sensors](sensors.md#trend-sensors) with [price levels](sensors.md#core-price-sensors) in automations, you can ride the full cheap wave instead of only using the detected period. +**Why this matters:** On these days, the Best Price Period may be short (1–2 hours, covering only the absolute minimum), but prices can remain favorable for 4–6 hours. By combining [trend sensors](sensors-trends.md) with [price levels](sensors-ratings-levels.md) in automations, you can ride the full cheap wave instead of only using the detected period. See [Automation Examples → V-Shaped Days](automation-examples.md#understanding-v-shaped-price-days) for practical patterns. @@ -110,5 +110,5 @@ Each home gets its own set of sensors with unique entity IDs. 💡 **Next Steps:** - [Glossary](glossary.md) - Detailed term definitions -- [Sensors](sensors.md) - How to use sensor data +- [Sensors Overview](sensors-overview.md) - How to use sensor data - [Automation Examples](automation-examples.md) - Practical use cases diff --git a/docs/user/docs/configuration.md b/docs/user/docs/configuration.md index 7a15806..1cff410 100644 --- a/docs/user/docs/configuration.md +++ b/docs/user/docs/configuration.md @@ -121,7 +121,7 @@ Configure when trend sensors report rising/falling: | **Falling** | -3% | Future average this much below current → "falling" | | **Strongly falling** | -9% | Future average far below current → "strongly_falling" | -Thresholds are [volatility-adaptive](sensors.md#trend-sensors): automatically widened on volatile days to prevent constant state changes. +Thresholds are [volatility-adaptive](sensors-trends.md): automatically widened on volatile days to prevent constant state changes. ### Step 9: Chart Data Export (Legacy) @@ -184,7 +184,7 @@ This setting applies to: - Hourly smoothed prices (current hour, next hour) - Future forecast sensors (next 1h, 2h, 3h, ... 12h) -See the **[Sensors Guide](sensors.md#average-price-sensors)** for detailed examples. +See the **[Average Sensors](sensors-average.md)** for detailed examples. #### Choosing Your Display diff --git a/docs/user/docs/dynamic-icons.md b/docs/user/docs/dynamic-icons.md index 8bcbe6c..4393886 100644 --- a/docs/user/docs/dynamic-icons.md +++ b/docs/user/docs/dynamic-icons.md @@ -176,5 +176,5 @@ The exact icons are chosen to be intuitive and meaningful in the Home Assistant ## See Also - [Dynamic Icon Colors](icon-colors.md) - Color your icons based on state -- [Sensors Reference](sensors.md) - Complete list of available sensors +- [Sensors Overview](sensors-overview.md) - Complete list of available sensors - [Automation Examples](automation-examples.md) - Use dynamic icons in automations diff --git a/docs/user/docs/glossary.md b/docs/user/docs/glossary.md index ab5d9b5..7c763ec 100644 --- a/docs/user/docs/glossary.md +++ b/docs/user/docs/glossary.md @@ -115,5 +115,5 @@ Quick reference for terms used throughout the documentation. 💡 **See Also:** - [Core Concepts](concepts.md) - In-depth explanations -- [Sensors](sensors.md) - How sensors use these concepts +- [Sensors Overview](sensors-overview.md) - How sensors use these concepts - [Period Calculation](period-calculation.md) - Deep dive into period detection diff --git a/docs/user/docs/icon-colors.md b/docs/user/docs/icon-colors.md index b8dce54..1e88062 100644 --- a/docs/user/docs/icon-colors.md +++ b/docs/user/docs/icon-colors.md @@ -484,6 +484,6 @@ styles: ## See Also -- [Sensors Reference](sensors.md) - Complete list of available sensors +- [Sensors Overview](sensors-overview.md) - Complete list of available sensors - [Automation Examples](automation-examples.md) - Use color-coded sensors in automations - [Configuration Guide](configuration.md) - Adjust thresholds for price levels and ratings diff --git a/docs/user/docs/intro.md b/docs/user/docs/intro.md index 243d9f6..ccd761c 100644 --- a/docs/user/docs/intro.md +++ b/docs/user/docs/intro.md @@ -15,7 +15,7 @@ This is an independent, community-maintained custom integration. It is **not** a - **[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 +- **[Sensors](sensors-overview.md)** - Available sensors, their states, and attributes - **[Dynamic Icons](dynamic-icons.md)** - State-based automatic icon changes - **[Dynamic Icon Colors](icon-colors.md)** - Using icon_color attribute for color-coded dashboards - **[Actions](actions.md)** - Custom actions (service endpoints) and how to use them diff --git a/docs/user/docs/period-calculation.md b/docs/user/docs/period-calculation.md index 493d947..ca5ec15 100644 --- a/docs/user/docs/period-calculation.md +++ b/docs/user/docs/period-calculation.md @@ -9,15 +9,9 @@ Learn how Best Price and Peak Price periods work, and how to configure them for - [Quick Start](#quick-start) - [How It Works](#how-it-works) - [Configuration Guide](#configuration-guide) -- [Understanding Relaxation](#understanding-relaxation) +- [Understanding Relaxation](#understanding-relaxation) → [Full Guide](period-relaxation.md) - [Common Scenarios](#common-scenarios) - [Troubleshooting](#troubleshooting) - - [Fewer Periods Than Configured](#fewer-periods-than-configured) - - [No Periods Found](#no-periods-found) - - [Periods Split Into Small Pieces](#periods-split-into-small-pieces) - - [Understanding Sensor Attributes](#understanding-sensor-attributes) - - [Midnight Price Classification Changes](#midnight-price-classification-changes) -- [Advanced Topics](#advanced-topics) - [Advanced Topics](#advanced-topics) --- @@ -357,104 +351,14 @@ best_price_max_level: cheap # Only show objectively CHEAP periods ## Understanding Relaxation -### What Is Relaxation? +Sometimes strict filters find too few periods. **Relaxation automatically loosens filters** until a minimum number of periods is found — enabled by default. -Sometimes, strict filters find too few periods (or none). **Relaxation automatically loosens filters** until a minimum number of periods is found. +**Key benefits:** +- Each day gets exactly the flexibility it needs (per-day independence) +- Uses a matrix approach: N flex levels × 2 filter combinations +- Stops as soon as enough periods are found -### How to Enable - -```yaml -enable_min_periods_best: true -min_periods_best: 2 # Try to find at least 2 periods per day -relaxation_attempts_best: 11 # Flex levels to test (default: 11 steps = 22 filter combinations) -``` - -**ℹ️ Good news:** Relaxation is **enabled by default** with sensible settings. Most users don't need to change anything here! - -Set the matching `relaxation_attempts_peak` value when tuning Peak Price periods. Both sliders accept 1-12 attempts, and the default of 11 flex levels translates to 22 filter-combination tries (11 flex levels × 2 filter combos) for each of Best and Peak calculations. Lower it for quick feedback, or raise it when either sensor struggles to hit the minimum-period target on volatile days. - -### Why Relaxation Is Better Than Manual Tweaking - -**Problem with manual settings:** -- You set flex to 25% → Works great on Monday (volatile prices) -- Same 25% flex on Tuesday (flat prices) → Finds "best price" periods that aren't really cheap -- You're stuck with one setting for all days - -**Solution with relaxation:** -- Monday (volatile): Uses flex 15% (original) → Finds 2 perfect periods ✓ -- Tuesday (flat): Escalates to flex 21% → Finds 2 decent periods ✓ -- Wednesday (mixed): Uses flex 18% → Finds 2 good periods ✓ - -**Each day gets exactly the flexibility it needs!** - -### How It Works (Adaptive Matrix) - -Relaxation uses a **matrix approach** - trying _N_ flexibility levels (your configured **relaxation attempts**) with 2 filter combinations per level. With the default of 11 attempts, that means 11 flex levels × 2 filter combinations = **22 total filter-combination tries per day**; fewer attempts mean fewer flex increases, while more attempts extend the search further before giving up. - -**Important:** The flexibility increment is **fixed at 3% per step** (hard-coded for reliability). This means: -- Base flex 15% → 18% → 21% → 24% → ... → 48% (with 11 attempts) -- Base flex 20% → 23% → 26% → 29% → ... → 50% (with 11 attempts) - -#### Phase Matrix - -For each day, the system tries: - -```mermaid -flowchart TD - Start["Start: base flex
(e.g. 15%)"] --> A1 - - subgraph Attempt1["Attempt 1 — flex 15%"] - A1["Your filters"] -->|not enough| A2["Level = any"] - end - - A2 -->|not enough| B1 - - subgraph Attempt2["Attempt 2 — flex 18%"] - B1["Your filters"] -->|not enough| B2["Level = any"] - end - - B2 -->|not enough| C1 - - subgraph Attempt3["Attempt 3 — flex 21%"] - C1["Your filters"] --> C2["Level = any"] - end - - C1 -->|"✅ enough"| Done - A1 -->|"✅ enough"| Done - A2 -->|"✅ enough"| Done - B1 -->|"✅ enough"| Done - B2 -->|"✅ enough"| Done - C2 -->|"✅ / not enough → next …"| Done - - Done["✅ Done
stops at first success"] - - style Start fill:#e6f7ff,stroke:#00b9e7,stroke-width:2px - style Done fill:#e6fff5,stroke:#00c853,stroke-width:2px - style Attempt1 fill:#f0f9ff,stroke:#00b9e7 - style Attempt2 fill:#fff9e6,stroke:#ffb800 - style Attempt3 fill:#fff0f0,stroke:#ff8a80 -``` - -Each attempt adds +3% flexibility and tries two filter combinations. The system **stops as soon as enough periods are found** — it doesn't keep trying the full matrix. - -### Choosing the Number of Attempts - -- **Default (11 attempts)** balances speed and completeness for most grids (22 combinations per day for both Best and Peak) -- **Lower (4-8 attempts)** if you only want mild relaxation and keep processing time minimal (reaches ~27-39% flex) -- **Higher (12 attempts)** for extremely volatile days when you must reach near the 50% maximum (24 combinations) -- Remember: each additional attempt adds two more filter combinations because every new flex level still runs both filter overrides (original + level=any) - -#### Per-Day Independence - -**Critical:** Each day relaxes **independently**: - -``` -Day 1: Finds 2 periods with flex 15% (original) → No relaxation needed -Day 2: Needs flex 21% + level=any → Uses relaxed settings -Day 3: Finds 2 periods with flex 15% (original) → No relaxation needed -``` - -**Why?** Price patterns vary daily. Some days have clear cheap/expensive windows (strict filters work), others don't (relaxation needed). +**→ [Full Relaxation Guide](period-relaxation.md)** — How it works, the adaptive matrix, choosing attempts, and diagnostics. --- diff --git a/docs/user/docs/period-relaxation.md b/docs/user/docs/period-relaxation.md new file mode 100644 index 0000000..da0ac9c --- /dev/null +++ b/docs/user/docs/period-relaxation.md @@ -0,0 +1,129 @@ +# Understanding Relaxation + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +Relaxation is the automatic filter-loosening mechanism that ensures your [Best/Peak Price periods](period-calculation.md) always find results — even on days with unusual price patterns. + +--- + +## What Is Relaxation? + +Sometimes, strict filters find too few periods (or none). **Relaxation automatically loosens filters** until a minimum number of periods is found. + +## How to Enable + +```yaml +enable_min_periods_best: true +min_periods_best: 2 # Try to find at least 2 periods per day +relaxation_attempts_best: 11 # Flex levels to test (default: 11 steps = 22 filter combinations) +``` + +**Good news:** Relaxation is **enabled by default** with sensible settings. Most users don't need to change anything here! + +Set the matching `relaxation_attempts_peak` value when tuning Peak Price periods. Both sliders accept 1-12 attempts, and the default of 11 flex levels translates to 22 filter-combination tries (11 flex levels × 2 filter combos) for each of Best and Peak calculations. Lower it for quick feedback, or raise it when either sensor struggles to hit the minimum-period target on volatile days. + +## Why Relaxation Is Better Than Manual Tweaking + +**Problem with manual settings:** +- You set flex to 25% → Works great on Monday (volatile prices) +- Same 25% flex on Tuesday (flat prices) → Finds "best price" periods that aren't really cheap +- You're stuck with one setting for all days + +**Solution with relaxation:** +- Monday (volatile): Uses flex 15% (original) → Finds 2 perfect periods ✓ +- Tuesday (flat): Escalates to flex 21% → Finds 2 decent periods ✓ +- Wednesday (mixed): Uses flex 18% → Finds 2 good periods ✓ + +**Each day gets exactly the flexibility it needs!** + +## How It Works (Adaptive Matrix) + +Relaxation uses a **matrix approach** - trying _N_ flexibility levels (your configured **relaxation attempts**) with 2 filter combinations per level. With the default of 11 attempts, that means 11 flex levels × 2 filter combinations = **22 total filter-combination tries per day**; fewer attempts mean fewer flex increases, while more attempts extend the search further before giving up. + +**Important:** The flexibility increment is **fixed at 3% per step** (hard-coded for reliability). This means: +- Base flex 15% → 18% → 21% → 24% → ... → 48% (with 11 attempts) +- Base flex 20% → 23% → 26% → 29% → ... → 50% (with 11 attempts) + +### Phase Matrix + +For each day, the system tries: + +```mermaid +flowchart TD + Start["Start: base flex
(e.g. 15%)"] --> A1 + + subgraph Attempt1["Attempt 1 — flex 15%"] + A1["Your filters"] -->|not enough| A2["Level = any"] + end + + A2 -->|not enough| B1 + + subgraph Attempt2["Attempt 2 — flex 18%"] + B1["Your filters"] -->|not enough| B2["Level = any"] + end + + B2 -->|not enough| C1 + + subgraph Attempt3["Attempt 3 — flex 21%"] + C1["Your filters"] --> C2["Level = any"] + end + + C1 -->|"✅ enough"| Done + A1 -->|"✅ enough"| Done + A2 -->|"✅ enough"| Done + B1 -->|"✅ enough"| Done + B2 -->|"✅ enough"| Done + C2 -->|"✅ / not enough → next …"| Done + + Done["✅ Done
stops at first success"] + + style Start fill:#e6f7ff,stroke:#00b9e7,stroke-width:2px + style Done fill:#e6fff5,stroke:#00c853,stroke-width:2px + style Attempt1 fill:#f0f9ff,stroke:#00b9e7 + style Attempt2 fill:#fff9e6,stroke:#ffb800 + style Attempt3 fill:#fff0f0,stroke:#ff8a80 +``` + +Each attempt adds +3% flexibility and tries two filter combinations. The system **stops as soon as enough periods are found** — it doesn't keep trying the full matrix. + +## Choosing the Number of Attempts + +- **Default (11 attempts)** balances speed and completeness for most grids (22 combinations per day for both Best and Peak) +- **Lower (4-8 attempts)** if you only want mild relaxation and keep processing time minimal (reaches ~27-39% flex) +- **Higher (12 attempts)** for extremely volatile days when you must reach near the 50% maximum (24 combinations) +- Remember: each additional attempt adds two more filter combinations because every new flex level still runs both filter overrides (original + level=any) + +## Per-Day Independence + +**Critical:** Each day relaxes **independently**: + +``` +Day 1: Finds 2 periods with flex 15% (original) → No relaxation needed +Day 2: Needs flex 21% + level=any → Uses relaxed settings +Day 3: Finds 2 periods with flex 15% (original) → No relaxation needed +``` + +**Why?** Price patterns vary daily. Some days have clear cheap/expensive windows (strict filters work), others don't (relaxation needed). + +## Diagnosing Relaxation Behavior + +Check the period sensor attributes to understand what happened: + +```yaml +# Entity: binary_sensor._best_price_period + +relaxation_active: true # This day needed relaxation +relaxation_level: "price_diff_18.0%+level_any" # Found at 18% flex, level filter removed +min_periods_configured: 2 # Your target +periods_found_total: 3 # What was actually found +``` + +| Attribute | Meaning | +|-----------|---------| +| `relaxation_active: false` | Original filters were sufficient | +| `relaxation_active: true` | Filters were loosened to find enough periods | +| `relaxation_level` | Shows exactly which flex% and filter combo succeeded | +| `relaxation_incomplete: true` | All attempts exhausted, still short of target | +| `flat_days_detected: 1` | Uniform prices → target reduced to 1 (expected) | + +**See also:** [Period Calculation — Troubleshooting](period-calculation.md#troubleshooting) for more diagnostic guidance. diff --git a/docs/user/docs/sensor-reference.md b/docs/user/docs/sensor-reference.md index b4719d0..e345aeb 100644 --- a/docs/user/docs/sensor-reference.md +++ b/docs/user/docs/sensor-reference.md @@ -29,7 +29,7 @@ You can also use your browser's built-in search (**Ctrl+F** / **Cmd+F**) to sear Sensors marked ❌ must be enabled manually via **Settings → Devices & Services → Entities** → find the entity → toggle **Enabled**. -**Detailed documentation:** See the **[Sensors Guide](sensors.md)** for detailed +**Detailed documentation:** See the **[Sensors Overview](sensors-overview.md)** for detailed explanations of each sensor's purpose, attributes, and automation examples. --- @@ -51,32 +51,32 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `current_hour_average_price` | ⌀ Hourly Price Current | ⌀ Stunden-Preis aktuell | ⌀ Timepris nåværende | ⌀ Uurprijs Huidig | ⌀ Timpris aktuell | ✅ | -| `next_hour_average_price` | ⌀ Hourly Price Next | ⌀ Stunden-Preis nächste Stunde | ⌀ Timepris neste | ⌀ Uurprijs Volgend | ⌀ Timpris nästa | ✅ | +| `current_hour_average_price` | ⌀ Hourly Price Current | ⌀ Stunden-Preis aktuell | ⌀ Timepris nåværende | ⌀ Uurprijs Huidig | ⌀ Timpris aktuell | ✅ | +| `next_hour_average_price` | ⌀ Hourly Price Next | ⌀ Stunden-Preis nächste Stunde | ⌀ Timepris neste | ⌀ Uurprijs Volgend | ⌀ Timpris nästa | ✅ | ### Daily Statistics | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `lowest_price_today` | Today's Lowest Price | Mindestpreis heute | Dagens laveste pris | Laagste Prijs Vandaag | Dagens lägsta pris | ✅ | -| `highest_price_today` | Today's Highest Price | Höchstpreis heute | Dagens høyeste pris | Hoogste Prijs Vandaag | Dagens högsta pris | ✅ | -| `average_price_today` | ⌀ Price Today | ⌀ Preis heute | ⌀ Pris i dag | ⌀ Prijs Vandaag | ⌀ Pris idag | ✅ | -| `lowest_price_tomorrow` | Tomorrow's Lowest Price | Mindestpreis morgen | Morgendagens laveste pris | Laagste Prijs Morgen | Morgondagens lägsta pris | ✅ | -| `highest_price_tomorrow` | Tomorrow's Highest Price | Höchstpreis morgen | Morgendagens høyeste pris | Hoogste Prijs Morgen | Morgondagens högsta pris | ✅ | -| `average_price_tomorrow` | ⌀ Price Tomorrow | ⌀ Preis morgen | ⌀ Pris i morgen | ⌀ Prijs Morgen | ⌀ Pris imorgon | ✅ | +| `lowest_price_today` | Today's Lowest Price | Mindestpreis heute | Dagens laveste pris | Laagste Prijs Vandaag | Dagens lägsta pris | ✅ | +| `highest_price_today` | Today's Highest Price | Höchstpreis heute | Dagens høyeste pris | Hoogste Prijs Vandaag | Dagens högsta pris | ✅ | +| `average_price_today` | ⌀ Price Today | ⌀ Preis heute | ⌀ Pris i dag | ⌀ Prijs Vandaag | ⌀ Pris idag | ✅ | +| `lowest_price_tomorrow` | Tomorrow's Lowest Price | Mindestpreis morgen | Morgendagens laveste pris | Laagste Prijs Morgen | Morgondagens lägsta pris | ✅ | +| `highest_price_tomorrow` | Tomorrow's Highest Price | Höchstpreis morgen | Morgendagens høyeste pris | Hoogste Prijs Morgen | Morgondagens högsta pris | ✅ | +| `average_price_tomorrow` | ⌀ Price Tomorrow | ⌀ Preis morgen | ⌀ Pris i morgen | ⌀ Prijs Morgen | ⌀ Pris imorgon | ✅ | ### 24h Window Sensors | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `trailing_price_average` | ⌀ Price Trailing 24h | ⌀ Preis nachlaufend 24h | ⌀ Pris glidende 24t | ⌀ Prijs Afgelopen 24u | ⌀ Pris glidande 24h | ❌ | -| `leading_price_average` | ⌀ Price Leading 24h | ⌀ Preis vorlaufend 24h | ⌀ Pris fremtidig 24t | ⌀ Prijs Komende 24u | ⌀ Pris framåt 24h | ❌ | -| `trailing_price_min` | Trailing 24h Minimum Price | 24h-Mindestpreis nachlaufend | Glidende 24t minimumspris | Afgelopen 24u Minimumprijs | Glidande 24h minimipris | ❌ | -| `trailing_price_max` | Trailing 24h Maximum Price | 24h-Höchstpreis nachlaufend | Glidende 24t maksimumspris | Afgelopen 24u Maximumprijs | Glidande 24h maximipris | ❌ | -| `leading_price_min` | Leading 24h Minimum Price | 24h-Mindestpreis vorlaufend | Fremtidig 24t minimumspris | Komende 24u Minimumprijs | Framåt 24h minimipris | ❌ | -| `leading_price_max` | Leading 24h Maximum Price | 24h-Höchstpreis vorlaufend | Fremtidig 24t maksimumspris | Komende 24u Maximumprijs | Framåt 24h maximipris | ❌ | +| `trailing_price_average` | ⌀ Price Trailing 24h | ⌀ Preis nachlaufend 24h | ⌀ Pris glidende 24t | ⌀ Prijs Afgelopen 24u | ⌀ Pris glidande 24h | ❌ | +| `leading_price_average` | ⌀ Price Leading 24h | ⌀ Preis vorlaufend 24h | ⌀ Pris fremtidig 24t | ⌀ Prijs Komende 24u | ⌀ Pris framåt 24h | ❌ | +| `trailing_price_min` | Trailing 24h Minimum Price | 24h-Mindestpreis nachlaufend | Glidende 24t minimumspris | Afgelopen 24u Minimumprijs | Glidande 24h minimipris | ❌ | +| `trailing_price_max` | Trailing 24h Maximum Price | 24h-Höchstpreis nachlaufend | Glidende 24t maksimumspris | Afgelopen 24u Maximumprijs | Glidande 24h maximipris | ❌ | +| `leading_price_min` | Leading 24h Minimum Price | 24h-Mindestpreis vorlaufend | Fremtidig 24t minimumspris | Komende 24u Minimumprijs | Framåt 24h minimipris | ❌ | +| `leading_price_max` | Leading 24h Maximum Price | 24h-Höchstpreis vorlaufend | Fremtidig 24t maksimumspris | Komende 24u Maximumprijs | Framåt 24h maximipris | ❌ | ### Future Price Averages @@ -97,28 +97,28 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `current_interval_price_level` | Current Price Level | Aktuelles Preisniveau | Nåværende prisnivå | Huidig Prijsniveau | Aktuell prisnivå | ✅ | -| `next_interval_price_level` | Next Price Level | Nächstes Preisniveau | Neste prisnivå | Volgend Prijsniveau | Nästa prisnivå | ✅ | -| `previous_interval_price_level` | Previous Price Level | Vorheriges Preisniveau | Forrige prisnivå | Vorig Prijsniveau | Föregående prisnivå | ❌ | -| `current_hour_price_level` | Current Hour Price Level | Aktuelles Stunden-Preisniveau | Nåværende timepris nivå | Huidig Uur Prijsniveau | Aktuell timprisnivå | ✅ | -| `next_hour_price_level` | Next Hour Price Level | Nächstes Stunden-Preisniveau | Neste timepris nivå | Volgend Uur Prijsniveau | Nästa timprisnivå | ✅ | -| `yesterday_price_level` | Yesterday's Price Level | Preisniveau gestern | Prisnivå i går | Gisteren Prijsniveau | Gårdagens prisnivå | ❌ | -| `today_price_level` | Today's Price Level | Preisniveau heute | Prisnivå i dag | Vandaag Prijsniveau | Dagens prisnivå | ✅ | -| `tomorrow_price_level` | Tomorrow's Price Level | Preisniveau morgen | Prisnivå i morgen | Morgen Prijsniveau | Morgondagens prisnivå | ✅ | +| `current_interval_price_level` | Current Price Level | Aktuelles Preisniveau | Nåværende prisnivå | Huidig Prijsniveau | Aktuell prisnivå | ✅ | +| `next_interval_price_level` | Next Price Level | Nächstes Preisniveau | Neste prisnivå | Volgend Prijsniveau | Nästa prisnivå | ✅ | +| `previous_interval_price_level` | Previous Price Level | Vorheriges Preisniveau | Forrige prisnivå | Vorig Prijsniveau | Föregående prisnivå | ❌ | +| `current_hour_price_level` | Current Hour Price Level | Aktuelles Stunden-Preisniveau | Nåværende timepris nivå | Huidig Uur Prijsniveau | Aktuell timprisnivå | ✅ | +| `next_hour_price_level` | Next Hour Price Level | Nächstes Stunden-Preisniveau | Neste timepris nivå | Volgend Uur Prijsniveau | Nästa timprisnivå | ✅ | +| `yesterday_price_level` | Yesterday's Price Level | Preisniveau gestern | Prisnivå i går | Gisteren Prijsniveau | Gårdagens prisnivå | ❌ | +| `today_price_level` | Today's Price Level | Preisniveau heute | Prisnivå i dag | Vandaag Prijsniveau | Dagens prisnivå | ✅ | +| `tomorrow_price_level` | Tomorrow's Price Level | Preisniveau morgen | Prisnivå i morgen | Morgen Prijsniveau | Morgondagens prisnivå | ✅ | ### Price Rating Sensors | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `current_interval_price_rating` | Current Price Rating | Aktuelle Preisbewertung | Nåværende prisvurdering | Huidige Prijsbeoordeling | Aktuellt prisbetyg | ❌ | -| `next_interval_price_rating` | Next Price Rating | Nächste Preisbewertung | Neste prisvurdering | Volgende Prijsbeoordeling | Nästa prisbetyg | ❌ | -| `previous_interval_price_rating` | Previous Price Rating | Vorherige Preisbewertung | Forrige prisvurdering | Vorige Prijsbeoordeling | Föregående prisbetyg | ❌ | -| `current_hour_price_rating` | Current Hour Price Rating | Aktuelle Stunden-Preisbewertung | Nåværende timeprisvurdering | Huidig Uur Prijsbeoordeling | Aktuellt timprisbetyg | ❌ | -| `next_hour_price_rating` | Next Hour Price Rating | Nächste Stunden-Preisbewertung | Neste timeprisvurdering | Volgend Uur Prijsbeoordeling | Nästa timprisbetyg | ❌ | -| `yesterday_price_rating` | Yesterday's Price Rating | Preisbewertung gestern | Prisvurdering i går | Gisteren Prijsbeoordeling | Gårdagens prisbetyg | ❌ | -| `today_price_rating` | Today's Price Rating | Preisbewertung heute | Prisvurdering i dag | Vandaag Prijsbeoordeling | Dagens prisbetyg | ❌ | -| `tomorrow_price_rating` | Tomorrow's Price Rating | Preisbewertung morgen | Prisvurdering i morgen | Morgen Prijsbeoordeling | Morgondagens prisbetyg | ❌ | +| `current_interval_price_rating` | Current Price Rating | Aktuelle Preisbewertung | Nåværende prisvurdering | Huidige Prijsbeoordeling | Aktuellt prisbetyg | ❌ | +| `next_interval_price_rating` | Next Price Rating | Nächste Preisbewertung | Neste prisvurdering | Volgende Prijsbeoordeling | Nästa prisbetyg | ❌ | +| `previous_interval_price_rating` | Previous Price Rating | Vorherige Preisbewertung | Forrige prisvurdering | Vorige Prijsbeoordeling | Föregående prisbetyg | ❌ | +| `current_hour_price_rating` | Current Hour Price Rating | Aktuelle Stunden-Preisbewertung | Nåværende timeprisvurdering | Huidig Uur Prijsbeoordeling | Aktuellt timprisbetyg | ❌ | +| `next_hour_price_rating` | Next Hour Price Rating | Nächste Stunden-Preisbewertung | Neste timeprisvurdering | Volgend Uur Prijsbeoordeling | Nästa timprisbetyg | ❌ | +| `yesterday_price_rating` | Yesterday's Price Rating | Preisbewertung gestern | Prisvurdering i går | Gisteren Prijsbeoordeling | Gårdagens prisbetyg | ❌ | +| `today_price_rating` | Today's Price Rating | Preisbewertung heute | Prisvurdering i dag | Vandaag Prijsbeoordeling | Dagens prisbetyg | ❌ | +| `tomorrow_price_rating` | Tomorrow's Price Rating | Preisbewertung morgen | Prisvurdering i morgen | Morgen Prijsbeoordeling | Morgondagens prisbetyg | ❌ | | `daily_rating` | Daily Price Rating | Tägliche Preisbewertung | Daglig prisvurdering | Dagelijkse Prijsbeoordeling | Dagligt prisbetyg | ✅ | | `monthly_rating` | Monthly Price Rating | Monatliche Preisbewertung | Månedlig prisvurdering | Maandelijkse Prijsbeoordeling | Månatligt prisbetyg | ✅ | @@ -127,8 +127,8 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `current_price_trend` | Current Price Trend | Aktueller Preistrend | Nåværende pristrend | Huidige Prijstrend | Aktuell pristrend | ✅ | -| `next_price_trend_change` | Next Price Trend Change | Nächste Trendänderung | Neste trendendring | Volgende Prijstrend Wijziging | Nästa pristrendändring | ✅ | +| `current_price_trend` | Current Price Trend | Aktueller Preistrend | Nåværende pristrend | Huidige Prijstrend | Aktuell pristrend | ✅ | +| `next_price_trend_change` | Next Price Trend Change | Nächste Trendänderung | Neste trendendring | Volgende Prijstrend Wijziging | Nästa pristrendändring | ✅ | | `next_price_trend_change_in` | Next Price Trend Change In | Nächste Trendänderung in | Neste trendendring om | Volgende Prijstrend Wijziging over | Nästa pristrendändring om | ✅ | | `price_outlook_1h` | Price Outlook (1h) | Preisausblick (1h) | Prisutblikk (1t) | Prijsvooruitzicht (1u) | Prisöversikt (1h) | ✅ | | `price_outlook_2h` | Price Outlook (2h) | Preisausblick (2h) | Prisutblikk (2t) | Prijsvooruitzicht (2u) | Prisöversikt (2h) | ✅ | @@ -151,34 +151,34 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `today_volatility` | Today's Price Volatility | Volatilität heute | Volatilitet i dag | Vandaag Prijsvolatiliteit | Dagens prisvolatilitet | ✅ | -| `tomorrow_volatility` | Tomorrow's Price Volatility | Volatilität morgen | Volatilitet i morgen | Morgen Prijsvolatiliteit | Morgondagens prisvolatilitet | ❌ | +| `today_volatility` | Today's Price Volatility | Volatilität heute | Volatilitet i dag | Vandaag Prijsvolatiliteit | Dagens prisvolatilitet | ✅ | +| `tomorrow_volatility` | Tomorrow's Price Volatility | Volatilität morgen | Volatilitet i morgen | Morgen Prijsvolatiliteit | Morgondagens prisvolatilitet | ❌ | | `next_24h_volatility` | Next 24h Price Volatility | Volatilität der nächsten 24h | Volatilitet neste 24t | Komende 24u Prijsvolatiliteit | Nästa 24h prisvolatilitet | ❌ | -| `today_tomorrow_volatility` | Today+Tomorrow Price Volatility | Volatilität heute+morgen | Volatilitet i dag+i morgen | Vandaag+Morgen Prijsvolatiliteit | Idag+Imorgon prisvolatilitet | ❌ | +| `today_tomorrow_volatility` | Today+Tomorrow Price Volatility | Volatilität heute+morgen | Volatilitet i dag+i morgen | Vandaag+Morgen Prijsvolatiliteit | Idag+Imorgon prisvolatilitet | ❌ | ### Best Price Timing | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `best_price_end_time` | Best Price End | Bestpreis endet | Beste pris slutter | Beste Prijs Einde | Bästa pris slutar | ✅ | -| `best_price_period_duration` | Best Price Duration | Bestpreis Dauer | Beste pris varighet | Beste Prijs Duur | Bästa pris varaktighet | ❌ | -| `best_price_remaining_minutes` | Best Price Remaining Time | Bestpreis verbleibend | Beste pris gjenværende tid | Beste Prijs Resterende Tijd | Bästa pris återstående tid | ✅ | -| `best_price_progress` | Best Price Progress | Bestpreis Fortschritt | Beste pris fremgang | Beste Prijs Voortgang | Bästa pris framsteg | ✅ | -| `best_price_next_start_time` | Best Price Start | Bestpreis startet | Beste pris starter | Beste Prijs Start | Bästa pris startar | ✅ | -| `best_price_next_in_minutes` | Best Price Starts In | Bestpreis startet in | Beste pris starter om | Beste Prijs Start Over | Bästa pris startar om | ✅ | +| `best_price_end_time` | Best Price End | Bestpreis endet | Beste pris slutter | Beste Prijs Einde | Bästa pris slutar | ✅ | +| `best_price_period_duration` | Best Price Duration | Bestpreis Dauer | Beste pris varighet | Beste Prijs Duur | Bästa pris varaktighet | ❌ | +| `best_price_remaining_minutes` | Best Price Remaining Time | Bestpreis verbleibend | Beste pris gjenværende tid | Beste Prijs Resterende Tijd | Bästa pris återstående tid | ✅ | +| `best_price_progress` | Best Price Progress | Bestpreis Fortschritt | Beste pris fremgang | Beste Prijs Voortgang | Bästa pris framsteg | ✅ | +| `best_price_next_start_time` | Best Price Start | Bestpreis startet | Beste pris starter | Beste Prijs Start | Bästa pris startar | ✅ | +| `best_price_next_in_minutes` | Best Price Starts In | Bestpreis startet in | Beste pris starter om | Beste Prijs Start Over | Bästa pris startar om | ✅ | ### Peak Price Timing | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `peak_price_end_time` | Peak Price End | Spitzenpreis endet | Topppris slutter | Piekprijs Einde | Topppris slutar | ✅ | -| `peak_price_period_duration` | Peak Price Duration | Spitzenpreis Dauer | Topppris varighet | Piekprijs Duur | Topppris varaktighet | ❌ | -| `peak_price_remaining_minutes` | Peak Price Remaining Time | Spitzenpreis verbleibend | Topppris gjenværende tid | Piekprijs Resterende Tijd | Topppris återstående tid | ✅ | -| `peak_price_progress` | Peak Price Progress | Spitzenpreis Fortschritt | Topppris fremgang | Piekprijs Voortgang | Topppris framsteg | ✅ | -| `peak_price_next_start_time` | Peak Price Start | Spitzenpreis startet | Topppris starter | Piekprijs Start | Topppris startar | ✅ | -| `peak_price_next_in_minutes` | Peak Price Starts In | Spitzenpreis startet in | Topppris starter om | Piekprijs Start Over | Topppris startar om | ✅ | +| `peak_price_end_time` | Peak Price End | Spitzenpreis endet | Topppris slutter | Piekprijs Einde | Topppris slutar | ✅ | +| `peak_price_period_duration` | Peak Price Duration | Spitzenpreis Dauer | Topppris varighet | Piekprijs Duur | Topppris varaktighet | ❌ | +| `peak_price_remaining_minutes` | Peak Price Remaining Time | Spitzenpreis verbleibend | Topppris gjenværende tid | Piekprijs Resterende Tijd | Topppris återstående tid | ✅ | +| `peak_price_progress` | Peak Price Progress | Spitzenpreis Fortschritt | Topppris fremgang | Piekprijs Voortgang | Topppris framsteg | ✅ | +| `peak_price_next_start_time` | Peak Price Start | Spitzenpreis startet | Topppris starter | Piekprijs Start | Topppris startar | ✅ | +| `peak_price_next_in_minutes` | Peak Price Starts In | Spitzenpreis startet in | Topppris starter om | Piekprijs Start Over | Topppris startar om | ✅ | ### Home & Metering Metadata @@ -215,8 +215,8 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `best_price_period` | Best Price Period | Bestpreis-Zeitraum | Lavpris-periode | Beste Prijs Periode | Bästa Prisperiod | ✅ | -| `peak_price_period` | Peak Price Period | Spitzenpreis-Zeitraum | Toppris-periode | Piekprijs Periode | Topprisperiod | ✅ | +| `best_price_period` | Best Price Period | Bestpreis-Zeitraum | Lavpris-periode | Beste Prijs Periode | Bästa Prisperiod | ✅ | +| `peak_price_period` | Peak Price Period | Spitzenpreis-Zeitraum | Toppris-periode | Piekprijs Periode | Topprisperiod | ✅ | | `connection` | Tibber API Connection | Tibber-API-Verbindung | Tibber API-tilkobling | Tibber API Verbinding | Tibber API-anslutning | ✅ | | `tomorrow_data_available` | Tomorrow's Data Available | Morgige Daten verfügbar | Morgendagens data tilgjengelig | Morgen Gegevens Beschikbaar | Morgondagens data tillgänglig | ✅ | | `has_ventilation_system` | Has Ventilation System | Hat Lüftungsanlage | Har ventilasjonsanlegg | Heeft Ventilatiesysteem | Har ventilationssystem | ❌ | @@ -230,11 +230,11 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `best_price_flex_override` | Best Price: Flexibility | Bestpreis: Flexibilität | Beste pris: Fleksibilitet | Beste prijs: Flexibiliteit | Bästa pris: Flexibilitet | ❌ | -| `best_price_min_distance_override` | Best Price: Minimum Distance | Bestpreis: Mindestabstand | Beste pris: Minimumsavstand | Beste prijs: Minimale afstand | Bästa pris: Minimiavstånd | ❌ | -| `best_price_min_period_length_override` | Best Price: Minimum Period Length | Bestpreis: Mindestperiodenlänge | Beste pris: Minimum periodelengde | Beste prijs: Minimale periodelengte | Bästa pris: Minsta periodlängd | ❌ | -| `best_price_min_periods_override` | Best Price: Minimum Periods | Bestpreis: Mindestperioden | Beste pris: Minimum perioder | Beste prijs: Minimum periodes | Bästa pris: Minsta antal perioder | ❌ | -| `best_price_relaxation_attempts_override` | Best Price: Relaxation Attempts | Bestpreis: Lockerungsversuche | Beste pris: Lemping forsøk | Beste prijs: Versoepeling pogingen | Bästa pris: Lättnadsförsök | ❌ | +| `best_price_flex_override` | Best Price: Flexibility | Bestpreis: Flexibilität | Beste pris: Fleksibilitet | Beste prijs: Flexibiliteit | Bästa pris: Flexibilitet | ❌ | +| `best_price_min_distance_override` | Best Price: Minimum Distance | Bestpreis: Mindestabstand | Beste pris: Minimumsavstand | Beste prijs: Minimale afstand | Bästa pris: Minimiavstånd | ❌ | +| `best_price_min_period_length_override` | Best Price: Minimum Period Length | Bestpreis: Mindestperiodenlänge | Beste pris: Minimum periodelengde | Beste prijs: Minimale periodelengte | Bästa pris: Minsta periodlängd | ❌ | +| `best_price_min_periods_override` | Best Price: Minimum Periods | Bestpreis: Mindestperioden | Beste pris: Minimum perioder | Beste prijs: Minimum periodes | Bästa pris: Minsta antal perioder | ❌ | +| `best_price_relaxation_attempts_override` | Best Price: Relaxation Attempts | Bestpreis: Lockerungsversuche | Beste pris: Lemping forsøk | Beste prijs: Versoepeling pogingen | Bästa pris: Lättnadsförsök | ❌ | | `best_price_gap_count_override` | Best Price: Gap Tolerance | Bestpreis: Lückentoleranz | Beste pris: Gaptoleranse | Beste prijs: Gap tolerantie | Bästa pris: Glaptolerans | ❌ | ### Peak Price Configuration @@ -242,11 +242,11 @@ explanations of each sensor's purpose, attributes, and automation examples. | Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default | |---|---|---|---|---|---|---| -| `peak_price_flex_override` | Peak Price: Flexibility | Spitzenpreis: Flexibilität | Topppris: Fleksibilitet | Piekprijs: Flexibiliteit | Topppris: Flexibilitet | ❌ | -| `peak_price_min_distance_override` | Peak Price: Minimum Distance | Spitzenpreis: Mindestabstand | Topppris: Minimumsavstand | Piekprijs: Minimale afstand | Topppris: Minimiavstånd | ❌ | -| `peak_price_min_period_length_override` | Peak Price: Minimum Period Length | Spitzenpreis: Mindestperiodenlänge | Topppris: Minimum periodelengde | Piekprijs: Minimale periodelengte | Topppris: Minsta periodlängd | ❌ | -| `peak_price_min_periods_override` | Peak Price: Minimum Periods | Spitzenpreis: Mindestperioden | Topppris: Minimum perioder | Piekprijs: Minimum periodes | Topppris: Minsta antal perioder | ❌ | -| `peak_price_relaxation_attempts_override` | Peak Price: Relaxation Attempts | Spitzenpreis: Lockerungsversuche | Topppris: Lemping forsøk | Piekprijs: Versoepeling pogingen | Topppris: Lättnadsförsök | ❌ | +| `peak_price_flex_override` | Peak Price: Flexibility | Spitzenpreis: Flexibilität | Topppris: Fleksibilitet | Piekprijs: Flexibiliteit | Topppris: Flexibilitet | ❌ | +| `peak_price_min_distance_override` | Peak Price: Minimum Distance | Spitzenpreis: Mindestabstand | Topppris: Minimumsavstand | Piekprijs: Minimale afstand | Topppris: Minimiavstånd | ❌ | +| `peak_price_min_period_length_override` | Peak Price: Minimum Period Length | Spitzenpreis: Mindestperiodenlänge | Topppris: Minimum periodelengde | Piekprijs: Minimale periodelengte | Topppris: Minsta periodlängd | ❌ | +| `peak_price_min_periods_override` | Peak Price: Minimum Periods | Spitzenpreis: Mindestperioden | Topppris: Minimum perioder | Piekprijs: Minimum periodes | Topppris: Minsta antal perioder | ❌ | +| `peak_price_relaxation_attempts_override` | Peak Price: Relaxation Attempts | Spitzenpreis: Lockerungsversuche | Topppris: Lemping forsøk | Piekprijs: Versoepeling pogingen | Topppris: Lättnadsförsök | ❌ | | `peak_price_gap_count_override` | Peak Price: Gap Tolerance | Spitzenpreis: Lückentoleranz | Topppris: Gaptoleranse | Piekprijs: Gap tolerantie | Topppris: Glaptolerans | ❌ | ## Switch Entities (Configuration Overrides) diff --git a/docs/user/docs/sensors-average.md b/docs/user/docs/sensors-average.md new file mode 100644 index 0000000..730808b --- /dev/null +++ b/docs/user/docs/sensors-average.md @@ -0,0 +1,194 @@ +# Average & Statistics Sensors + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +The integration provides several sensors that calculate average electricity prices over different time windows. These sensors show a **typical** price value that represents the overall price level, helping you make informed decisions about when to use electricity. + +## Available Average Sensors + +| Sensor | Description | Time Window | +|--------|-------------|-------------| +| Average Price Today | Typical price for current calendar day | 00:00 - 23:59 today | +| Average Price Tomorrow | Typical price for next calendar day | 00:00 - 23:59 tomorrow | +| Trailing Price Average | Typical price for last 24 hours | Rolling 24h backward | +| Leading Price Average | Typical price for next 24 hours | Rolling 24h forward | +| Current Hour Average | Smoothed price around current time | 5 intervals (~75 min) | +| Next Hour Average | Smoothed price around next hour | 5 intervals (~75 min) | +| **Next N Hours Average** (`next_avg_1h`–`next_avg_12h`) | Future price forecast | 1h, 2h, 3h, 4h, 5h, 6h, 8h, 12h | + +## Configurable Display: Median vs Mean + +All average sensors support **two different calculation methods** for the state value: + +- **Median** (default): The "middle value" when all prices are sorted. Resistant to extreme price spikes, shows the **typical** price level you experienced. +- **Arithmetic Mean**: The mathematical average including all prices. Better for **cost calculations** but affected by extreme spikes. + +**Why two values matter:** + +```yaml +# Example price data for one day: +# Prices: 10, 12, 13, 15, 80 ct/kWh (one extreme spike) +# +# Median = 13 ct/kWh ← "Typical" price level (middle value) +# Mean = 26 ct/kWh ← Mathematical average (affected by spike) +``` + +The median shows you what price level was **typical** during that period, while the mean shows the actual **average cost** if you consumed evenly throughout the period. + +## Configuring the Display + +You can choose which value is displayed in the sensor state: + +1. Go to **Settings → Devices & Services → Tibber Prices** +2. Click **Configure** on your home +3. Navigate to **Step 6: Average Sensor Display Settings** +4. Choose between: + - **Median** (default) - Shows typical price level, resistant to spikes + - **Arithmetic Mean** - Shows actual mathematical average + +**Important:** Both values are **always available** as sensor attributes, regardless of your choice! This ensures your automations continue to work if you change the display setting. + +## Using Both Values in Automations + +Both `price_mean` and `price_median` are always available as attributes: + +```yaml +# Example: Get both values regardless of display setting +sensor: + - platform: template + sensors: + daily_price_analysis: + friendly_name: "Daily Price Analysis" + value_template: > + {% set median = state_attr('sensor._price_today', 'price_median') %} + {% set mean = state_attr('sensor._price_today', 'price_mean') %} + {% set current = states('sensor._current_electricity_price') | float %} + + {% if current < median %} + Below typical ({{ ((1 - current/median) * 100) | round(1) }}% cheaper) + {% elif current < mean %} + Typical price range + {% else %} + Above average ({{ ((current/mean - 1) * 100) | round(1) }}% more expensive) + {% endif %} +``` + +## Practical Examples + +**Example 1: Smart dishwasher control** + +Run dishwasher only when price is significantly below the daily typical level: + +
+Show YAML: Automation — start dishwasher when cheap + +```yaml +automation: + - alias: "Start Dishwasher When Cheap" + trigger: + - platform: state + entity_id: binary_sensor._best_price_period + to: "on" + condition: + # Only if current price is at least 20% below typical (median) + - condition: template + value_template: > + {% set current = states('sensor._current_electricity_price') | float %} + {% set median = state_attr('sensor._price_today', 'price_median') | float %} + {{ current < (median * 0.8) }} + action: + - service: switch.turn_on + entity_id: switch.dishwasher +``` + +
+ +**Example 2: Cost-aware heating control** + +Use mean for actual cost calculations: + +
+Show YAML: Automation — cost-aware heating control + +```yaml +automation: + - alias: "Heating Budget Control" + trigger: + - platform: time + at: "06:00:00" + action: + # Calculate expected daily heating cost + - variables: + mean_price: "{{ state_attr('sensor._price_today', 'price_mean') | float }}" + heating_kwh_per_day: 15 # Estimated consumption + daily_cost: "{{ (mean_price * heating_kwh_per_day / 100) | round(2) }}" + - service: notify.mobile_app + data: + title: "Heating Cost Estimate" + message: "Expected cost today: €{{ daily_cost }} (avg price: {{ mean_price }} ct/kWh)" +``` + +
+ +**Example 3: Smart charging based on rolling average** + +Use trailing average to understand recent price trends: + +
+Show YAML: Automation — EV charging based on rolling average + +```yaml +automation: + - alias: "EV Charging - Price Trend Based" + trigger: + - platform: state + entity_id: sensor.ev_battery_level + condition: + # Start charging if current price < 90% of recent 24h average + - condition: template + value_template: > + {% set current = states('sensor._current_electricity_price') | float %} + {% set trailing_avg = state_attr('sensor._price_trailing_24h', 'price_median') | float %} + {{ current < (trailing_avg * 0.9) }} + # And battery < 80% + - condition: numeric_state + entity_id: sensor.ev_battery_level + below: 80 + action: + - service: switch.turn_on + entity_id: switch.ev_charger +``` + +
+ +## Key Attributes + +All average sensors provide these attributes: + +| Attribute | Description | Example | +|-----------|-------------|---------| +| `price_mean` | Arithmetic mean (always available) | 25.3 ct/kWh | +| `price_median` | Median value (always available) | 22.1 ct/kWh | +| `interval_count` | Number of intervals included | 96 | +| `timestamp` | Reference time for calculation | 2025-12-18T00:00:00+01:00 | + +**Note:** The `price_mean` and `price_median` attributes are **always present** regardless of which value you configured for display. This ensures automation compatibility when changing the display setting. + +## When to Use Which Value + +**Use Median for:** +- ✅ Comparing "typical" price levels across days +- ✅ Determining if current price is unusually high/low +- ✅ User-facing displays ("What was today like?") +- ✅ Volatility analysis (comparing typical vs extremes) + +**Use Mean for:** +- ✅ Cost calculations and budgeting +- ✅ Energy cost estimations +- ✅ Comparing actual average costs between periods +- ✅ Financial planning and forecasting + +**Both values tell different stories:** +- High median + much higher mean = Expensive spikes occurred +- Low median + higher mean = Generally cheap with occasional spikes +- Similar median and mean = Stable prices (low volatility) diff --git a/docs/user/docs/sensors-energy-tax.md b/docs/user/docs/sensors-energy-tax.md new file mode 100644 index 0000000..1f844f6 --- /dev/null +++ b/docs/user/docs/sensors-energy-tax.md @@ -0,0 +1,110 @@ +# Energy & Tax Attributes + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +Most price sensors include **energy price** and **tax** attributes that break down the total price into its components: + +``` +total = energy_price + tax +``` + +These attributes appear on **all price sensors that display a raw price** (not on percentage, level, or trend sensors). The `energy_price` is the raw spot/market price, while `tax` includes all fees, surcharges, and taxes added by your electricity provider. + +:::note Transition After Update +After updating the integration, the `energy_price` and `tax` attributes will appear gradually as new price data is fetched from the Tibber API. Existing cached intervals (up to ~2 days old) won't have these fields yet — the attributes will simply be absent until fresh data replaces them. No action needed. +::: + +## Where These Attributes Appear + +| Sensor Type | `energy_price` | `tax` | Notes | +|-------------|:-:|:-:|-------| +| Current/Next/Previous Interval Price | ✅ | ✅ | Single interval values | +| Rolling Hour Average | ✅ | ✅ | Averaged across 5 intervals | +| Daily Min/Max/Average | ✅ | ✅ | Aggregated for the day | +| Trailing/Leading 24h | ✅ | ✅ | Aggregated across window | +| Future Average (N-hour) | ✅ | ✅ | Averaged across future window | +| Levels, Ratings, Trends | ❌ | ❌ | Not price sensors | +| Volatility | ❌ | ❌ | Statistical, not price | + +## Use Cases + +### Solar Feed-In & Net Metering (Saldering) + +In countries like the Netherlands, solar feed-in compensation is based on the **raw energy/spot price**, not the total consumer price. The `energy_price` attribute gives you exactly this value — no more reverse-engineering from the total price with fragile template calculations. + +
+Show YAML: Automation — solar export or consume decision + +```yaml +# Example: Decide whether to export solar power or consume it +# Compare energy price (what you'd earn by exporting) vs. total price (what you'd pay) +automation: + - alias: "Solar: Export or Consume" + trigger: + - platform: numeric_state + entity_id: sensor.solar_production_power + above: 2000 # Producing more than 2 kW + condition: + - condition: template + value_template: > + {% set energy = state_attr('sensor._current_electricity_price', 'energy_price') %} + {% set total = states('sensor._current_electricity_price') | float %} + {# Export when energy price is high relative to total — you earn more #} + {{ energy is not none and energy > (total * 0.4) }} + action: + - service: switch.turn_off + entity_id: switch.battery_charging # Don't charge battery, export instead +``` + +
+ +### Price Composition Analysis + +Understand how your electricity price is structured — useful for comparing across days or spotting trends in market prices vs. fees: + +
+Show YAML: Template sensor — electricity tax share percentage + +```yaml +# Template sensor showing tax share +template: + - sensor: + - name: "Electricity Tax Share" + unit_of_measurement: "%" + state: > + {% set tax = state_attr('sensor._current_electricity_price', 'tax') %} + {% set total = states('sensor._current_electricity_price') | float %} + {% if tax is not none and total > 0 %} + {{ ((tax / total) * 100) | round(1) }} + {% else %} + unavailable + {% endif %} +``` + +
+ +### Dashboard: Daily Cost Breakdown + +Show users how today's average price splits into energy vs. tax: + +```yaml +# Mushroom chips card showing the split +type: custom:mushroom-chips-card +chips: + - type: template + icon: mdi:flash + content: > + ⚡ {{ state_attr('sensor._price_today', 'energy_price_mean') | round(1) }} ct + - type: template + icon: mdi:receipt-text + content: > + 🏛️ {{ state_attr('sensor._price_today', 'tax_mean') | round(1) }} ct +``` + +## Country-Specific Calculations + +The composition of the `tax` field varies by country (Norway, Sweden, Germany, Netherlands each have different fee structures). For detailed examples of how to build country-specific calculations using `input_number` helpers and template sensors — including **Dutch solar feed-in compensation (saldering)** — see the **[Community Examples](community-examples.md#country-specific-price-calculations)** page. + +## In Chart Data Actions + +The `energy_price` and `tax` fields are also available in the `get_chartdata` action. See [Actions — Energy & Tax Fields](./actions.md#energy--tax-fields-in-get_chartdata) for details. diff --git a/docs/user/docs/sensors-overview.md b/docs/user/docs/sensors-overview.md new file mode 100644 index 0000000..d0bbb1a --- /dev/null +++ b/docs/user/docs/sensors-overview.md @@ -0,0 +1,183 @@ +# Sensors Overview + +> **Tip:** Many sensors have dynamic icons and colors! See the **[Dynamic Icons Guide](dynamic-icons.md)** and **[Dynamic Icon Colors Guide](icon-colors.md)** to enhance your dashboards. + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +The integration provides **100+ sensors** organized by purpose. This page gives a quick overview and links to detailed guides for each sensor family. + +| Sensor Family | Purpose | Guide | +|---|---|---| +| **Binary Sensors** | Period on/off indicators | [below](#binary-sensors) | +| **Core Price** | Current, next, previous interval prices | [below](#core-price-sensors) | +| **Average & Statistics** | Daily averages, rolling averages, median/mean | [Average Sensors](sensors-average.md) | +| **Ratings & Levels** | Price classification (3-level ratings, 5-level API levels) | [Ratings & Levels](sensors-ratings-levels.md) | +| **Min/Max** | Daily and rolling 24h extremes | [below](#minmax-sensors) | +| **Volatility** | Price fluctuation analysis | [Volatility Sensors](sensors-volatility.md) | +| **Trends** | Price outlook, trajectory, direction | [Trend Sensors](sensors-trends.md) | +| **Timing** | Period countdown, progress, duration | [Timing Sensors](sensors-timing.md) | +| **Energy & Tax** | Spot price and tax breakdown | [Energy & Tax](sensors-energy-tax.md) | +| **Diagnostic** | Chart metadata, data export | [below](#diagnostic-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 + +The integration provides price sensors for the **current**, **next**, and **previous** 15-minute interval. Each exposes the total price as sensor state, with `energy_price` and `tax` available as attributes (see [Energy & Tax Breakdown](sensors-energy-tax.md)). + +**Next N Hours Average** sensors (`next_avg_1h`–`next_avg_12h`) provide future price forecasts for 1h, 2h, 3h, 4h, 5h, 6h, 8h, and 12h windows. + +For detailed average sensor behavior (median vs mean, configuration, automation examples), see **[Average & Statistics Sensors](sensors-average.md)**. + +## Min/Max Sensors + +These sensors show the lowest and highest prices for calendar days and rolling windows: + +### Daily Min/Max + +| Sensor | Description | +|--------|-------------| +| Today's Lowest Price | Minimum price today (00:00–23:59) | +| Today's Highest Price | Maximum price today (00:00–23:59) | +| Tomorrow's Lowest Price | Minimum price tomorrow | +| Tomorrow's Highest Price | Maximum price tomorrow | + +### 24-Hour Rolling Min/Max + +| Sensor | Description | +|--------|-------------| +| Trailing Price Min | Lowest price in the last 24 hours | +| Trailing Price Max | Highest price in the last 24 hours | +| Leading Price Min | Lowest price in the next 24 hours | +| Leading Price Max | Highest price in the next 24 hours | + +### Key Attributes + +All min/max sensors include: + +| Attribute | Description | +|-----------|-------------| +| `timestamp` | When the extreme price occurs/occurred | +| `price_diff_from_daily_min` | Difference from daily minimum | +| `price_diff_from_daily_min_%` | Percentage difference | + +## Diagnostic Sensors + +### Chart Metadata + +**Entity ID:** `sensor._chart_metadata` + +> **✨ New Feature**: This sensor provides dynamic chart configuration metadata for optimal visualization. Perfect for use with the `get_apexcharts_yaml` action! + +This diagnostic sensor provides essential chart configuration values as sensor attributes, enabling dynamic Y-axis scaling and optimal chart appearance in rolling window modes. + +**Key Features:** + +- **Dynamic Y-Axis Bounds**: Automatically calculates optimal `yaxis_min` and `yaxis_max` for your price data +- **Automatic Updates**: Refreshes when price data changes (coordinator updates) +- **Lightweight**: Metadata-only mode (no data processing) for fast response +- **State Indicator**: Shows `pending` (initialization), `ready` (data available), or `error` (service call failed) + +**Attributes:** + +- **`timestamp`**: When the metadata was last fetched +- **`yaxis_min`**: Suggested minimum value for Y-axis (optimal scaling) +- **`yaxis_max`**: Suggested maximum value for Y-axis (optimal scaling) +- **`currency`**: Currency code (e.g., "EUR", "NOK") +- **`resolution`**: Interval duration in minutes (usually 15) +- **`error`**: Error message if service call failed + +**Usage:** + +The `tibber_prices.get_apexcharts_yaml` action **automatically uses this sensor** for dynamic Y-axis scaling in `rolling_window` and `rolling_window_autozoom` modes! No manual configuration needed - just enable the action's result with `config-template-card` and the sensor provides optimal Y-axis bounds automatically. + +See the **[Chart Examples Guide](chart-examples.md)** for practical examples! + +--- + +### Chart Data Export + +**Entity ID:** `sensor._chart_data_export` +**Default State:** Disabled (must be manually enabled) + +> **⚠️ Legacy Feature**: This sensor is maintained for backward compatibility. For new integrations, use the **`tibber_prices.get_chartdata`** action instead, which offers more flexibility and better performance. + +This diagnostic sensor provides cached chart-friendly price data that can be consumed by chart cards (ApexCharts, custom cards, etc.). + +**Key Features:** + +- **Configurable via Options Flow**: Service parameters can be configured through the integration's options menu (Step 7 of 7) +- **Automatic Updates**: Data refreshes on coordinator updates (every 15 minutes) +- **Attribute-Based Output**: Chart data is stored in sensor attributes for easy access +- **State Indicator**: Shows `pending` (before first call), `ready` (data available), or `error` (service call failed) + +**Important Notes:** + +- ⚠️ Disabled by default - must be manually enabled in entity settings +- ⚠️ Consider using the action instead for better control and flexibility +- ⚠️ Configuration updates require HA restart + +**Attributes:** + +The sensor exposes chart data with metadata in attributes: + +- **`timestamp`**: When the data was last fetched +- **`error`**: Error message if service call failed +- **`data`** (or custom name): Array of price data points in configured format + +**Configuration:** + +To configure the sensor's output format: + +1. Go to **Settings → Devices & Services → Tibber Prices** +2. Click **Configure** on your Tibber home +3. Navigate through the options wizard to **Step 7: Chart Data Export Settings** +4. Configure output format, filters, field names, and other options +5. Save and restart Home Assistant + +**Available Settings:** + +See the `tibber_prices.get_chartdata` action documentation for a complete list of available parameters. All action parameters can be configured through the options flow. + +**Example Usage:** + +```yaml +# ApexCharts card consuming the sensor +type: custom:apexcharts-card +series: + - entity: sensor._chart_data_export + data_generator: | + return entity.attributes.data; +``` + +**Migration Path:** + +If you're currently using this sensor, consider migrating to the action: + +```yaml +# Old approach (sensor) +- service: apexcharts_card.update + data: + entity: sensor._chart_data_export + +# New approach (action) +- service: tibber_prices.get_chartdata + data: + entry_id: YOUR_CONFIG_ENTRY_ID + day: ["today", "tomorrow"] + output_format: array_of_objects + response_variable: chart_data +``` diff --git a/docs/user/docs/sensors-ratings-levels.md b/docs/user/docs/sensors-ratings-levels.md new file mode 100644 index 0000000..cbd18d8 --- /dev/null +++ b/docs/user/docs/sensors-ratings-levels.md @@ -0,0 +1,140 @@ +# Ratings & Levels + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +The integration provides **two** classification systems for electricity prices. Both are useful, but serve different purposes. + +## Ratings vs Levels at a Glance + +| | Ratings | Levels | +|--|---------|--------| +| **Source** | Calculated by integration | Provided by Tibber API | +| **Scale** | 3 levels (LOW, NORMAL, HIGH) | 5 levels (VERY_CHEAP → VERY_EXPENSIVE) | +| **Basis** | Trailing 24h average | Daily min/max range | +| **Best for** | Automations (simple thresholds) | Dashboard displays (fine granularity) | +| **Configurable** | Yes (thresholds) | Gap tolerance only | +| **Automation attribute** | `rating_level` (always lowercase English) | `level` (always uppercase English) | + +**Which to use?** + +- **Automations**: Use **ratings** (3 simple states, configurable thresholds, hysteresis) +- **Dashboards**: Use **levels** (5 color-coded states, more visual granularity) +- **Advanced automations**: Combine both (e.g., "LOW rating AND VERY_CHEAP level") + +--- + +## Rating Sensors + +Rating sensors classify prices relative to the **trailing 24-hour average**, answering: "Is the current price cheap, normal, or expensive compared to recent history?" + +### How Ratings Work + +The integration calculates a **percentage difference** between the current price and the trailing 24-hour average: + +``` +difference = ((current_price - trailing_avg) / abs(trailing_avg)) × 100% +``` + +This percentage is then classified: + +| Rating | Condition (default) | Meaning | +|--------|---------------------|---------| +| **LOW** | difference ≤ -10% | Significantly below recent average | +| **NORMAL** | -10% < difference < +10% | Within normal range | +| **HIGH** | difference ≥ +10% | Significantly above recent average | + +**Hysteresis** (default 2%) prevents flickering: once a rating enters LOW, it must cross -8% (not -10%) to return to NORMAL. This avoids rapid switching at threshold boundaries. + +```mermaid +stateDiagram-v2 + direction LR + + LOW: 🟢 LOW
price ≤ −10% + NORMAL: 🟡 NORMAL
−10% … +10% + HIGH: 🔴 HIGH
price ≥ +10% + + LOW --> NORMAL: crosses −8%
(hysteresis) + NORMAL --> LOW: drops below −10% + NORMAL --> HIGH: rises above +10% + HIGH --> NORMAL: crosses +8%
(hysteresis) +``` + +> **The 2% gap** between entering (−10%) and leaving (−8%) a state prevents the sensor from flickering back and forth when prices hover near a threshold. + +### Available Rating Sensors + +| Sensor | Scope | Description | +|--------|-------|-------------| +| Current Price Rating | Current interval | Rating of the current 15-minute price | +| Next Price Rating | Next interval | Rating for the upcoming 15-minute price | +| Previous Price Rating | Previous interval | Rating for the past 15-minute price | +| Current Hour Price Rating | Rolling 5-interval | Smoothed rating around the current hour | +| Next Hour Price Rating | Rolling 5-interval | Smoothed rating around the next hour | +| Yesterday's Price Rating | Calendar day | Aggregated rating for yesterday | +| Today's Price Rating | Calendar day | Aggregated rating for today | +| Tomorrow's Price Rating | Calendar day | Aggregated rating for tomorrow | + +### Key Attributes + +| Attribute | Description | Example | +|-----------|-------------|---------| +| `rating_level` | Language-independent rating (always lowercase) | `low` | +| `difference` | Percentage difference from trailing average | `-12.5` | +| `trailing_avg_24h` | The reference average used for classification | `22.3` | + +### Usage in Automations + +**Best Practice:** Always use the `rating_level` attribute (lowercase English) instead of the sensor state (which is translated to your HA language): + +```yaml +# ✅ Correct — language-independent +condition: + - condition: template + value_template: > + {{ state_attr('sensor._current_price_rating', 'rating_level') == 'low' }} + +# ❌ Avoid — breaks when HA language changes +condition: + - condition: state + entity_id: sensor._current_price_rating + state: "Low" # "Niedrig" in German, "Lav" in Norwegian... +``` + +### Configuration + +Rating thresholds can be adjusted in the options flow: + +1. Go to **Settings → Devices & Services → Tibber Prices → Configure** +2. Navigate to **Price Rating Thresholds** +3. Adjust LOW/HIGH thresholds, hysteresis, and gap tolerance + +See [Configuration](configuration.md#step-3-price-rating-thresholds) for details. + +--- + +## Level Sensors + +Level sensors show the **Tibber API's own price classification** with a 5-level scale: + +| Level | Meaning | Numeric Value | +|-------|---------|---------------| +| **VERY_CHEAP** | Exceptionally low | -2 | +| **CHEAP** | Below average | -1 | +| **NORMAL** | Typical range | 0 | +| **EXPENSIVE** | Above average | +1 | +| **VERY_EXPENSIVE** | Exceptionally high | +2 | + +### Available Level Sensors + +| Sensor | Scope | +|--------|-------| +| Current Price Level | Current interval | +| Next Price Level | Next interval | +| Previous Price Level | Previous interval | +| Current Hour Price Level | Rolling 5-interval window | +| Next Hour Price Level | Rolling 5-interval window | +| Yesterday's Price Level | Calendar day (aggregated) | +| Today's Price Level | Calendar day (aggregated) | +| Tomorrow's Price Level | Calendar day (aggregated) | + +**Gap tolerance** smoothing is applied to prevent isolated level flickers (e.g., a single NORMAL between two CHEAPs → corrected to CHEAP). Configure in [options flow](configuration.md#step-4-price-level-gap-tolerance). diff --git a/docs/user/docs/sensors-timing.md b/docs/user/docs/sensors-timing.md new file mode 100644 index 0000000..9618369 --- /dev/null +++ b/docs/user/docs/sensors-timing.md @@ -0,0 +1,95 @@ +# Timing Sensors + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +Timing sensors provide **real-time information about Best Price and Peak Price periods**: when they start, end, how long they last, and your progress through them. + +```mermaid +stateDiagram-v2 + direction LR + + IDLE: ⏸️ IDLE
No active period + ACTIVE: ▶️ ACTIVE
In period + GRACE: ⏳ GRACE
60s buffer + + IDLE --> ACTIVE: period starts + ACTIVE --> GRACE: period ends + GRACE --> IDLE: 60s elapsed + GRACE --> ACTIVE: new period starts
(within grace) +``` + +**IDLE** = waiting for next period (shows countdown via `next_in_minutes`). **ACTIVE** = inside a period (shows `progress` 0–100% and `remaining_minutes`). **GRACE** = short buffer after a period ends, allowing back-to-back periods to merge seamlessly. + +## Available Timing Sensors + +For each period type (Best Price and Peak Price): + +| Sensor | When Period Active | When No Active Period | +|--------|-------------------|----------------------| +| End Time | Current period's end time | Next period's end time | +| Period Duration | Current period length (minutes) | Next period length | +| Remaining Minutes | Minutes until current period ends | 0 | +| Progress | 0–100% through current period | 0 | +| Next Start Time | When next-next period starts | When next period starts | +| Next In Minutes | Minutes to next-next period | Minutes to next period | + +## Usage Examples + +### Show Countdown to Next Cheap Window + +```yaml +type: custom:mushroom-entity-card +entity: sensor._best_price_next_in_minutes +name: Next Cheap Window +icon: mdi:clock-fast +``` + +### Display Period Progress Bar + +
+Show YAML: Bar card for period progress + +```yaml +type: custom:bar-card +entity: sensor._best_price_progress +name: Best Price Progress +min: 0 +max: 100 +severity: + - from: 0 + to: 50 + color: green + - from: 50 + to: 80 + color: orange + - from: 80 + to: 100 + color: red +``` + +
+ +### Notify When Period Is Almost Over + +
+Show YAML: Automation — notify when best price period is ending + +```yaml +automation: + - alias: "Warn: Best Price Ending Soon" + trigger: + - platform: numeric_state + entity_id: sensor._best_price_remaining_minutes + below: 15 + condition: + - condition: numeric_state + entity_id: sensor._best_price_remaining_minutes + above: 0 + action: + - service: notify.mobile_app + data: + title: "Best Price Ending Soon" + message: "Only {{ states('sensor._best_price_remaining_minutes') }} minutes left!" +``` + +
diff --git a/docs/user/docs/sensors-trends.md b/docs/user/docs/sensors-trends.md new file mode 100644 index 0000000..752c27c --- /dev/null +++ b/docs/user/docs/sensors-trends.md @@ -0,0 +1,266 @@ +# Trend Sensors + +> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. + +Trend sensors help you understand **whether to act now or wait**. The integration provides two complementary families: + +- **Price Outlook Sensors (1h–12h):** Compare current price vs. future window average — "Is now cheaper than the next Nh on average?" +- **Price Trajectory Sensors (2h–12h):** Compare first half vs. second half of the window — "Are prices rising or falling *within* the window?" + +--- + +## Price Outlook Sensors (1h–12h) + +These sensors compare the **current price** with the **average price** of the next N hours: + +| Sensor | Compares Against | +|--------|-----------------| +| **Price Outlook (1h)** (`price_outlook_1h`) | Average of next 1 hour | +| **Price Outlook (2h)** (`price_outlook_2h`) | Average of next 2 hours | +| **Price Outlook (3h)** (`price_outlook_3h`) | Average of next 3 hours | +| **Price Outlook (4h)** (`price_outlook_4h`) | Average of next 4 hours | +| **Price Outlook (5h)** (`price_outlook_5h`) | Average of next 5 hours | +| **Price Outlook (6h)** (`price_outlook_6h`) | Average of next 6 hours | +| **Price Outlook (8h)** (`price_outlook_8h`) | Average of next 8 hours | +| **Price Outlook (12h)** (`price_outlook_12h`) | Average of next 12 hours | + +:::info Same Starting Point — All Outlook Sensors Use Your Current Price +All outlook sensors share the **same base: your current 15-minute price**. They differ only in how far ahead they average. The windows **overlap** — the 3h average includes ALL intervals from the 1h and 2h windows, plus one more hour. + +**This means:** +- `price_outlook_3h` shows "current price vs. average of the **entire** next 3 hours" — **not** "what happens between hour 2 and hour 3" +- If 1h shows `falling` but 6h shows `rising`: near-term prices are below your current price, but looking at the full 6h window (which includes expensive evening hours), the overall average is above your current price +- Larger windows smooth out short-term fluctuations — a 30-minute price spike affects the 1h average more than the 6h average + +**⚠️ At a price minimum, outlook sensors can be misleading!** If you're at the minimum and prices are about to rise, `price_outlook_3h` may still show `strongly_falling` because the cheap minimum pulls the 3h average below your current high price. Use `price_trajectory_3h` to see the direction *within* the window. +::: + +**States:** Each sensor has one of five states: + +```mermaid +stateDiagram-v2 + direction LR + + SF: ⬇️⬇️ strongly_falling
−2 · future ≤ −9% + F: ⬇️ falling
−1 · future ≤ −3% + S: ➡️ stable
0 · within ±3% + R: ⬆️ rising
+1 · future ≥ +3% + SR: ⬆️⬆️ strongly_rising
+2 · future ≥ +9% + + SF --> F: price recovers + F --> S: approaches average + S --> R: future rises + R --> SR: accelerates + SR --> R: slows down + R --> S: stabilizes + S --> F: future drops + F --> SF: accelerates +``` + +| State | Meaning | `trend_value` | +|-------|---------|---------------| +| `strongly_falling` | Prices will drop significantly | -2 | +| `falling` | Prices will drop | -1 | +| `stable` | Prices staying roughly the same | 0 | +| `rising` | Prices will increase | +1 | +| `strongly_rising` | Prices will increase significantly | +2 | + +**Key attributes:** + +| Attribute | Description | Example | +|-----------|-------------|---------| +| `trend_value` | Numeric value for automations (-2 to +2) | `-1` | +| `trend_Nh_%` | Percentage difference from current price | `-12.3` | +| `next_Nh_avg` | Average price in the future window | `18.5` | +| `second_half_Nh_avg` | Average price in later half of window | `16.2` | +| `threshold_rising_%` | Active rising threshold after volatility adjustment | `3.0` | +| `threshold_rising_strongly_%` | Active strongly-rising threshold after volatility adjustment | `4.8` | +| `threshold_falling_%` | Active falling threshold after volatility adjustment | `-3.0` | +| `threshold_falling_strongly_%` | Active strongly-falling threshold after volatility adjustment | `-4.8` | +| `volatility_factor` | Applied multiplier (0.6 = low, 1.0 = moderate, 1.4 = high volatility) | `0.8` | + +**Tip:** The `trend_value` attribute (`-2` to `+2`) is ideal for automations — use numeric comparisons instead of matching translated state strings. + +--- + +## Price Trajectory Sensors (2h–12h) + +These sensors compare the **first half** of the future window against the **second half** — revealing the price *direction within* the window. + +| Sensor | Compares | +|--------|----------| +| **Price Trajectory (2h)** (`price_trajectory_2h`) | Avg of hour 1 vs avg of hour 2 | +| **Price Trajectory (3h)** (`price_trajectory_3h`) | Avg of first 1.5h vs avg of second 1.5h | +| **Price Trajectory (4h)** (`price_trajectory_4h`) | Avg of first 2h vs avg of second 2h | +| **Price Trajectory (5h)** (`price_trajectory_5h`) | Avg of first 2.5h vs avg of second 2.5h | +| **Price Trajectory (6h)** (`price_trajectory_6h`) | Avg of first 3h vs avg of second 3h | +| **Price Trajectory (8h)** (`price_trajectory_8h`) | Avg of first 4h vs avg of second 4h | +| **Price Trajectory (12h)** (`price_trajectory_12h`) | Avg of first 6h vs avg of second 6h | + +**States:** Same 5-level scale as outlook sensors (`strongly_falling` → `strongly_rising`). + +:::info Why trajectory sensors complement outlook sensors +**At a price minimum** — the exact moment you should act — `price_outlook_3h` may show `strongly_falling` because the cheap minimum pulls the entire 3h average below your current high price. But `price_trajectory_3h` shows `rising` because the second half (after the minimum) is more expensive than the first half. + +| Combination | Interpretation | +|-------------|----------------| +| Outlook `falling` + Trajectory `rising` | **You're AT the minimum** — act now | +| Outlook `falling` + Trajectory `falling` | Prices still dropping — wait | +| Outlook `rising` + Trajectory `rising` | Strong signal to act now | +| Outlook `rising` + Trajectory `falling` | Short spike, then cheaper — wait | +::: + +**Key attributes:** + +| Attribute | Description | Example | +|-----------|-------------|---------| +| `trend_value` | Numeric value for automations (-2 to +2) | `1` | +| `first_half_avg` | Average price in first half of window | `12.4` | +| `second_half_avg` | Average price in second half of window | `18.1` | +| `half_diff_%` | Percentage difference (second vs first half) | `46.0` | + +--- + +## Current Price Trend + +**Entity ID:** `sensor._current_price_trend` + +This sensor shows the **currently active trend direction** based on a 3-hour future outlook with volatility-adaptive thresholds. + +Unlike the simple trend sensors that always compare current price vs future average, the current price trend represents the **ongoing trend** — it remains stable between updates and only changes when the underlying price direction actually shifts. + +**States:** Same 5-level scale as simple trends. + +**Key attributes:** + +| Attribute | Description | Example | +|-----------|-------------|---------| +| `previous_direction` | Price direction before the current trend started | `falling` | +| `price_direction_duration_minutes` | How long prices have been moving in this direction | `45` | +| `price_direction_since` | Timestamp when prices started moving in this direction | `2025-11-08T14:00:00+01:00` | + +--- + +## Next Price Trend Change + +**Entity ID:** `sensor._next_price_trend_change` + +This sensor predicts **when the current trend will change** by scanning future intervals. It requires 3 consecutive intervals (configurable: 2–6) confirming the new trend before reporting a change (hysteresis), which prevents false alarms from short-lived price spikes. + +**Important:** Only **direction changes** count as trend changes. The five states are grouped into three directions: + +| Direction | States | +|-----------|--------| +| **falling** | `strongly_falling`, `falling` | +| **stable** | `stable` | +| **rising** | `rising`, `strongly_rising` | + +A change from `rising` to `strongly_rising` (same direction) is **not** reported as a trend change — only actual reversals like `rising` → `stable` or `falling` → `rising`. + +**State:** Timestamp of the next trend change (or unavailable if no change predicted). + +**Key attributes:** + +| Attribute | Description | Example | +|-----------|-------------|---------| +| `direction` | What the trend will change TO | `rising` | +| `from_direction` | Current trend (will change FROM) | `falling` | +| `minutes_until_change` | Minutes until trend changes | `90` | +| `price_at_change` | Price at the change point | `13.8` | +| `price_avg_after_change` | Average price after change | `18.1` | +| `threshold_rising_%` | Active rising threshold after volatility adjustment | `3.0` | +| `threshold_rising_strongly_%` | Active strongly-rising threshold after volatility adjustment | `4.8` | +| `threshold_falling_%` | Active falling threshold after volatility adjustment | `-3.0` | +| `threshold_falling_strongly_%` | Active strongly-falling threshold after volatility adjustment | `-4.8` | +| `volatility_factor` | Applied multiplier (0.6 = low, 1.0 = moderate, 1.4 = high volatility) | `0.8` | + +--- + +## How to Use Trend Sensors for Decisions + +:::danger Common Misconception — Don't "Wait for Stable"! +A natural intuition is to treat trend states like a stock ticker: + +- ❌ "It's **falling** → I'll wait until it reaches **stable** (the bottom)" +- ❌ "It's **rising** → too late, I missed the best price" +- ❌ "It's **stable** → now is the perfect time to act!" + +**This is wrong.** Trend sensors don't show a trajectory — they show a **comparison** between your current price and future prices. The correct interpretation is the opposite: + +| State | What the Sensor Calculates | ✅ Correct Action | +|-------|---------------------------|-------------------| +| `falling` | Current price **higher** than future average | **WAIT** — cheaper prices are coming | +| `strongly_falling` | Current price **much higher** than future average | **DEFINITELY WAIT** — significant savings ahead | +| `stable` | Current price **≈ equal** to future average | **Timing doesn't matter** — start whenever convenient | +| `rising` | Current price **lower** than future average | **ACT NOW** — it only gets more expensive | +| `strongly_rising` | Current price **much lower** than future average | **ACT IMMEDIATELY** — best price right now | + +**"Rising" is NOT "too late" — it means NOW is the best time because prices will be higher later.** +::: + +### Basic Automation Pattern + +For most appliances (dishwasher, washing machine, dryer), a single outlook sensor is enough: + +```yaml +# Example: Start dishwasher when prices are favorable +trigger: + - platform: state + entity_id: sensor.my_home_price_outlook_3h +condition: + - condition: numeric_state + entity_id: sensor.my_home_price_outlook_3h + attribute: trend_value + # rising (1) or strongly_rising (2) = act now + above: 0 +action: + - service: switch.turn_on + target: + entity_id: switch.dishwasher +``` + +### Combining Multiple Windows + +When short-term and long-term trends disagree, you get richer insight: + +| 1h Outlook | 6h Outlook | Interpretation | Recommendation | +|----------|----------|---------------|----------------| +| `rising` | `rising` | Prices going up across the board | **Start now** | +| `falling` | `falling` | Prices dropping across the board | **Wait** | +| `falling` | `rising` | Brief dip, then expensive evening | **Wait briefly**, then start during the dip | +| `rising` | `falling` | Short spike, but cheaper hours ahead | **Wait** if you can — better prices coming | +| `stable` | any | Short-term doesn't matter | Use the **longer window** for your decision | + +### Dashboard Quick-Glance + +On your dashboard, trend sensors give an instant overview: + +- 🟢 All **falling/strongly_falling** → "Relax, prices are dropping — wait" +- 🔴 All **rising/strongly_rising** → "Start everything you can — it only gets more expensive" +- 🟡 **Mixed** → Compare short-term vs. long-term sensors, or check the Best Price Period sensor + +--- + +## Outlook & Trajectory vs Average Sensors + +Both sensor families provide future price information, but serve different purposes: + +| | Outlook/Trajectory Sensors | Average Sensors | +|--|---------------------------|-----------------| +| **Purpose** | Dashboard display, quick visual overview | Automations, precise numeric comparisons | +| **Output** | Classification (falling/stable/rising) | Exact price values (ct/kWh) | +| **Best for** | "Should I worry about prices?" | "Is the future average below 15 ct?" | +| **Use in** | Dashboard icons, status displays | Template conditions, numeric thresholds | + +**Design principle:** Use **trend sensors** (enum) for visual feedback at a glance, use **average sensors** (numeric) for precise decision-making in automations. + +## Configuration + +Trend thresholds can be adjusted in the options flow: + +1. Go to **Settings → Devices & Services → Tibber Prices** +2. Click **Configure** on your home +3. Navigate to **📈 Price Trend Thresholds** +4. Adjust the rising/falling and strongly rising/falling percentages + +The thresholds are **volatility-adaptive**: on days with high price volatility, thresholds are widened automatically to prevent constant state changes. This means the trend sensors give more stable readings during volatile market conditions. diff --git a/docs/user/docs/sensors-volatility.md b/docs/user/docs/sensors-volatility.md new file mode 100644 index 0000000..92c094b --- /dev/null +++ b/docs/user/docs/sensors-volatility.md @@ -0,0 +1,99 @@ +# Volatility Sensors + +> **Entity ID tip:** `` 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)](sensor-reference.md)** 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: +1. Go to **Settings → Devices & Services → Tibber Prices**. +2. Click **Configure**. +3. 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_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` | +| `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 `state` of 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. The `price_volatility` attribute 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. +```yaml +automation: + - alias: "Enable battery optimization only on volatile days" + trigger: + - platform: template + value_template: > + {{ state_attr('sensor._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_volatility` attribute, 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. +```yaml +automation: + - alias: "Brittle - Enable battery optimization" + trigger: + # + # BAD: Avoid hard-coding numeric values + # + - platform: numeric_state + entity_id: sensor._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. diff --git a/docs/user/docs/sensors.md b/docs/user/docs/sensors.md deleted file mode 100644 index f4c4aa8..0000000 --- a/docs/user/docs/sensors.md +++ /dev/null @@ -1,1038 +0,0 @@ ---- -comments: false ---- - -# Sensors - -> **Tip:** Many sensors have dynamic icons and colors! See the **[Dynamic Icons Guide](dynamic-icons.md)** and **[Dynamic Icon Colors Guide](icon-colors.md)** to enhance your dashboards. - -> **Entity ID tip:** `` 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)](sensor-reference.md)** to search by name in your language. - -## 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 - -### Average Price Sensors - -The integration provides several sensors that calculate average electricity prices over different time windows. These sensors show a **typical** price value that represents the overall price level, helping you make informed decisions about when to use electricity. - -#### Available Average Sensors - -| Sensor | Description | Time Window | -|--------|-------------|-------------| -| Average Price Today | Typical price for current calendar day | 00:00 - 23:59 today | -| Average Price Tomorrow | Typical price for next calendar day | 00:00 - 23:59 tomorrow | -| Trailing Price Average | Typical price for last 24 hours | Rolling 24h backward | -| Leading Price Average | Typical price for next 24 hours | Rolling 24h forward | -| Current Hour Average | Smoothed price around current time | 5 intervals (~75 min) | -| Next Hour Average | Smoothed price around next hour | 5 intervals (~75 min) | -| **Next N Hours Average** (`next_avg_1h`–`next_avg_12h`) | Future price forecast | 1h, 2h, 3h, 4h, 5h, 6h, 8h, 12h | - -#### Configurable Display: Median vs Mean - -All average sensors support **two different calculation methods** for the state value: - -- **Median** (default): The "middle value" when all prices are sorted. Resistant to extreme price spikes, shows the **typical** price level you experienced. -- **Arithmetic Mean**: The mathematical average including all prices. Better for **cost calculations** but affected by extreme spikes. - -**Why two values matter:** - -```yaml -# Example price data for one day: -# Prices: 10, 12, 13, 15, 80 ct/kWh (one extreme spike) -# -# Median = 13 ct/kWh ← "Typical" price level (middle value) -# Mean = 26 ct/kWh ← Mathematical average (affected by spike) -``` - -The median shows you what price level was **typical** during that period, while the mean shows the actual **average cost** if you consumed evenly throughout the period. - -#### Configuring the Display - -You can choose which value is displayed in the sensor state: - -1. Go to **Settings → Devices & Services → Tibber Prices** -2. Click **Configure** on your home -3. Navigate to **Step 6: Average Sensor Display Settings** -4. Choose between: - - **Median** (default) - Shows typical price level, resistant to spikes - - **Arithmetic Mean** - Shows actual mathematical average - -**Important:** Both values are **always available** as sensor attributes, regardless of your choice! This ensures your automations continue to work if you change the display setting. - -#### Using Both Values in Automations - -Both `price_mean` and `price_median` are always available as attributes: - -```yaml -# Example: Get both values regardless of display setting -sensor: - - platform: template - sensors: - daily_price_analysis: - friendly_name: "Daily Price Analysis" - value_template: > - {% set median = state_attr('sensor._price_today', 'price_median') %} - {% set mean = state_attr('sensor._price_today', 'price_mean') %} - {% set current = states('sensor._current_electricity_price') | float %} - - {% if current < median %} - Below typical ({{ ((1 - current/median) * 100) | round(1) }}% cheaper) - {% elif current < mean %} - Typical price range - {% else %} - Above average ({{ ((current/mean - 1) * 100) | round(1) }}% more expensive) - {% endif %} -``` - -#### Practical Examples - -**Example 1: Smart dishwasher control** - -Run dishwasher only when price is significantly below the daily typical level: - -
-Show YAML: Automation — start dishwasher when cheap - -```yaml -automation: - - alias: "Start Dishwasher When Cheap" - trigger: - - platform: state - entity_id: binary_sensor._best_price_period - to: "on" - condition: - # Only if current price is at least 20% below typical (median) - - condition: template - value_template: > - {% set current = states('sensor._current_electricity_price') | float %} - {% set median = state_attr('sensor._price_today', 'price_median') | float %} - {{ current < (median * 0.8) }} - action: - - service: switch.turn_on - entity_id: switch.dishwasher -``` - -
- -**Example 2: Cost-aware heating control** - -Use mean for actual cost calculations: - -
-Show YAML: Automation — cost-aware heating control - -```yaml -automation: - - alias: "Heating Budget Control" - trigger: - - platform: time - at: "06:00:00" - action: - # Calculate expected daily heating cost - - variables: - mean_price: "{{ state_attr('sensor._price_today', 'price_mean') | float }}" - heating_kwh_per_day: 15 # Estimated consumption - daily_cost: "{{ (mean_price * heating_kwh_per_day / 100) | round(2) }}" - - service: notify.mobile_app - data: - title: "Heating Cost Estimate" - message: "Expected cost today: €{{ daily_cost }} (avg price: {{ mean_price }} ct/kWh)" -``` - -
- -**Example 3: Smart charging based on rolling average** - -Use trailing average to understand recent price trends: - -
-Show YAML: Automation — EV charging based on rolling average - -```yaml -automation: - - alias: "EV Charging - Price Trend Based" - trigger: - - platform: state - entity_id: sensor.ev_battery_level - condition: - # Start charging if current price < 90% of recent 24h average - - condition: template - value_template: > - {% set current = states('sensor._current_electricity_price') | float %} - {% set trailing_avg = state_attr('sensor._price_trailing_24h', 'price_median') | float %} - {{ current < (trailing_avg * 0.9) }} - # And battery < 80% - - condition: numeric_state - entity_id: sensor.ev_battery_level - below: 80 - action: - - service: switch.turn_on - entity_id: switch.ev_charger -``` - -
- -#### Key Attributes - -All average sensors provide these attributes: - -| Attribute | Description | Example | -|-----------|-------------|---------| -| `price_mean` | Arithmetic mean (always available) | 25.3 ct/kWh | -| `price_median` | Median value (always available) | 22.1 ct/kWh | -| `interval_count` | Number of intervals included | 96 | -| `timestamp` | Reference time for calculation | 2025-12-18T00:00:00+01:00 | - -**Note:** The `price_mean` and `price_median` attributes are **always present** regardless of which value you configured for display. This ensures automation compatibility when changing the display setting. - -#### When to Use Which Value - -**Use Median for:** -- ✅ Comparing "typical" price levels across days -- ✅ Determining if current price is unusually high/low -- ✅ User-facing displays ("What was today like?") -- ✅ Volatility analysis (comparing typical vs extremes) - -**Use Mean for:** -- ✅ Cost calculations and budgeting -- ✅ Energy cost estimations -- ✅ Comparing actual average costs between periods -- ✅ Financial planning and forecasting - -**Both values tell different stories:** -- High median + much higher mean = Expensive spikes occurred -- Low median + higher mean = Generally cheap with occasional spikes -- Similar median and mean = Stable prices (low volatility) - - - -## Volatility Sensors - -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 / aAithmetic 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: -1. Go to **Settings → Devices & Services → Tibber Prices**. -2. Click **Configure**. -3. 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_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` | -| `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 `state` of 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. The `price_volatility` attribute 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. -```yaml -automation: - - alias: "Enable battery optimization only on volatile days" - trigger: - - platform: template - value_template: > - {{ state_attr('sensor._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_volatility` attribute, 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. -```yaml -automation: - - alias: "Brittle - Enable battery optimization" - trigger: - # - # BAD: Avoid hard-coding numeric values - # - - platform: numeric_state - entity_id: sensor._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. - -## Rating Sensors - -Rating sensors classify prices relative to the **trailing 24-hour average**, answering: "Is the current price cheap, normal, or expensive compared to recent history?" - -### How Ratings Work - -The integration calculates a **percentage difference** between the current price and the trailing 24-hour average: - -``` -difference = ((current_price - trailing_avg) / abs(trailing_avg)) × 100% -``` - -This percentage is then classified: - -| Rating | Condition (default) | Meaning | -|--------|---------------------|---------| -| **LOW** | difference ≤ -10% | Significantly below recent average | -| **NORMAL** | -10% < difference < +10% | Within normal range | -| **HIGH** | difference ≥ +10% | Significantly above recent average | - -**Hysteresis** (default 2%) prevents flickering: once a rating enters LOW, it must cross -8% (not -10%) to return to NORMAL. This avoids rapid switching at threshold boundaries. - -```mermaid -stateDiagram-v2 - direction LR - - LOW: 🟢 LOW
price ≤ −10% - NORMAL: 🟡 NORMAL
−10% … +10% - HIGH: 🔴 HIGH
price ≥ +10% - - LOW --> NORMAL: crosses −8%
(hysteresis) - NORMAL --> LOW: drops below −10% - NORMAL --> HIGH: rises above +10% - HIGH --> NORMAL: crosses +8%
(hysteresis) -``` - -> **The 2% gap** between entering (−10%) and leaving (−8%) a state prevents the sensor from flickering back and forth when prices hover near a threshold. - -### Available Rating Sensors - -| Sensor | Scope | Description | -|--------|-------|-------------| -| Current Price Rating | Current interval | Rating of the current 15-minute price | -| Next Price Rating | Next interval | Rating for the upcoming 15-minute price | -| Previous Price Rating | Previous interval | Rating for the past 15-minute price | -| Current Hour Price Rating | Rolling 5-interval | Smoothed rating around the current hour | -| Next Hour Price Rating | Rolling 5-interval | Smoothed rating around the next hour | -| Yesterday's Price Rating | Calendar day | Aggregated rating for yesterday | -| Today's Price Rating | Calendar day | Aggregated rating for today | -| Tomorrow's Price Rating | Calendar day | Aggregated rating for tomorrow | - -### Ratings vs Levels - -The integration provides **two** classification systems that serve different purposes: - -| | Ratings | Levels | -|--|---------|--------| -| **Source** | Calculated by integration | Provided by Tibber API | -| **Scale** | 3 levels (LOW, NORMAL, HIGH) | 5 levels (VERY_CHEAP → VERY_EXPENSIVE) | -| **Basis** | Trailing 24h average | Daily min/max range | -| **Best for** | Automations (simple thresholds) | Dashboard displays (fine granularity) | -| **Configurable** | Yes (thresholds) | Gap tolerance only | -| **Automation attribute** | `rating_level` (always lowercase English) | `level` (always uppercase English) | - -**Which to use?** - -- **Automations**: Use **ratings** (3 simple states, configurable thresholds, hysteresis) -- **Dashboards**: Use **levels** (5 color-coded states, more visual granularity) -- **Advanced automations**: Combine both (e.g., "LOW rating AND VERY_CHEAP level") - -### Key Attributes - -| Attribute | Description | Example | -|-----------|-------------|---------| -| `rating_level` | Language-independent rating (always lowercase) | `low` | -| `difference` | Percentage difference from trailing average | `-12.5` | -| `trailing_avg_24h` | The reference average used for classification | `22.3` | - -### Usage in Automations - -**Best Practice:** Always use the `rating_level` attribute (lowercase English) instead of the sensor state (which is translated to your HA language): - -```yaml -# ✅ Correct — language-independent -condition: - - condition: template - value_template: > - {{ state_attr('sensor._current_price_rating', 'rating_level') == 'low' }} - -# ❌ Avoid — breaks when HA language changes -condition: - - condition: state - entity_id: sensor._current_price_rating - state: "Low" # "Niedrig" in German, "Lav" in Norwegian... -``` - -### Configuration - -Rating thresholds can be adjusted in the options flow: - -1. Go to **Settings → Devices & Services → Tibber Prices → Configure** -2. Navigate to **Price Rating Thresholds** -3. Adjust LOW/HIGH thresholds, hysteresis, and gap tolerance - -See [Configuration](configuration.md#step-3-price-rating-thresholds) for details. - -## Level Sensors - -Level sensors show the **Tibber API's own price classification** with a 5-level scale: - -| Level | Meaning | Numeric Value | -|-------|---------|---------------| -| **VERY_CHEAP** | Exceptionally low | -2 | -| **CHEAP** | Below average | -1 | -| **NORMAL** | Typical range | 0 | -| **EXPENSIVE** | Above average | +1 | -| **VERY_EXPENSIVE** | Exceptionally high | +2 | - -### Available Level Sensors - -| Sensor | Scope | -|--------|-------| -| Current Price Level | Current interval | -| Next Price Level | Next interval | -| Previous Price Level | Previous interval | -| Current Hour Price Level | Rolling 5-interval window | -| Next Hour Price Level | Rolling 5-interval window | -| Yesterday's Price Level | Calendar day (aggregated) | -| Today's Price Level | Calendar day (aggregated) | -| Tomorrow's Price Level | Calendar day (aggregated) | - -**Gap tolerance** smoothing is applied to prevent isolated level flickers (e.g., a single NORMAL between two CHEAPs → corrected to CHEAP). Configure in [options flow](configuration.md#step-4-price-level-gap-tolerance). - -## Min/Max Sensors - -These sensors show the lowest and highest prices for calendar days and rolling windows: - -### Daily Min/Max - -| Sensor | Description | -|--------|-------------| -| Today's Lowest Price | Minimum price today (00:00–23:59) | -| Today's Highest Price | Maximum price today (00:00–23:59) | -| Tomorrow's Lowest Price | Minimum price tomorrow | -| Tomorrow's Highest Price | Maximum price tomorrow | - -### 24-Hour Rolling Min/Max - -| Sensor | Description | -|--------|-------------| -| Trailing Price Min | Lowest price in the last 24 hours | -| Trailing Price Max | Highest price in the last 24 hours | -| Leading Price Min | Lowest price in the next 24 hours | -| Leading Price Max | Highest price in the next 24 hours | - -### Key Attributes - -All min/max sensors include: - -| Attribute | Description | -|-----------|-------------| -| `timestamp` | When the extreme price occurs/occurred | -| `price_diff_from_daily_min` | Difference from daily minimum | -| `price_diff_from_daily_min_%` | Percentage difference | - -## Energy Price & Tax Breakdown - -Many price sensors expose the **raw energy price** (spot price) and the **tax component** as additional attributes. These are sourced directly from the Tibber API's `energy` and `tax` fields, which together make up the `total` price you see in the sensor state: - -`total = energy + tax` - -### Where These Attributes Appear - -| Sensor Group | Attributes | Description | -|---|---|---| -| **Current/Next/Previous Interval Price** | `energy_price`, `tax` | Raw values for that specific 15-minute interval | -| **Today's/Tomorrow's Min/Max Price** | `energy_price`, `tax` | Values from the interval with the extreme price | -| **Today's/Tomorrow's Average Price** | `energy_price_mean`, `energy_price_median`, `tax_mean`, `tax_median` | Mean and median values across all intervals of the day | - -:::note Transition After Update -After updating the integration, the `energy_price` and `tax` attributes will appear gradually as new price data is fetched from the Tibber API. Existing cached intervals (up to ~2 days old) won't have these fields yet — the attributes will simply be absent until fresh data replaces them. No action needed. -::: - -### Use Cases - -#### Solar Feed-In & Net Metering (Saldering) - -In countries like the Netherlands, solar feed-in compensation is based on the **raw energy/spot price**, not the total consumer price. The `energy_price` attribute gives you exactly this value — no more reverse-engineering from the total price with fragile template calculations. - -
-Show YAML: Automation — solar export or consume decision - -```yaml -# Example: Decide whether to export solar power or consume it -# Compare energy price (what you'd earn by exporting) vs. total price (what you'd pay) -automation: - - alias: "Solar: Export or Consume" - trigger: - - platform: numeric_state - entity_id: sensor.solar_production_power - above: 2000 # Producing more than 2 kW - condition: - - condition: template - value_template: > - {% set energy = state_attr('sensor._current_electricity_price', 'energy_price') %} - {% set total = states('sensor._current_electricity_price') | float %} - {# Export when energy price is high relative to total — you earn more #} - {{ energy is not none and energy > (total * 0.4) }} - action: - - service: switch.turn_off - entity_id: switch.battery_charging # Don't charge battery, export instead -``` - -
- -#### Price Composition Analysis - -Understand how your electricity price is structured — useful for comparing across days or spotting trends in market prices vs. fees: - -
-Show YAML: Template sensor — electricity tax share percentage - -```yaml -# Template sensor showing tax share -template: - - sensor: - - name: "Electricity Tax Share" - unit_of_measurement: "%" - state: > - {% set tax = state_attr('sensor._current_electricity_price', 'tax') %} - {% set total = states('sensor._current_electricity_price') | float %} - {% if tax is not none and total > 0 %} - {{ ((tax / total) * 100) | round(1) }} - {% else %} - unavailable - {% endif %} -``` - -
- -#### Dashboard: Daily Cost Breakdown - -Show users how today's average price splits into energy vs. tax: - -```yaml -# Mushroom chips card showing the split -type: custom:mushroom-chips-card -chips: - - type: template - icon: mdi:flash - content: > - ⚡ {{ state_attr('sensor._price_today', 'energy_price_mean') | round(1) }} ct - - type: template - icon: mdi:receipt-text - content: > - 🏛️ {{ state_attr('sensor._price_today', 'tax_mean') | round(1) }} ct -``` - -### Country-Specific Calculations - -The composition of the `tax` field varies by country (Norway, Sweden, Germany, Netherlands each have different fee structures). For detailed examples of how to build country-specific calculations using `input_number` helpers and template sensors — including **Dutch solar feed-in compensation (saldering)** — see the **[Community Examples](community-examples.md#country-specific-price-calculations)** page. - -### In Chart Data Actions - -The `energy_price` and `tax` fields are also available in the `get_chartdata` action. See [Actions — Energy & Tax Fields](./actions.md#energy--tax-fields-in-get_chartdata) for details. - -## Timing Sensors - -Timing sensors provide **real-time information about Best Price and Peak Price periods**: when they start, end, how long they last, and your progress through them. - -```mermaid -stateDiagram-v2 - direction LR - - IDLE: ⏸️ IDLE
No active period - ACTIVE: ▶️ ACTIVE
In period - GRACE: ⏳ GRACE
60s buffer - - IDLE --> ACTIVE: period starts - ACTIVE --> GRACE: period ends - GRACE --> IDLE: 60s elapsed - GRACE --> ACTIVE: new period starts
(within grace) -``` - -**IDLE** = waiting for next period (shows countdown via `next_in_minutes`). **ACTIVE** = inside a period (shows `progress` 0–100% and `remaining_minutes`). **GRACE** = short buffer after a period ends, allowing back-to-back periods to merge seamlessly. - -### Available Timing Sensors - -For each period type (Best Price and Peak Price): - -| Sensor | When Period Active | When No Active Period | -|--------|-------------------|----------------------| -| End Time | Current period's end time | Next period's end time | -| Period Duration | Current period length (minutes) | Next period length | -| Remaining Minutes | Minutes until current period ends | 0 | -| Progress | 0–100% through current period | 0 | -| Next Start Time | When next-next period starts | When next period starts | -| Next In Minutes | Minutes to next-next period | Minutes to next period | - -### Usage Examples - -**Show countdown to next cheap window:** - -```yaml -type: custom:mushroom-entity-card -entity: sensor._best_price_next_in_minutes -name: Next Cheap Window -icon: mdi:clock-fast -``` - -**Display period progress bar:** - -
-Show YAML: Bar card for period progress - -```yaml -type: custom:bar-card -entity: sensor._best_price_progress -name: Best Price Progress -min: 0 -max: 100 -severity: - - from: 0 - to: 50 - color: green - - from: 50 - to: 80 - color: orange - - from: 80 - to: 100 - color: red -``` - -
- -**Automation: notify when period is almost over:** - -
-Show YAML: Automation — notify when best price period is ending - -```yaml -automation: - - alias: "Warn: Best Price Ending Soon" - trigger: - - platform: numeric_state - entity_id: sensor._best_price_remaining_minutes - below: 15 - condition: - - condition: numeric_state - entity_id: sensor._best_price_remaining_minutes - above: 0 - action: - - service: notify.mobile_app - data: - title: "Best Price Ending Soon" - message: "Only {{ states('sensor._best_price_remaining_minutes') }} minutes left!" -``` - -
- -## Trend Sensors - -Trend sensors help you understand **whether to act now or wait**. The integration provides two complementary families: - -- **Price Outlook Sensors (1h–12h):** Compare current price vs. future window average — "Is now cheaper than the next Nh on average?" -- **Price Trajectory Sensors (2h–12h):** Compare first half vs. second half of the window — "Are prices rising or falling *within* the window?" - -### Price Outlook Sensors (1h–12h) - -These sensors compare the **current price** with the **average price** of the next N hours: - -| Sensor | Compares Against | -|--------|-----------------| -| **Price Outlook (1h)** (`price_outlook_1h`) | Average of next 1 hour | -| **Price Outlook (2h)** (`price_outlook_2h`) | Average of next 2 hours | -| **Price Outlook (3h)** (`price_outlook_3h`) | Average of next 3 hours | -| **Price Outlook (4h)** (`price_outlook_4h`) | Average of next 4 hours | -| **Price Outlook (5h)** (`price_outlook_5h`) | Average of next 5 hours | -| **Price Outlook (6h)** (`price_outlook_6h`) | Average of next 6 hours | -| **Price Outlook (8h)** (`price_outlook_8h`) | Average of next 8 hours | -| **Price Outlook (12h)** (`price_outlook_12h`) | Average of next 12 hours | - -:::info Same Starting Point — All Outlook Sensors Use Your Current Price -All outlook sensors share the **same base: your current 15-minute price**. They differ only in how far ahead they average. The windows **overlap** — the 3h average includes ALL intervals from the 1h and 2h windows, plus one more hour. - -**This means:** -- `price_outlook_3h` shows "current price vs. average of the **entire** next 3 hours" — **not** "what happens between hour 2 and hour 3" -- If 1h shows `falling` but 6h shows `rising`: near-term prices are below your current price, but looking at the full 6h window (which includes expensive evening hours), the overall average is above your current price -- Larger windows smooth out short-term fluctuations — a 30-minute price spike affects the 1h average more than the 6h average - -**⚠️ At a price minimum, outlook sensors can be misleading!** If you're at the minimum and prices are about to rise, `price_outlook_3h` may still show `strongly_falling` because the cheap minimum pulls the 3h average below your current high price. Use `price_trajectory_3h` to see the direction *within* the window. -::: - -**States:** Each sensor has one of five states: - -```mermaid -stateDiagram-v2 - direction LR - - SF: ⬇️⬇️ strongly_falling
−2 · future ≤ −9% - F: ⬇️ falling
−1 · future ≤ −3% - S: ➡️ stable
0 · within ±3% - R: ⬆️ rising
+1 · future ≥ +3% - SR: ⬆️⬆️ strongly_rising
+2 · future ≥ +9% - - SF --> F: price recovers - F --> S: approaches average - S --> R: future rises - R --> SR: accelerates - SR --> R: slows down - R --> S: stabilizes - S --> F: future drops - F --> SF: accelerates -``` - -| State | Meaning | `trend_value` | -|-------|---------|---------------| -| `strongly_falling` | Prices will drop significantly | -2 | -| `falling` | Prices will drop | -1 | -| `stable` | Prices staying roughly the same | 0 | -| `rising` | Prices will increase | +1 | -| `strongly_rising` | Prices will increase significantly | +2 | - -**Key attributes:** - -| Attribute | Description | Example | -|-----------|-------------|---------| -| `trend_value` | Numeric value for automations (-2 to +2) | `-1` | -| `trend_Nh_%` | Percentage difference from current price | `-12.3` | -| `next_Nh_avg` | Average price in the future window | `18.5` | -| `second_half_Nh_avg` | Average price in later half of window | `16.2` | -| `threshold_rising_%` | Active rising threshold after volatility adjustment | `3.0` | -| `threshold_rising_strongly_%` | Active strongly-rising threshold after volatility adjustment | `4.8` | -| `threshold_falling_%` | Active falling threshold after volatility adjustment | `-3.0` | -| `threshold_falling_strongly_%` | Active strongly-falling threshold after volatility adjustment | `-4.8` | -| `volatility_factor` | Applied multiplier (0.6 = low, 1.0 = moderate, 1.4 = high volatility) | `0.8` | - -**Tip:** The `trend_value` attribute (`-2` to `+2`) is ideal for automations — use numeric comparisons instead of matching translated state strings. - -### Price Trajectory Sensors (2h–12h) - -These sensors compare the **first half** of the future window against the **second half** — revealing the price *direction within* the window. - -| Sensor | Compares | -|--------|----------| -| **Price Trajectory (2h)** (`price_trajectory_2h`) | Avg of hour 1 vs avg of hour 2 | -| **Price Trajectory (3h)** (`price_trajectory_3h`) | Avg of first 1.5h vs avg of second 1.5h | -| **Price Trajectory (4h)** (`price_trajectory_4h`) | Avg of first 2h vs avg of second 2h | -| **Price Trajectory (5h)** (`price_trajectory_5h`) | Avg of first 2.5h vs avg of second 2.5h | -| **Price Trajectory (6h)** (`price_trajectory_6h`) | Avg of first 3h vs avg of second 3h | -| **Price Trajectory (8h)** (`price_trajectory_8h`) | Avg of first 4h vs avg of second 4h | -| **Price Trajectory (12h)** (`price_trajectory_12h`) | Avg of first 6h vs avg of second 6h | - -**States:** Same 5-level scale as outlook sensors (`strongly_falling` → `strongly_rising`). - -:::info Why trajectory sensors complement outlook sensors -**At a price minimum** — the exact moment you should act — `price_outlook_3h` may show `strongly_falling` because the cheap minimum pulls the entire 3h average below your current high price. But `price_trajectory_3h` shows `rising` because the second half (after the minimum) is more expensive than the first half. - -| Combination | Interpretation | -|-------------|----------------| -| Outlook `falling` + Trajectory `rising` | **You're AT the minimum** — act now | -| Outlook `falling` + Trajectory `falling` | Prices still dropping — wait | -| Outlook `rising` + Trajectory `rising` | Strong signal to act now | -| Outlook `rising` + Trajectory `falling` | Short spike, then cheaper — wait | -::: - -**Key attributes:** - -| Attribute | Description | Example | -|-----------|-------------|---------| -| `trend_value` | Numeric value for automations (-2 to +2) | `1` | -| `first_half_avg` | Average price in first half of window | `12.4` | -| `second_half_avg` | Average price in second half of window | `18.1` | -| `half_diff_%` | Percentage difference (second vs first half) | `46.0` | - -### Current Price Trend - -**Entity ID:** `sensor._current_price_trend` - -This sensor shows the **currently active trend direction** based on a 3-hour future outlook with volatility-adaptive thresholds. - -Unlike the simple trend sensors that always compare current price vs future average, the current price trend represents the **ongoing trend** — it remains stable between updates and only changes when the underlying price direction actually shifts. - -**States:** Same 5-level scale as simple trends. - -**Key attributes:** - -| Attribute | Description | Example | -|-----------|-------------|---------| -| `previous_direction` | Price direction before the current trend started | `falling` | -| `price_direction_duration_minutes` | How long prices have been moving in this direction | `45` | -| `price_direction_since` | Timestamp when prices started moving in this direction | `2025-11-08T14:00:00+01:00` | - -### Next Price Trend Change - -**Entity ID:** `sensor._next_price_trend_change` - -This sensor predicts **when the current trend will change** by scanning future intervals. It requires 3 consecutive intervals (configurable: 2–6) confirming the new trend before reporting a change (hysteresis), which prevents false alarms from short-lived price spikes. - -**Important:** Only **direction changes** count as trend changes. The five states are grouped into three directions: - -| Direction | States | -|-----------|--------| -| **falling** | `strongly_falling`, `falling` | -| **stable** | `stable` | -| **rising** | `rising`, `strongly_rising` | - -A change from `rising` to `strongly_rising` (same direction) is **not** reported as a trend change — only actual reversals like `rising` → `stable` or `falling` → `rising`. - -**State:** Timestamp of the next trend change (or unavailable if no change predicted). - -**Key attributes:** - -| Attribute | Description | Example | -|-----------|-------------|---------| -| `direction` | What the trend will change TO | `rising` | -| `from_direction` | Current trend (will change FROM) | `falling` | -| `minutes_until_change` | Minutes until trend changes | `90` | -| `price_at_change` | Price at the change point | `13.8` | -| `price_avg_after_change` | Average price after change | `18.1` | -| `threshold_rising_%` | Active rising threshold after volatility adjustment | `3.0` | -| `threshold_rising_strongly_%` | Active strongly-rising threshold after volatility adjustment | `4.8` | -| `threshold_falling_%` | Active falling threshold after volatility adjustment | `-3.0` | -| `threshold_falling_strongly_%` | Active strongly-falling threshold after volatility adjustment | `-4.8` | -| `volatility_factor` | Applied multiplier (0.6 = low, 1.0 = moderate, 1.4 = high volatility) | `0.8` | - -### How to Use Trend Sensors for Decisions - -:::danger Common Misconception — Don't "Wait for Stable"! -A natural intuition is to treat trend states like a stock ticker: - -- ❌ "It's **falling** → I'll wait until it reaches **stable** (the bottom)" -- ❌ "It's **rising** → too late, I missed the best price" -- ❌ "It's **stable** → now is the perfect time to act!" - -**This is wrong.** Trend sensors don't show a trajectory — they show a **comparison** between your current price and future prices. The correct interpretation is the opposite: - -| State | What the Sensor Calculates | ✅ Correct Action | -|-------|---------------------------|-------------------| -| `falling` | Current price **higher** than future average | **WAIT** — cheaper prices are coming | -| `strongly_falling` | Current price **much higher** than future average | **DEFINITELY WAIT** — significant savings ahead | -| `stable` | Current price **≈ equal** to future average | **Timing doesn't matter** — start whenever convenient | -| `rising` | Current price **lower** than future average | **ACT NOW** — it only gets more expensive | -| `strongly_rising` | Current price **much lower** than future average | **ACT IMMEDIATELY** — best price right now | - -**"Rising" is NOT "too late" — it means NOW is the best time because prices will be higher later.** -::: - -#### Basic Automation Pattern - -For most appliances (dishwasher, washing machine, dryer), a single outlook sensor is enough: - -```yaml -# Example: Start dishwasher when prices are favorable -trigger: - - platform: state - entity_id: sensor.my_home_price_outlook_3h -condition: - - condition: numeric_state - entity_id: sensor.my_home_price_outlook_3h - attribute: trend_value - # rising (1) or strongly_rising (2) = act now - above: 0 -action: - - service: switch.turn_on - target: - entity_id: switch.dishwasher -``` - -#### Combining Multiple Windows - -When short-term and long-term trends disagree, you get richer insight: - -| 1h Outlook | 6h Outlook | Interpretation | Recommendation | -|----------|----------|---------------|----------------| -| `rising` | `rising` | Prices going up across the board | **Start now** | -| `falling` | `falling` | Prices dropping across the board | **Wait** | -| `falling` | `rising` | Brief dip, then expensive evening | **Wait briefly**, then start during the dip | -| `rising` | `falling` | Short spike, but cheaper hours ahead | **Wait** if you can — better prices coming | -| `stable` | any | Short-term doesn't matter | Use the **longer window** for your decision | - -#### Dashboard Quick-Glance - -On your dashboard, trend sensors give an instant overview: - -- 🟢 All **falling/strongly_falling** → "Relax, prices are dropping — wait" -- 🔴 All **rising/strongly_rising** → "Start everything you can — it only gets more expensive" -- 🟡 **Mixed** → Compare short-term vs. long-term sensors, or check the Best Price Period sensor - -### Outlook & Trajectory Sensors vs Average Sensors - -Both sensor families provide future price information, but serve different purposes: - -| | Outlook/Trajectory Sensors | Average Sensors | -|--|---------------------------|-----------------| -| **Purpose** | Dashboard display, quick visual overview | Automations, precise numeric comparisons | -| **Output** | Classification (falling/stable/rising) | Exact price values (ct/kWh) | -| **Best for** | "Should I worry about prices?" | "Is the future average below 15 ct?" | -| **Use in** | Dashboard icons, status displays | Template conditions, numeric thresholds | - -**Design principle:** Use **trend sensors** (enum) for visual feedback at a glance, use **average sensors** (numeric) for precise decision-making in automations. - -### Configuration - -Trend thresholds can be adjusted in the options flow: - -1. Go to **Settings → Devices & Services → Tibber Prices** -2. Click **Configure** on your home -3. Navigate to **📈 Price Trend Thresholds** -4. Adjust the rising/falling and strongly rising/falling percentages - -The thresholds are **volatility-adaptive**: on days with high price volatility, thresholds are widened automatically to prevent constant state changes. This means the trend sensors give more stable readings during volatile market conditions. - -## Diagnostic Sensors - -### Chart Metadata - -**Entity ID:** `sensor._chart_metadata` - -> **✨ New Feature**: This sensor provides dynamic chart configuration metadata for optimal visualization. Perfect for use with the `get_apexcharts_yaml` action! - -This diagnostic sensor provides essential chart configuration values as sensor attributes, enabling dynamic Y-axis scaling and optimal chart appearance in rolling window modes. - -**Key Features:** - -- **Dynamic Y-Axis Bounds**: Automatically calculates optimal `yaxis_min` and `yaxis_max` for your price data -- **Automatic Updates**: Refreshes when price data changes (coordinator updates) -- **Lightweight**: Metadata-only mode (no data processing) for fast response -- **State Indicator**: Shows `pending` (initialization), `ready` (data available), or `error` (service call failed) - -**Attributes:** - -- **`timestamp`**: When the metadata was last fetched -- **`yaxis_min`**: Suggested minimum value for Y-axis (optimal scaling) -- **`yaxis_max`**: Suggested maximum value for Y-axis (optimal scaling) -- **`currency`**: Currency code (e.g., "EUR", "NOK") -- **`resolution`**: Interval duration in minutes (usually 15) -- **`error`**: Error message if service call failed - -**Usage:** - -The `tibber_prices.get_apexcharts_yaml` action **automatically uses this sensor** for dynamic Y-axis scaling in `rolling_window` and `rolling_window_autozoom` modes! No manual configuration needed - just enable the action's result with `config-template-card` and the sensor provides optimal Y-axis bounds automatically. - -See the **[Chart Examples Guide](chart-examples.md)** for practical examples! - ---- - -### Chart Data Export - -**Entity ID:** `sensor._chart_data_export` -**Default State:** Disabled (must be manually enabled) - -> **⚠️ Legacy Feature**: This sensor is maintained for backward compatibility. For new integrations, use the **`tibber_prices.get_chartdata`** service instead, which offers more flexibility and better performance. - -This diagnostic sensor provides cached chart-friendly price data that can be consumed by chart cards (ApexCharts, custom cards, etc.). - -**Key Features:** - -- **Configurable via Options Flow**: Service parameters can be configured through the integration's options menu (Step 7 of 7) -- **Automatic Updates**: Data refreshes on coordinator updates (every 15 minutes) -- **Attribute-Based Output**: Chart data is stored in sensor attributes for easy access -- **State Indicator**: Shows `pending` (before first call), `ready` (data available), or `error` (service call failed) - -**Important Notes:** - -- ⚠️ Disabled by default - must be manually enabled in entity settings -- ⚠️ Consider using the service instead for better control and flexibility -- ⚠️ Configuration updates require HA restart - -**Attributes:** - -The sensor exposes chart data with metadata in attributes: - -- **`timestamp`**: When the data was last fetched -- **`error`**: Error message if service call failed -- **`data`** (or custom name): Array of price data points in configured format - -**Configuration:** - -To configure the sensor's output format: - -1. Go to **Settings → Devices & Services → Tibber Prices** -2. Click **Configure** on your Tibber home -3. Navigate through the options wizard to **Step 7: Chart Data Export Settings** -4. Configure output format, filters, field names, and other options -5. Save and restart Home Assistant - -**Available Settings:** - -See the `tibber_prices.get_chartdata` service documentation below for a complete list of available parameters. All service parameters can be configured through the options flow. - -**Example Usage:** - -```yaml -# ApexCharts card consuming the sensor -type: custom:apexcharts-card -series: - - entity: sensor._chart_data_export - data_generator: | - return entity.attributes.data; -``` - -**Migration Path:** - -If you're currently using this sensor, consider migrating to the service: - -```yaml -# Old approach (sensor) -- service: apexcharts_card.update - data: - entity: sensor._chart_data_export - -# New approach (service) -- service: tibber_prices.get_chartdata - data: - entry_id: YOUR_CONFIG_ENTRY_ID - day: ["today", "tomorrow"] - output_format: array_of_objects - response_variable: chart_data -``` diff --git a/docs/user/sidebars.ts b/docs/user/sidebars.ts index 2c6ca8f..dbcd3d9 100644 --- a/docs/user/sidebars.ts +++ b/docs/user/sidebars.ts @@ -31,25 +31,47 @@ const sidebars: SidebarsConfig = { }, { type: 'category', - label: '📊 Features', - items: ['sensors', 'sensor-reference', 'period-calculation', 'dynamic-icons', 'icon-colors', 'actions'], + label: '📊 Sensors', + items: [ + 'sensors-overview', + 'sensors-average', + 'sensors-ratings-levels', + 'sensors-volatility', + 'sensors-trends', + 'sensors-timing', + 'sensors-energy-tax', + ], collapsible: true, collapsed: false, }, { type: 'category', - label: '🎨 Visualization', - items: ['dashboard-examples', 'chart-examples'], + label: '⏰ Price Periods', + items: ['period-calculation', 'period-relaxation'], collapsible: true, collapsed: false, }, { type: 'category', - label: '🤖 Automation', + label: '🎨 Dashboards & Charts', + items: ['dynamic-icons', 'icon-colors', 'dashboard-examples', 'chart-examples'], + collapsible: true, + collapsed: false, + }, + { + type: 'category', + label: '🤖 Automations', items: ['automation-examples'], collapsible: true, collapsed: false, }, + { + type: 'category', + label: '📖 Reference', + items: ['sensor-reference', 'actions'], + collapsible: true, + collapsed: false, + }, { type: 'category', label: '👥 Community', diff --git a/docs/user/src/components/EntitySearch.tsx b/docs/user/src/components/EntitySearch.tsx index 2de5b9f..be0d27d 100644 --- a/docs/user/src/components/EntitySearch.tsx +++ b/docs/user/src/components/EntitySearch.tsx @@ -27,9 +27,16 @@ const MAX_RESULTS = 12; /** Display names for doc page back-links (from data-refs attribute). */ const DOC_NAMES: Record = { - sensors: 'Sensors Guide', + 'sensors-overview': 'Sensors Overview', + 'sensors-average': 'Average Sensors', + 'sensors-ratings-levels': 'Ratings & Levels', + 'sensors-volatility': 'Volatility', + 'sensors-trends': 'Trends', + 'sensors-timing': 'Timing', + 'sensors-energy-tax': 'Energy & Tax', configuration: 'Configuration', 'period-calculation': 'Period Calculation', + 'period-relaxation': 'Relaxation', 'automation-examples': 'Automation Examples', actions: 'Actions', }; @@ -151,9 +158,10 @@ export default function EntitySearch(): React.ReactElement { if (docRefs.length > 0 && firstCell && !firstCell.querySelector('.entity-back-links')) { const span = document.createElement('span'); span.className = 'entity-back-links'; - docRefs.forEach((slug) => { + docRefs.forEach((ref) => { + const slug = ref.split('#')[0]; const a = document.createElement('a'); - a.href = slug; + a.href = ref; a.className = 'entity-back-link'; a.title = DOC_NAMES[slug] ?? slug; a.setAttribute('aria-label', `View in: ${DOC_NAMES[slug] ?? slug}`); diff --git a/scripts/docs/generate-sensor-reference b/scripts/docs/generate-sensor-reference index eaacfb0..c7ba21c 100755 --- a/scripts/docs/generate-sensor-reference +++ b/scripts/docs/generate-sensor-reference @@ -337,37 +337,66 @@ def load_all_disabled() -> dict[str, set[str]]: return result +def _heading_to_anchor(heading_text: str) -> str: + """Convert a Markdown heading to a Docusaurus-style anchor slug.""" + text = heading_text.strip().lower() + # Remove inline code backticks and common Markdown formatting + text = re.sub(r"[`*_]", "", text) + # Replace non-alphanumeric (except hyphens) with hyphens + text = re.sub(r"[^a-z0-9-]+", "-", text) + # Collapse multiple hyphens and strip leading/trailing hyphens + return re.sub(r"-{2,}", "-", text).strip("-") + + def scan_doc_refs() -> dict[str, list[str]]: """ Scan doc markdown files for EntityRef usage. - Returns: {entity_key: [doc_slug, ...]} + Returns: {entity_key: [doc_slug#anchor, ...]} + Each value includes the section anchor of the nearest heading above the EntityRef. """ refs: dict[str, list[str]] = {} docs_dir = REPO_ROOT / "docs" / "user" / "docs" entity_ref_pattern = re.compile(r']*?\bid="([^"]+)"') also_pattern = re.compile(r'\balso="([^"]+)"') + heading_pattern = re.compile(r"^(#{2,6})\s+(.+)$", re.MULTILINE) for md_file in sorted(docs_dir.glob("*.md")): if md_file.name == "sensor-reference.md": continue slug = md_file.stem text = md_file.read_text(encoding="utf-8") + + # Build list of (position, anchor) for all headings in this file + headings: list[tuple[int, str]] = [ + (h_match.start(), _heading_to_anchor(h_match.group(2))) for h_match in heading_pattern.finditer(text) + ] + + def _ref_with_anchor(pos: int, *, _headings: list[tuple[int, str]] = headings, _slug: str = slug) -> str: + """Return 'slug#anchor' for the nearest heading above pos.""" + anchor = "" + for h_pos, h_anchor in reversed(_headings): + if h_pos < pos: + anchor = h_anchor + break + return f"{_slug}#{anchor}" if anchor else _slug + + def _add_ref(key: str, ref: str) -> None: + refs.setdefault(key, []) + if ref not in refs[key]: + refs[key].append(ref) + for match in entity_ref_pattern.finditer(text): key = match.group(1) - refs.setdefault(key, []) - if slug not in refs[key]: - refs[key].append(slug) + ref = _ref_with_anchor(match.start()) + _add_ref(key, ref) # Check for 'also' prop in the same tag tag_end = text.find(">", match.start()) if tag_end != -1: tag_text = text[match.start() : tag_end] also_match = also_pattern.search(tag_text) if also_match: - also_key = also_match.group(1) - refs.setdefault(also_key, []) - if slug not in refs[also_key]: - refs[also_key].append(slug) + _add_ref(also_match.group(1), ref) return refs @@ -411,7 +440,7 @@ You can also use your browser's built-in search (**Ctrl+F** / **Cmd+F**) to sear Sensors marked ❌ must be enabled manually via **Settings → Devices & Services → Entities** → find the entity → toggle **Enabled**. -**Detailed documentation:** See the **[Sensors Guide](sensors.md)** for detailed +**Detailed documentation:** See the **[Sensors Overview](sensors-overview.md)** for detailed explanations of each sensor's purpose, attributes, and automation examples. ---