Commit graph

10 commits

Author SHA1 Message Date
Julian Pawlowski
76dc488bb5 feat(sensors): add momentum-based trend detection with two new sensors
Added intelligent price trend analysis combining historical momentum
(weighted 1h lookback) with future outlook for more accurate trend
recognition. Introduced two complementary sensors for comprehensive
trend monitoring.

New sensors:
- current_price_trend: Shows active trend direction with duration
- next_price_trend_change: Predicts when trend will reverse

Momentum analysis (historical perspective):
- Weighted 1h lookback (4 × 15-min intervals)
- Linear weight progression [0.5, 0.75, 1.0, 1.25]
- ±3% threshold for momentum classification
- Recognizes ongoing trends earlier than future-only analysis

Two-phase trend calculation:
- Phase 1: Calculate momentum from weighted trailing average
- Phase 2: Validate with volatility-adaptive future comparison
- Combines both for final trend determination (rising/falling/stable)
- Centralized in _calculate_trend_info() with 60s cache

Volatility-adaptive thresholds:
- Existing trend sensors (1h-12h) now use adaptive thresholds
- calculate_price_trend() adjusted by market volatility:
  * LOW volatility (<15% CV): factor 0.6 → more sensitive (e.g., 3%→1.8%)
  * MODERATE volatility (15-30%): factor 1.0 → baseline (3%)
  * HIGH volatility (≥30%): factor 1.4 → less sensitive (e.g., 3%→4.2%)
- Uses same coefficient of variation as volatility sensors
- Ensures mathematical consistency across integration

Default threshold reduction:
- Rising/falling thresholds: 5% → 3% (more responsive)
- Momentum-based detection enables lower thresholds without noise
- Adaptive adjustment compensates during high volatility

Architectural improvements:
- Centralized calculation: Single source of truth for both sensors
- Eliminates Henne-Ei problem (duplicate calculations)
- 60-second cache per coordinator update
- Shared helper methods: _calculate_momentum(), _combine_momentum_with_future()

Translation updates (all 5 languages):
- Documented momentum feature in custom_translations (de/en/nb/nl/sv)
- Explained "recognizes ongoing trends earlier" advantage
- Added sensor names and state options to standard translations
- Updated volatility threshold descriptions (clarify usage by trend sensors)

Files changed:
- custom_components/tibber_prices/sensor/core.py (930 lines added)
  * New: _calculate_momentum(), _combine_momentum_with_future()
  * New: _calculate_trend_info() (centralized with cache)
  * New: _get_current_trend_value(), _get_next_trend_change_value()
  * Modified: _get_price_trend_value() (volatility-adaptive thresholds)
- custom_components/tibber_prices/sensor/definitions.py
  * Added: current_price_trend (ENUM sensor)
  * Added: next_price_trend_change (TIMESTAMP sensor)
- custom_components/tibber_prices/sensor/attributes.py
  * New: _add_cached_trend_attributes() helper
  * Support for current_trend_attributes, trend_change_attributes
- custom_components/tibber_prices/price_utils.py (178 lines added)
  * New: _calculate_lookahead_volatility_factor()
  * Modified: calculate_price_trend() with volatility adjustment
  * Added: VOLATILITY_FACTOR_* constants (0.6/1.0/1.4)
- custom_components/tibber_prices/entity_utils/icons.py
  * Added: Dynamic icon handling for next_price_trend_change
- custom_components/tibber_prices/const.py
  * Changed: DEFAULT_PRICE_TREND_THRESHOLD_RISING/FALLING (5→3%)
- custom_components/tibber_prices/translations/*.json (5 files)
  * Added: Sensor names, state options, descriptions
- custom_components/tibber_prices/custom_translations/*.json (5 files)
  * Added: Long descriptions with momentum feature explanation

Impact: Users get significantly more accurate trend detection that
understands they're ALREADY in a trend, not just predicting future
changes. Momentum-based approach recognizes ongoing movements 15-60
minutes earlier. Adaptive thresholds prevent false signals during
volatile periods. Two complementary sensors enable both status display
(current trend) and event-based automation (when will it change).
Perfect for use cases like "charge EV when next trend change shows
falling prices" or dashboard badges showing "Rising for 2.5h".
2025-11-16 12:49:43 +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
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
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
db0d65a939 feat: Add price trend thresholds configuration and update related calculations 2025-11-08 16:02:21 +00:00
Julian Pawlowski
f9f4908748 refactor: Enhance period calculations with aggregated levels and ratings 2025-11-08 15:01:25 +00:00
Julian Pawlowski
ef1a81ccc1 Refactor translations for electricity prices in multiple languages
- 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.
2025-11-06 22:36:12 +00:00
Julian Pawlowski
bba5f180b0 add lots of new sensors 2025-11-03 20:55:28 +00:00
Julian Pawlowski
ef05929247 fix entity update 2025-11-02 23:14:26 +00:00
Julian Pawlowski
9fd196948c remove priceRating API relations 2025-11-02 22:30:01 +00:00