Commit graph

139 commits

Author SHA1 Message Date
Julian Pawlowski
decca432df feat(sensors): add timing sensors for best_price and peak_price periods
Added 10 new timing sensors (5 for best_price, 5 for peak_price) to track
period timing and progress:

Timestamp sensors (quarter-hour updates):
- best_price_end_time / peak_price_end_time
  Shows when current/next period ends (always useful reference time)
- best_price_next_start_time / peak_price_next_start_time
  Shows when next period starts (even during active periods)

Countdown sensors (minute updates):
- best_price_remaining_minutes / peak_price_remaining_minutes
  Minutes left in current period (0 when inactive)
- best_price_next_in_minutes / peak_price_next_in_minutes
  Minutes until next period starts
- best_price_progress / peak_price_progress
  Progress percentage through current period (0-100%)

Smart fallback behavior:
- Sensors always show useful values (no 'Unknown' during normal operation)
- Timestamp sensors show current OR next period end/start times
- Countdown sensors return 0 when no period is active
- Grace period: Progress stays at 100% for 60 seconds after period ends

Dynamic visual feedback:
- Progress icons differentiate 3 states at 0%:
  * No data: mdi:help-circle-outline (gray)
  * Waiting for next period: mdi:timer-pause-outline
  * Period just started: mdi:circle-outline
- Progress 1-99%: mdi:circle-slice-1 to mdi:circle-slice-8 (pie chart)
- Timer icons based on urgency (alert/timer/timer-sand/timer-outline)
- Dynamic colors: green (best_price), orange/red (peak_price), gray (disabled)
- icon_color attribute for UI styling

Implementation details:
- Dual update mechanism: quarter-hour (timestamps) + minute (countdowns)
- Period state callbacks: Check if period is currently active
- IconContext dataclass: Reduced function parameters from 6 to 3
- Unit constants: UnitOfTime.MINUTES, PERCENTAGE from homeassistant.const
- Complete translations for 5 languages (de, en, nb, nl, sv)

Impact: Users can now build sophisticated automations based on period timing
('start dishwasher if remaining_minutes > 60'), display countdowns in
dashboards, and get clear visual feedback about period states. All sensors
provide meaningful values at all times, making automation logic simpler.
2025-11-15 17:12:55 +00:00
Julian Pawlowski
22165d038d feat(sensors): add timestamp attributes and enhance icon system
Added timestamp attributes to all sensors and enhanced the dynamic icon
system for comprehensive price sensor coverage with rolling hour support.

TIMESTAMP ATTRIBUTES:

Core Changes:
- sensor/attributes.py:
  * Enhanced add_average_price_attributes() to track extreme intervals
    for min/max sensors and add appropriate timestamps
  * Added _update_extreme_interval() helper to reduce complexity
  * Extended add_volatility_type_attributes() with timestamp logic for
    all 4 volatility types (today/tomorrow/today_tomorrow/next_24h)
  * Fixed current_interval_price timestamp assignment (use interval_data)

Timestamp Logic:
- Interval-based sensors: Use startsAt of specific 15-minute interval
- Min/Max sensors: Use startsAt of interval with extreme price
- Average sensors: Use startsAt of first interval in window
- Volatility sensors: Use midnight (00:00) for calendar day sensors,
  current time for rolling 24h window
- Daily sensors: Already used fallback to midnight (verified)

ICON SYSTEM ENHANCEMENTS:

Major Extensions:
- entity_utils/icons.py:
  * Created get_rolling_hour_price_level_for_icon() implementing
    5-interval window aggregation matching sensor calculation logic
  * Extended get_price_sensor_icon() coverage from 1 to 4 sensors:
    - current_interval_price (existing)
    - next_interval_price (NEW - dynamic instead of static)
    - current_hour_average_price (NEW - uses rolling hour aggregation)
    - next_hour_average_price (NEW - uses rolling hour aggregation)
  * Added imports for aggregate_level_data and find_rolling_hour_center_index

