- Skip asymmetry/zigzag rejection near the data tail and refactor spike
validation so legitimate end-of-day spikes stop breaking periods.
- Expose relaxation attempt sliders for both Best/Peak flows, wire the values
through the coordinator, and extend the relaxation engine to honor the new
max-attempt cap with richer logging & metadata.
- Raise the default attempt count to eight flex levels so the 25% increment
pattern can stretch much further before stopping, keeping translations and
docs (including the matrix explanation) in sync across all locales.
Impact: Tail spikes no longer get thrown out incorrectly, users can tune how
aggressively the period search relaxes, and the defaults now find more viable
periods on volatile days.
* feat(period-calc): adaptive defaults + remove volatility filter
Major improvements to period calculation with smarter defaults and
simplified configuration:
**Adaptive Defaults:**
- ENABLE_MIN_PERIODS: true (was false) - Always try to find periods
- MIN_PERIODS target: 2 periods/day (ensures coverage)
- BEST_PRICE_MAX_LEVEL: "cheap" (was "any") - Prefer genuinely cheap
- PEAK_PRICE_MIN_LEVEL: "expensive" (was "any") - Prefer genuinely expensive
- GAP_TOLERANCE: 1 (was 0) - Allow 1-level deviations in sequences
- MIN_DISTANCE_FROM_AVG: 5% (was 2%) - Ensure significance
- PEAK_PRICE_MIN_PERIOD_LENGTH: 30min (was 60min) - More responsive
- PEAK_PRICE_FLEX: -20% (was -15%) - Better peak detection
**Volatility Filter Removal:**
- Removed CONF_BEST_PRICE_MIN_VOLATILITY from const.py
- Removed CONF_PEAK_PRICE_MIN_VOLATILITY from const.py
- Removed volatility filter UI controls from config_flow.py
- Removed filter_periods_by_volatility() calls from coordinator.py
- Updated all 5 translations (de, en, nb, nl, sv)
**Period Calculation Logic:**
- Level filter now integrated into _build_periods() (applied during
interval qualification, not as post-filter)
- Gap tolerance implemented via _check_level_with_gap_tolerance()
- Short periods (<1.5h) use strict filtering (no gap tolerance)
- Relaxation now passes level_filter + gap_count directly to
PeriodConfig
- show_periods check skipped when relaxation enabled (relaxation
tries "any" as fallback)
**Documentation:**
- Complete rewrite of docs/user/period-calculation.md:
* Visual examples with timelines
* Step-by-step explanation of 4-step process
* Configuration scenarios (5 common use cases)
* Troubleshooting section with specific fixes
* Advanced topics (per-day independence, early stop, etc.)
- Updated README.md: "volatility" → "distance from average"
Impact: Periods now reliably appear on most days with meaningful
quality filters. Users get warned about expensive periods and notified
about cheap opportunities without manual tuning. Relaxation ensures
coverage while keeping filters as strict as possible.
Breaking change: Volatility filter removed (was never a critical
feature, often confused users). Existing configs continue to work
(removed keys are simply ignored).
* feat(periods): modularize period_utils and add statistical outlier filtering
Refactored monolithic period_utils.py (1800 lines) into focused modules
for better maintainability and added advanced outlier filtering with
smart impact tracking.
Modular structure:
- types.py: Type definitions and constants (89 lines)
- level_filtering.py: Level filtering with gap tolerance (121 lines)
- period_building.py: Period construction from intervals (238 lines)
- period_statistics.py: Statistics and summaries (318 lines)
- period_merging.py: Overlap resolution (382 lines)
- relaxation.py: Per-day relaxation strategy (547 lines)
- core.py: Main API orchestration (251 lines)
- outlier_filtering.py: Statistical spike detection (294 lines)
- __init__.py: Public API exports (62 lines)
New statistical outlier filtering:
- Linear regression for trend-based spike detection
- 2 standard deviation confidence intervals (95%)
- Symmetry checking to preserve legitimate price shifts
- Enhanced zigzag detection with relative volatility (catches clusters)
- Replaces simple average smoothing with trend-based predictions
Smart impact tracking:
- Tests if original price would have passed criteria
- Only counts smoothed intervals that actually changed period formation
- Tracks level gap tolerance usage separately
- Both attributes only appear when > 0 (clean UI)
New period attributes:
- period_interval_smoothed_count: Intervals kept via outlier smoothing
- period_interval_level_gap_count: Intervals kept via gap tolerance
Impact: Statistical outlier filtering prevents isolated price spikes from
breaking continuous periods while preserving data integrity. All statistics
use original prices. Smart tracking shows only meaningful interventions,
making it clear when tolerance mechanisms actually influenced results.
Backwards compatible: All public APIs re-exported from period_utils package.
* Update docs/user/period-calculation.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update custom_components/tibber_prices/const.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update custom_components/tibber_prices/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update custom_components/tibber_prices/const.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* docs(periods): fix corrupted period-calculation.md and add outlier filtering documentation
Completely rewrote period-calculation.md after severe corruption (massive text
duplication throughout the file made it 2489 lines).
Changes:
- Fixed formatting: Removed all duplicate text and headers
- Reduced file size: 2594 lines down to 516 lines (clean, readable structure)
- Added section 5: "Statistical Outlier Filtering (NEW)" explaining:
- Linear regression-based spike detection (95% confidence intervals)
- Symmetry checking to preserve legitimate price shifts
- Enhanced zigzag detection with relative volatility
- Data integrity guarantees (original prices always used)
- New period attributes: period_interval_smoothed_count
- Added troubleshooting: "Price spikes breaking periods" section
- Added technical details: Algorithm constants and implementation notes
Impact: Users can now understand how outlier filtering prevents isolated
price spikes from breaking continuous periods. Documentation is readable
again with no duplicate content.
* fix(const): improve clarity in comments regarding period lengths for price alerts
* docs(periods): improve formatting and clarity in period-calculation.md
* Initial plan
* refactor: convert flexibility_pct to ratio once at function entry
Co-authored-by: jpawlowski <75446+jpawlowski@users.noreply.github.com>
* Update custom_components/tibber_prices/const.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update custom_components/tibber_prices/period_utils/period_building.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update custom_components/tibber_prices/period_utils/relaxation.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Julian Pawlowski <jpawlowski@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Implemented configurable gap tolerance (0-8 intervals) for best price and peak price
level filters to prevent periods from being split by occasional level deviations.
Key features:
- Gap tolerance only applies to periods ≥ MIN_INTERVALS_FOR_GAP_TOLERANCE (1.5h)
- Short periods (< 1.5h) use strict filtering (zero tolerance)
- Dynamic minimum distance between gaps: max(2, (interval_count // max_gap_count) // 2)
- 25% maximum cap on total gaps to prevent excessive outliers in long periods
- Intelligent period splitting at gap clusters (2+ consecutive non-qualifying intervals)
- Each sub-period independently validated with same gap tolerance rules
Technical implementation:
- Added CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT and CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT constants
- Added MIN_INTERVALS_FOR_GAP_TOLERANCE = 6 (1.5h minimum for gap tolerance)
- Implemented _split_at_gap_clusters() for period recovery
- Implemented _check_short_period_strict() for strict short-period filtering
- Implemented _check_level_filter_with_gaps() with fallback splitting logic
- Extracted _check_sequence_with_gap_tolerance() for reusable core validation
- Enhanced _check_level_filter() to use gap-tolerant validation
Configuration UI:
- Added NumberSelector (0-8, slider mode) for gap count in config flow
- Added translations for all 5 languages (de, en, nb, nl, sv)
- Default: 0 (strict filtering, backwards compatible)
Impact: Users can now configure how many occasional level deviations are acceptable
within qualifying price periods. This reduces period fragmentation while maintaining
meaningful price-based filtering. Long periods are protected by the 25% cap, and
gap clusters trigger intelligent splitting to recover usable sub-periods.
Implemented multi-phase filter relaxation system to ensure minimum number
of best-price and peak-price periods are found, even on days with unusual
price patterns.
New configuration options per period type (best/peak):
- enable_min_periods_{best|peak}: Toggle feature on/off
- min_periods_{best|peak}: Target number of periods (default: 2)
- relaxation_step_{best|peak}: Step size for threshold increase (default: 25%)
Relaxation phases (applied sequentially until target reached):
1. Flex threshold increase (up to 4 steps, e.g., 15% → 18.75% → 22.5% → ...)
2. Volatility filter bypass + continued flex increase
3. All filters off + continued flex increase
Changes to period calculation:
- New calculate_periods_with_relaxation() wrapper function
- filter_periods_by_volatility() now applies post-calculation filtering
- _resolve_period_overlaps() merges baseline + relaxed periods intelligently
- Relaxed periods marked with relaxation_level, relaxation_threshold_* attributes
- Overlap detection prevents double-counting same intervals
Binary sensor attribute ordering improvements:
- Added helper methods for consistent attribute priority
- Relaxation info grouped in priority 6 (after detail attributes)
- Only shown when period was actually relaxed (relaxation_active=true)
Translation updates:
- Added UI labels + descriptions for 6 new config options (all 5 languages)
- Explained relaxation concept with examples in data_description fields
- Clarified volatility filter now applies per-period, not per-day
Impact: Users can configure integration to guarantee minimum number of
periods per day. System automatically relaxes filters when needed while
preserving baseline periods found with strict filters. Particularly useful
for automation reliability on days with flat pricing or unusual patterns.
Fixes edge case where no periods were found despite prices varying enough
for meaningful optimization decisions.
Changed all selector option keys from uppercase to lowercase to comply
with Home Assistant's hassfest validation pattern [a-z0-9-_]+.
Fixed inconsistency in PEAK_PRICE_MIN_LEVEL_OPTIONS where some values
were uppercase while others were lowercase.
Changes:
- translations/*.json: All selector keys now lowercase (volatility, price_level)
- const.py: Added .lower() to all PEAK_PRICE_MIN_LEVEL_OPTIONS values
- binary_sensor.py: Added .upper() conversion when looking up price levels
in PRICE_LEVEL_MAPPING to handle lowercase config values
Impact: Config flow now works correctly with translated selector options.
Hassfest validation passes without selector key errors.
Added comprehensive volatility analysis system:
- 4 new volatility sensors (today, tomorrow, next_24h, today+tomorrow)
- Volatility classification (LOW/MODERATE/HIGH/VERY HIGH) based on price spread
- Configurable thresholds in options flow (step 6 of 6)
- Best/Peak price period filters using volatility and price level
- Price spread calculation in get_price service
Volatility sensors help users decide if price-based optimization is worthwhile.
For example, battery optimization only makes sense when volatility ≥ MODERATE.
Period filters allow AND-logic combinations:
- best_price_min_volatility: Only show cheap periods on volatile days
- best_price_max_level: Only show periods when prices reach desired level
- peak_price_min_volatility: Only show peaks on volatile days
- peak_price_min_level: Only show peaks when expensive levels occur
All 5 language files updated (de, en, nb, nl, sv) with:
- Volatility sensor translations (name, states, descriptions)
- Config flow step 6 "Volatility" with threshold settings
- Step progress indicators added to all config steps
- Period filter translations with usage tips
Impact: Users can now assess daily price volatility and configure period
sensors to only activate when conditions justify battery cycling or load
shifting. Reduces unnecessary battery wear on low-volatility days.
- Revised various phrases for clarity and consistency in Dutch (nl.json) and Swedish (sv.json) translations.
- Changed terms from "woning" to "huis" in Dutch for better contextual accuracy.
- Improved readability and grammatical correctness in both languages.
- Ensured all user-facing strings are updated to reflect the latest terminology and phrasing.
- Updated keys from "cents" to more user-friendly terms for current, next, and previous prices.
- Added state descriptions for price levels and ratings, including categories like "very cheap," "cheap," "normal," "expensive," and "very expensive."
- Introduced new average price sensors for the next 1 to 12 hours.
- Added price trend sensors for 1 to 12 hours with states indicating rising, falling, or stable trends.
- Ensured consistency in naming conventions across English, Norwegian, Dutch, and Swedish translations.
- Added new configuration options for minimum distance from average price for best and peak prices.
- Updated default values for best and peak price flexibility.
- Improved coordinator to handle midnight turnover and data rotation more effectively.
- Refactored entity initialization to streamline device information retrieval.
- Updated sensor attributes to use more descriptive names for price values.
- Enhanced translations for new configuration options in English and German.
- Improved unit tests for coordinator functionality, ensuring proper cleanup and async handling.