Documentation:
- sensor/definitions.py:
  * Updated 30+ sensor descriptions with detailed icon behavior comments
  * Changed next_interval_price from static to dynamic icon
  * Documented dynamic vs static icons for all sensor types
  * Added clear icon mapping source documentation

SENSOR KEY RENAMING:

Renamed for clarity (current_hour_average → current_hour_average_price):
- sensor/core.py: Updated value getters and cached data lookup
- sensor/definitions.py: Updated entity descriptions
- sensor/attributes.py: Updated key references in attribute builders
- coordinator.py: Updated TIME_SENSITIVE_ENTITY_KEYS set
- const.py: Updated comment documentation

Translation Updates:
- custom_translations/*.json (5 files): Updated sensor keys
- translations/*.json (5 files): Updated sensor keys

Impact:
- All sensors now have timestamp attribute showing applicable time/interval
- Icon system provides richer visual feedback for more sensor types
- Consistent sensor naming improves code readability
- Users get temporal context for all sensor values
- Dynamic icons adapt to price conditions across more sensors
2025-11-15 15:31:43 +00:00
Julian Pawlowski
b32679ba75 feat(translations): add price level and rating states for multiple languages 2025-11-15 14:18:41 +00:00
Julian Pawlowski
e18d653233 feat(sensors): add daily aggregated price level and rating sensors
Added 6 new sensors for yesterday/today/tomorrow aggregated price
levels and ratings, following the same calculation logic as existing
current/next interval sensors.

New sensors:
- yesterday_price_level, today_price_level, tomorrow_price_level
- yesterday_price_rating, today_price_rating, tomorrow_price_rating

Implementation details:
- Added DAILY_LEVEL_SENSORS and DAILY_RATING_SENSORS in sensor/definitions.py
- Implemented _get_daily_aggregated_value() in sensor/core.py using
  existing aggregate_level_data() and aggregate_rating_data() helpers
- Extended icon support in entity_utils/icons.py for dynamic icons
- Added icon_color attributes in sensor/attributes.py with helper
  functions _get_day_key_from_sensor_key() and _add_fallback_timestamp()
- Complete translations in all 5 languages (de, en, nb, nl, sv):
  * Standard translations: sensor names
  * Custom translations: description, long_description, usage_tips

Impact: Users can now see aggregated daily price levels and ratings
for yesterday, today, and tomorrow at a glance, making it easier to
compare overall price situations across days and plan energy consumption
accordingly. Sensors use same aggregation logic as hourly sensors for
consistency.
2025-11-15 13:31:44 +00:00
Julian Pawlowski
1e51b0485b refactor(const): update icon for 'off_no_future' state in BINARY_SENSOR_ICON_MAPPING 2025-11-15 13:08:54 +00:00
Julian Pawlowski
e35970dfcb refactor(const): update icon for 'off_no_future' state in BINARY_SENSOR_ICON_MAPPING 2025-11-15 13:07:04 +00:00
Julian Pawlowski
d90266e1ad refactor(config_flow): split monolithic file into modular package structure
Refactored config_flow.py (995 lines) into focused modules within config_flow/
package to improve maintainability and code organization.

Changes:
- Created config_flow/ package with 6 specialized modules (1,260 lines total)
- Extracted validators to validators.py (95 lines) - pure, testable functions
- Extracted schemas to schemas.py (577 lines) - centralized vol.Schema definitions
- Split flow handlers into separate files:
  * user_flow.py (274 lines) - Main config flow (setup + reauth)
  * subentry_flow.py (124 lines) - Subentry flow (add homes)
  * options_flow.py (160 lines) - Options flow (6-step configuration wizard)
- Package exports via __init__.py (50 lines) for backward compatibility
- Deleted config_flow_legacy.py (no longer needed)

Technical improvements:
- Used Mapping[str, Any] for config_entry.options compatibility
- Proper TYPE_CHECKING imports for circular dependency management
- All 10 inline vol.Schema definitions replaced with reusable functions
- Validators are pure functions (no side effects, easily testable)
- Clear separation of concerns (validation, schemas, flows)

Documentation:
- Updated AGENTS.md with new package structure
- Updated config flow patterns and examples
- Added "Add a new config flow step" guide to Common Tasks
- Marked refactoring plan as COMPLETED with lessons learned

Verification:
- All linting checks pass (./scripts/lint-check)
- All flow handlers import successfully
- Home Assistant loads integration without errors
- All flow types functional (user, subentry, options, reauth)
- No user-facing changes (backward compatible)

Impact: Improves code maintainability by organizing 995 lines into 6 focused
modules (avg 210 lines/module). Enables easier testing, future modifications,
and onboarding of new contributors.
2025-11-15 13:03:13 +00:00
Julian Pawlowski
efda22f7ad refactor(binary_sensor): split into package matching sensor/ structure
Split binary_sensor.py (645 lines) into binary_sensor/ package with
4 modules following the established sensor/ pattern for consistency
and maintainability.

Package structure:
- binary_sensor/__init__.py (32 lines): Platform setup
- binary_sensor/definitions.py (46 lines): ENTITY_DESCRIPTIONS, constants
- binary_sensor/attributes.py (443 lines): Attribute builder functions
- binary_sensor/core.py (282 lines): TibberPricesBinarySensor class

Changes:
- Created binary_sensor/ package with __init__.py importing from .core
- Extracted ENTITY_DESCRIPTIONS and constants to definitions.py
- Moved 13 attribute builders to attributes.py (get_price_intervals_attributes,
  build_async/sync_extra_state_attributes, add_* helpers)
- Moved TibberPricesBinarySensor class to core.py with state logic and
  icon handling
- Used keyword-only parameters to satisfy Ruff PLR0913 (too many args)
- Applied absolute imports (custom_components.tibber_prices.*) in modules

All 4 binary sensors tested and working:
- peak_price_period
- best_price_period
- connection
- tomorrow_data_available

Documentation updated:
- AGENTS.md: Architecture Overview, Component Structure, Common Tasks
- binary-sensor-refactoring-plan.md: Marked  COMPLETED with summary

Impact: Symmetric platform structure (sensor/ ↔ binary_sensor/). Easier
to add new binary sensors following documented pattern. No user-visible
changes.
2025-11-15 12:35:02 +00:00
Copilot
78498a9aec
Fix AttributeError when homes lack active subscriptions (#28)
* Initial plan

* Fix AttributeError for homes without active subscription

Co-authored-by: jpawlowski <75446+jpawlowski@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jpawlowski <75446+jpawlowski@users.noreply.github.com>
2025-11-15 13:08:15 +01:00
Julian Pawlowski
fa40c00f67 refactor(sensors): Transform sensor platform into package 2025-11-15 11:46:54 +00:00
Julian Pawlowski
d2d07d5e16 refactor(sensors): Refactor price sensor calculations and remove unused methods
- Removed the `calculate_current_rolling_5interval_avg` and `calculate_next_hour_rolling_5interval_avg` functions from `average_utils.py` to streamline the codebase.
- Introduced unified methods for retrieving interval values and rolling hour calculations in `sensor.py`, enhancing code reusability and readability.
- Organized sensor definitions into categories based on calculation methods for better maintainability.
- Updated handler methods to utilize the new unified methods, ensuring consistent data retrieval across different sensor types.
- Improved documentation and comments throughout the code to clarify the purpose and functionality of various methods.
2025-11-15 09:29:33 +00:00
Julian Pawlowski
7737dccd49 refactor(sensors): rename current price sensors for clarity
Renamed internal sensor keys to be more explicit about their temporal scope:
- current_price → current_interval_price
- price_level → current_interval_price_level
- price_rating → current_interval_price_rating

This naming makes it clearer that these sensors represent the current
15-minute interval, distinguishing them from hourly averages and other
time-based calculations.

Updated across all components:
- Sensor entity descriptions and handlers (sensor.py)
- Time-sensitive entity keys list (coordinator.py)
- Config flow step IDs (config_flow.py)
- Translation keys in all 5 languages (de, en, nb, nl, sv)
- Custom translations (entity descriptions, usage tips)
- Price level/rating lookups (const.py, sensor.py)
- Documentation examples (AGENTS.md, README.md)

Impact: Sensor entity IDs remain unchanged due to translation_key system.
Existing automations continue to work. Only internal code references and
translation structures updated for consistency.
2025-11-15 08:30:25 +00:00
Julian Pawlowski
c4f36d04de feat(icons): add dynamic icons and colors for all sensor types
Implemented comprehensive dynamic icon and color system across all sensor types:

Price Sensors (5 sensors):
- Current/hour prices: Dynamic cash-family icons based on price level
  (cash-multiple/plus/cash/minus/remove)
- Next/previous: Static contextual icons (cash-fast, cash-refund, clock-fast)
- All have icon_color attribute for card-mod styling

Price Level Sensors (5 sensors):
- Dynamic gauge-family icons: gauge-empty → gauge-low → gauge → gauge-full → alert
- icon_color attribute with CSS variables (green/gray/orange/red)

Price Rating Sensors (5 sensors):
- Dynamic thumb-family icons: thumb-up → thumbs-up-down → thumb-down
- icon_color attribute for LOW/NORMAL/HIGH ratings

Volatility Sensors (4 sensors):
- Dynamic chart-family icons: chart-line-variant → chart-timeline-variant →
  chart-bar → chart-scatter-plot
- icon_color attribute for LOW/MODERATE/HIGH/VERY_HIGH levels

Trend Sensors (8 sensors):
- Dynamic trend icons: trending-up/down/neutral based on price movement
- icon_color attribute (red=rising, green=falling, gray=stable)

Binary Sensors (2 sensors):
- Best Price Period: piggy-bank (ON) / timer-sand or timer-sand-complete (OFF)
- Peak Price Period: alert-circle (ON) / shield-check or shield-check-outline (OFF)
- 6-hour lookahead window for intelligent OFF state icons
- icon_color attribute for all states

Technical implementation:
- PRICE_LEVEL_CASH_ICON_MAPPING in const.py for price sensor icons
- PRICE_SENSOR_ICON_MAPPING removed (static icons now in entity descriptions)
- Centralized icon logic in sensor.py icon property
- All color mappings use CSS variables for theme compatibility
- Binary sensors detect future periods within 6-hour window

Impact: Users now have visual indicators for all price-related states without
requiring card-mod. Optional card-mod styling available via icon_color attribute
for advanced customization. Icons update dynamically as price levels, ratings,
volatility, and trends change throughout the day.
2025-11-14 11:31:25 +00:00
Julian Pawlowski
fe5af68f8e feat(sensor): add icon color determination based on price trend state 2025-11-14 09:52:28 +00:00
Julian Pawlowski
6521aa7bdd feat(sensor): add dynamic icon support for price trend sensors 2025-11-14 09:39:37 +00:00
Julian Pawlowski
3a9234ffbf chore(release): bump version to 0.6.1 2025-11-14 01:14:05 +00:00
Julian Pawlowski
07517660e3 refactor(volatility): migrate to coefficient of variation calculation
Replaced absolute volatility thresholds (ct/øre) with relative coefficient
of variation (CV = std_dev / mean * 100%) for scale-independent volatility
measurement that works across all price levels.

Changes to volatility calculation:
- price_utils.py: Rewrote calculate_volatility_level() to accept price list
  instead of spread value, using statistics.mean() and statistics.stdev()
- sensor.py: Updated volatility sensors to pass price lists (not spread)
- services.py: Modified _get_price_stats() to calculate CV from prices
- period_statistics.py: Extract prices for CV calculation in period summaries
- const.py: Updated default thresholds to 15%/30%/50% (was 5/15/30 ct)
  with comprehensive documentation explaining CV-based approach

Dead code removal:
- period_utils/core.py: Removed filter_periods_by_volatility() function
  (86 lines of code that was never actually called)
- period_utils/__init__.py: Removed dead function export
- period_utils/relaxation.py: Simplified callback signature from
  Callable[[str|None, str|None], bool] to Callable[[str|None], bool]
- coordinator.py: Updated lambda callbacks to match new signature
- const.py: Replaced RELAXATION_VOLATILITY_ANY with RELAXATION_LEVEL_ANY

Bug fix:
- relaxation.py: Added int() conversion for max_relaxation_attempts
  (line 435: attempts = max(1, int(max_relaxation_attempts)))
  Fixes TypeError when config value arrives as float

Configuration UI:
- config_flow.py: Changed volatility threshold unit display from "ct" to "%"

Translations (all 5 languages):
- Updated volatility descriptions to explain coefficient of variation
- Changed threshold labels from "spread ≥ value" to "CV ≥ percentage"
- Languages: de, en, nb, nl, sv

Documentation:
- period-calculation.md: Removed volatility filter section (dead feature)

Impact: Breaking change for users with custom volatility thresholds.
Old absolute values (e.g., 5 ct) will be interpreted as percentages (5%).
However, new defaults (15%/30%/50%) are more conservative and work
universally across all currencies and price levels. No data migration
needed - existing configs continue to work with new interpretation.
2025-11-14 01:12:47 +00:00
Julian Pawlowski
6dc49becb1 chore(release): bump version to 0.6.0 2025-11-14 00:18:32 +00:00
Julian Pawlowski
67270d8fe2 chore(release): bump version to 0.5.1 2025-11-14 00:07:50 +00:00
Julian Pawlowski
5a5c8ca3cc feat(relaxation): make tail handling smarter and attempts configurable
- 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.
2025-11-14 00:07:12 +00:00
Julian Pawlowski
d3c02568ee fix(build_periods): improve comment clarity for smoothing impact on interval qualification 2025-11-13 23:00:26 +00:00
Julian Pawlowski
a39eb66f49 fix(const): clarify comments on peak price flexibility threshold 2025-11-13 23:00:18 +00:00
Julian Pawlowski
2e7dd64db0 chore(release): bump version to 0.5.0 2025-11-13 22:53:48 +00:00
Julian Pawlowski
383b495545
Feature/adaptive defaults (#22)
* 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>
2025-11-13 23:51:29 +01:00
Julian Pawlowski
3f43bb4bc0 chore(release): bump version to 0.4.1 2025-11-11 21:22:36 +00:00
Julian Pawlowski
aa58c6718f refactor(period_utils): implement per-day relaxation with 4×4 matrix strategy
Restructured relaxation mechanism to process each day independently instead
of globally, enabling different days to relax at different levels.

Key changes:
- Added hierarchical logging with INDENT_L0-L5 constants
- Replaced global relaxation loop with per-day relaxation (_relax_single_day)
- Implemented 4×4 matrix strategy (4 flex levels × 4 filter combinations)
- Enhanced _resolve_period_overlaps with replacement and extension logic
- Added helper functions: _group_periods_by_day, _group_prices_by_day,
  _check_min_periods_per_day

Relaxation strategy:
- Each flex level tries 4 filter combinations before increasing flex
- Early exit after EACH successful combination (minimal relaxation)
- Extensions preserve baseline metadata, replacements use relaxed metadata
- Only standalone periods count toward min_periods requirement

Impact: Users get more accurate period detection per day. Days with clear
cheap/expensive patterns use strict filters while difficult days relax as
needed. Reduces over-relaxation - finds 'good enough' solutions faster.
2025-11-11 21:21:56 +00:00
Julian Pawlowski
7605e88b96 refactor(period_utils): simplify period qualification logic by removing average boundary check 2025-11-10 14:58:16 +00:00
Julian Pawlowski
6a77572f4e refactor(docs): update references from copilot-instructions.md to AGENTS.md across documentation 2025-11-10 14:09:40 +00:00
Julian Pawlowski
6e1a3e37c5 chore(release): bump version to 0.4.0 2025-11-10 12:15:58 +00:00
Julian Pawlowski
817658f230 feat(periods): add gap tolerance for price level filters with intelligent period splitting
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.
2025-11-10 04:38:44 +00:00
Julian Pawlowski
40a335dabe feat(periods): add adaptive filter relaxation for minimum period guarantee
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.
2025-11-10 03:34:09 +00:00
Julian Pawlowski
9640b041e0 refactor(periods): move all period logic to coordinator and refactor period_utils
Moved filter logic and all period attribute calculations from binary_sensor.py
to coordinator.py and period_utils.py, following Home Assistant best practices
for data flow architecture.

ARCHITECTURE CHANGES:

Binary Sensor Simplification (~225 lines removed):
- Removed _build_periods_summary, _add_price_diff_for_period (calculation logic)
- Removed _get_period_intervals_from_price_info (107 lines, interval reconstruction)
- Removed _should_show_periods, _check_volatility_filter, _check_level_filter
- Removed _build_empty_periods_result (filtering result builder)
- Removed _get_price_hours_attributes (24 lines, dead code)
- Removed datetime import (unused after cleanup)
- New: _build_final_attributes_simple (~20 lines, timestamp-only)
- Result: Pure display-only logic, reads pre-calculated data from coordinator

Coordinator Enhancement (+160 lines):
- Added _should_show_periods(): UND-Verknüpfung of volatility and level filters
- Added _check_volatility_filter(): Checks min_volatility threshold
- Added _check_level_filter(): Checks min/max level bounds
- Enhanced _calculate_periods_for_price_info(): Applies filters before period calculation
- Returns empty periods when filters don't match (instead of calculating unnecessarily)
- Passes volatility thresholds (moderate/high/very_high) to PeriodConfig

Period Utils Refactoring (+110 lines):
- Extended PeriodConfig with threshold_volatility_moderate/high/very_high
- Added PeriodData NamedTuple: Groups timing data (start, end, length, position)
- Added PeriodStatistics NamedTuple: Groups calculated stats (prices, volatility, ratings)
- Added ThresholdConfig NamedTuple: Groups all thresholds + reverse_sort flag
- New _calculate_period_price_statistics(): Extracts price_avg/min/max/spread calculation
- New _build_period_summary_dict(): Builds final dict with correct attribute ordering
- Enhanced _extract_period_summaries(): Now calculates ALL attributes (no longer lightweight):
  * price_avg, price_min, price_max, price_spread (in minor units: ct/øre)
  * volatility (low/moderate/high/very_high based on absolute thresholds)
  * rating_difference_% (average of interval differences)
  * period_price_diff_from_daily_min/max (period avg vs daily reference)
  * aggregated level and rating_level
  * period_interval_count (renamed from interval_count for clarity)
- Removed interval_starts array (redundant - start/end/count sufficient)
- Function signature refactored from 9→4 parameters using NamedTuples

Code Organization (HA Best Practice):
- Moved calculate_volatility_level() from const.py to price_utils.py
- Rule: const.py should contain only constants, no functions
- Removed duplicate VOLATILITY_THRESHOLD_* constants from const.py
- Updated imports in sensor.py, services.py, period_utils.py

DATA FLOW:

Before:
API → Coordinator (basic enrichment) → Binary Sensor (calculate everything on each access)

After:
API → Coordinator (enrichment + filtering + period calculation with ALL attributes) →
      Cached Data → Binary Sensor (display + timestamp only)

ATTRIBUTE STRUCTURE:

Period summaries now contain (following copilot-instructions.md ordering):
1. Time: start, end, duration_minutes
2. Decision: level, rating_level, rating_difference_%
3. Prices: price_avg, price_min, price_max, price_spread, volatility
4. Differences: period_price_diff_from_daily_min/max (conditional)
5. Details: period_interval_count, period_position
6. Meta: periods_total, periods_remaining

BREAKING CHANGES: None
- Period data structure enhanced but backwards compatible
- Binary sensor API unchanged (state + attributes)

Impact: Binary sensors now display pre-calculated data from coordinator instead
of calculating on every access. Reduces complexity, improves performance, and
centralizes business logic following Home Assistant coordinator pattern. All
period filtering (volatility + level) now happens in coordinator before caching.
2025-11-09 23:46:48 +00:00
Julian Pawlowski
b36a94d53b feat(translations): update language style and tone for user instructions across multiple languages 2025-11-09 19:27:42 +00:00
Julian Pawlowski
900e77203a chore(release): bump version to 0.3.0 2025-11-09 16:06:34 +00:00
Julian Pawlowski
ae82e4637c fix: handle missing entry_id in reauth flow and ensure integration version is a string 2025-11-09 16:04:18 +00:00
Julian Pawlowski
da5a723777 fix: update return type annotation for API request method and ensure exceptions are raised 2025-11-09 16:04:12 +00:00
Julian Pawlowski
12fbe33bb9 fix: handle unknown integration version in setup entry 2025-11-09 16:04:04 +00:00
Julian Pawlowski
7e57facf50 fix(translations): restore corrupted Norwegian and Dutch translations
Restored valid Norwegian (nb) and Dutch (nl) translations from git
commit 5e0a297 after corruption caused JSON parsing errors.

Added 4 missing volatility sensor translations to both languages:
- today_volatility
- tomorrow_volatility
- next_24h_volatility
- today_tomorrow_volatility

All translations include complete description, long_description, and
usage_tips fields.

Impact: Norwegian and Dutch users now see complete, properly translated
sensor descriptions. JSON validation passes without errors.
2025-11-09 15:31:56 +00:00
Julian Pawlowski
532a91be58 fix(translations): resolve hassfest selector key validation errors
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.
2025-11-09 15:31:37 +00:00
Julian Pawlowski
f4568be34e feat(sensors): add price volatility analysis and period filters
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.
2025-11-09 14:24:34 +00:00
Julian Pawlowski
32e429624e refactor: Adjust suggested display precision for future average price sensors and add timestamp attributes for next average calculations 2025-11-08 17:06:19 +00:00
Julian Pawlowski
ac100216ee refactor: Update attribute naming and ordering for clarity and consistency 2025-11-08 16:50:55 +00:00
Julian Pawlowski
db0d65a939 feat: Add price trend thresholds configuration and update related calculations 2025-11-08 16:02:21 +00:00
Julian Pawlowski
5ba0633d15 refactor: Store trend attributes in a sensor-specific dictionary for better organization 2025-11-08 15:30:55 +00:00
Julian Pawlowski
f9f4908748 refactor: Enhance period calculations with aggregated levels and ratings 2025-11-08 15:01:25 +00:00
Julian Pawlowski
db3299b7a7 Add period calculation for best and peak prices
- Introduced a new utility module `period_utils.py` for calculating price periods.
- Implemented `_get_period_config` method to retrieve configuration for best and peak price calculations.
- Added `_calculate_periods_for_price_info` method to compute best and peak price periods based on price data.
- Enhanced `TibberPricesDataUpdateCoordinator` to include calculated periods in the data transformation methods.
- Updated configuration constants for best and peak price settings.
2025-11-08 14:26:46 +00:00
Julian Pawlowski
3f3edd8a28 refactor: Update price level and rating options to inline definitions for sensor initialization 2025-11-08 09:24:28 +00:00
Julian Pawlowski
9c6ebc45ec chore: Bump version to 0.2.0 2025-11-07 23:45:28 +00:00
Julian Pawlowski
df9fb37fe4 refactor: Update integration version handling in TibberPrices components 2025-11-07 23:43:39 +00:00
Julian Pawlowski
3df68db20b refactor: Update interval attribute keys and improve period merging logic in TibberPricesBinarySensor 2025-11-07 23:31:29 +00:00