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).
This commit is contained in:
Julian Pawlowski 2025-11-12 13:20:14 +00:00
parent b1c36f5279
commit 53e73a7fda
11 changed files with 1054 additions and 340 deletions

View file

@ -175,7 +175,7 @@ automation:
entity_id: switch.dishwasher entity_id: switch.dishwasher
``` ```
> **Learn more:** The [period calculation guide](docs/user/period-calculation.md) explains how Best/Peak Price periods are identified and how you can configure filters (flexibility, minimum volatility, price level filters with gap tolerance). > **Learn more:** The [period calculation guide](docs/user/period-calculation.md) explains how Best/Peak Price periods are identified and how you can configure filters (flexibility, minimum distance from average, price level filters with gap tolerance).
### Notify on Extremely High Prices ### Notify on Extremely High Prices

View file

@ -45,7 +45,6 @@ from .const import (
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT, CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_BEST_PRICE_MIN_PERIOD_LENGTH, CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
CONF_BEST_PRICE_MIN_VOLATILITY,
CONF_ENABLE_MIN_PERIODS_BEST, CONF_ENABLE_MIN_PERIODS_BEST,
CONF_ENABLE_MIN_PERIODS_PEAK, CONF_ENABLE_MIN_PERIODS_PEAK,
CONF_EXTENDED_DESCRIPTIONS, CONF_EXTENDED_DESCRIPTIONS,
@ -56,7 +55,6 @@ from .const import (
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_PEAK_PRICE_MIN_LEVEL, CONF_PEAK_PRICE_MIN_LEVEL,
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH, CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
CONF_PEAK_PRICE_MIN_VOLATILITY,
CONF_PRICE_RATING_THRESHOLD_HIGH, CONF_PRICE_RATING_THRESHOLD_HIGH,
CONF_PRICE_RATING_THRESHOLD_LOW, CONF_PRICE_RATING_THRESHOLD_LOW,
CONF_PRICE_TREND_THRESHOLD_FALLING, CONF_PRICE_TREND_THRESHOLD_FALLING,
@ -71,7 +69,6 @@ from .const import (
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT, DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH, DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_BEST_PRICE_MIN_VOLATILITY,
DEFAULT_ENABLE_MIN_PERIODS_BEST, DEFAULT_ENABLE_MIN_PERIODS_BEST,
DEFAULT_ENABLE_MIN_PERIODS_PEAK, DEFAULT_ENABLE_MIN_PERIODS_PEAK,
DEFAULT_EXTENDED_DESCRIPTIONS, DEFAULT_EXTENDED_DESCRIPTIONS,
@ -82,7 +79,6 @@ from .const import (
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_PEAK_PRICE_MIN_LEVEL, DEFAULT_PEAK_PRICE_MIN_LEVEL,
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH, DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_PEAK_PRICE_MIN_VOLATILITY,
DEFAULT_PRICE_RATING_THRESHOLD_HIGH, DEFAULT_PRICE_RATING_THRESHOLD_HIGH,
DEFAULT_PRICE_RATING_THRESHOLD_LOW, DEFAULT_PRICE_RATING_THRESHOLD_LOW,
DEFAULT_PRICE_TREND_THRESHOLD_FALLING, DEFAULT_PRICE_TREND_THRESHOLD_FALLING,
@ -94,7 +90,6 @@ from .const import (
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH, DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
DOMAIN, DOMAIN,
LOGGER, LOGGER,
MIN_VOLATILITY_FOR_PERIODS_OPTIONS,
PEAK_PRICE_MIN_LEVEL_OPTIONS, PEAK_PRICE_MIN_LEVEL_OPTIONS,
) )
@ -657,19 +652,6 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
mode=NumberSelectorMode.SLIDER, mode=NumberSelectorMode.SLIDER,
), ),
), ),
vol.Optional(
CONF_BEST_PRICE_MIN_VOLATILITY,
default=self.config_entry.options.get(
CONF_BEST_PRICE_MIN_VOLATILITY,
DEFAULT_BEST_PRICE_MIN_VOLATILITY,
),
): SelectSelector(
SelectSelectorConfig(
options=MIN_VOLATILITY_FOR_PERIODS_OPTIONS,
mode=SelectSelectorMode.DROPDOWN,
translation_key="volatility",
),
),
vol.Optional( vol.Optional(
CONF_BEST_PRICE_MAX_LEVEL, CONF_BEST_PRICE_MAX_LEVEL,
default=self.config_entry.options.get( default=self.config_entry.options.get(
@ -805,19 +787,6 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
mode=NumberSelectorMode.SLIDER, mode=NumberSelectorMode.SLIDER,
), ),
), ),
vol.Optional(
CONF_PEAK_PRICE_MIN_VOLATILITY,
default=self.config_entry.options.get(
CONF_PEAK_PRICE_MIN_VOLATILITY,
DEFAULT_PEAK_PRICE_MIN_VOLATILITY,
),
): SelectSelector(
SelectSelectorConfig(
options=MIN_VOLATILITY_FOR_PERIODS_OPTIONS,
mode=SelectSelectorMode.DROPDOWN,
translation_key="volatility",
),
),
vol.Optional( vol.Optional(
CONF_PEAK_PRICE_MIN_LEVEL, CONF_PEAK_PRICE_MIN_LEVEL,
default=self.config_entry.options.get( default=self.config_entry.options.get(

View file

@ -31,8 +31,6 @@ CONF_PRICE_TREND_THRESHOLD_FALLING = "price_trend_threshold_falling"
CONF_VOLATILITY_THRESHOLD_MODERATE = "volatility_threshold_moderate" CONF_VOLATILITY_THRESHOLD_MODERATE = "volatility_threshold_moderate"
CONF_VOLATILITY_THRESHOLD_HIGH = "volatility_threshold_high" CONF_VOLATILITY_THRESHOLD_HIGH = "volatility_threshold_high"
CONF_VOLATILITY_THRESHOLD_VERY_HIGH = "volatility_threshold_very_high" CONF_VOLATILITY_THRESHOLD_VERY_HIGH = "volatility_threshold_very_high"
CONF_BEST_PRICE_MIN_VOLATILITY = "best_price_min_volatility"
CONF_PEAK_PRICE_MIN_VOLATILITY = "peak_price_min_volatility"
CONF_BEST_PRICE_MAX_LEVEL = "best_price_max_level" CONF_BEST_PRICE_MAX_LEVEL = "best_price_max_level"
CONF_PEAK_PRICE_MIN_LEVEL = "peak_price_min_level" CONF_PEAK_PRICE_MIN_LEVEL = "peak_price_min_level"
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT = "best_price_max_level_gap_count" CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT = "best_price_max_level_gap_count"
@ -50,11 +48,11 @@ ATTRIBUTION = "Data provided by Tibber"
DEFAULT_NAME = "Tibber Price Information & Ratings" DEFAULT_NAME = "Tibber Price Information & Ratings"
DEFAULT_EXTENDED_DESCRIPTIONS = False DEFAULT_EXTENDED_DESCRIPTIONS = False
DEFAULT_BEST_PRICE_FLEX = 15 # 15% flexibility for best price (user-facing, percent) DEFAULT_BEST_PRICE_FLEX = 15 # 15% flexibility for best price (user-facing, percent)
DEFAULT_PEAK_PRICE_FLEX = -15 # 15% flexibility for peak price (user-facing, percent) DEFAULT_PEAK_PRICE_FLEX = -20 # 20% flexibility for peak price (user-facing, percent)
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG = 2 # 2% minimum distance from daily average for best price DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG = 5 # 5% minimum distance from daily average (ensures significance)
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG = 2 # 2% minimum distance from daily average for peak price DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG = 5 # 5% minimum distance from daily average (ensures significance)
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH = 60 # 60 minutes minimum period length for best price (user-facing, minutes) DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH = 60 # 60 minutes minimum period length for best price (user-facing, minutes)
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH = 60 # 60 minutes minimum period length for peak price (user-facing, minutes) DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH = 30 # 30 minutes minimum period length for peak price (user-facing, minutes)
DEFAULT_PRICE_RATING_THRESHOLD_LOW = -10 # Default rating threshold low percentage DEFAULT_PRICE_RATING_THRESHOLD_LOW = -10 # Default rating threshold low percentage
DEFAULT_PRICE_RATING_THRESHOLD_HIGH = 10 # Default rating threshold high percentage DEFAULT_PRICE_RATING_THRESHOLD_HIGH = 10 # Default rating threshold high percentage
DEFAULT_PRICE_TREND_THRESHOLD_RISING = 5 # Default trend threshold for rising prices (%) DEFAULT_PRICE_TREND_THRESHOLD_RISING = 5 # Default trend threshold for rising prices (%)
@ -62,17 +60,15 @@ DEFAULT_PRICE_TREND_THRESHOLD_FALLING = -5 # Default trend threshold for fallin
DEFAULT_VOLATILITY_THRESHOLD_MODERATE = 5.0 # Default threshold for MODERATE volatility (ct/øre) DEFAULT_VOLATILITY_THRESHOLD_MODERATE = 5.0 # Default threshold for MODERATE volatility (ct/øre)
DEFAULT_VOLATILITY_THRESHOLD_HIGH = 15.0 # Default threshold for HIGH volatility (ct/øre) DEFAULT_VOLATILITY_THRESHOLD_HIGH = 15.0 # Default threshold for HIGH volatility (ct/øre)
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH = 30.0 # Default threshold for VERY_HIGH volatility (ct/øre) DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH = 30.0 # Default threshold for VERY_HIGH volatility (ct/øre)
DEFAULT_BEST_PRICE_MIN_VOLATILITY = "low" # Show best price at any volatility (optimization always useful) DEFAULT_BEST_PRICE_MAX_LEVEL = "cheap" # Default: prefer genuinely cheap periods, relax to "any" if needed
DEFAULT_PEAK_PRICE_MIN_VOLATILITY = "low" # Always show peak price (warning relevant even at low spreads) DEFAULT_PEAK_PRICE_MIN_LEVEL = "expensive" # Default: prefer genuinely expensive periods, relax to "any" if needed
DEFAULT_BEST_PRICE_MAX_LEVEL = "any" # Default: show best price periods regardless of price level DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT = 1 # Default: allow 1 level gap (e.g., CHEAP→NORMAL→CHEAP stays together)
DEFAULT_PEAK_PRICE_MIN_LEVEL = "any" # Default: show peak price periods regardless of price level DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT = 1 # Default: allow 1 level gap for peak price periods
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT = 0 # Default: no tolerance for level gaps (strict filtering)
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT = 0 # Default: no tolerance for level gaps (strict filtering)
MIN_INTERVALS_FOR_GAP_TOLERANCE = 6 # Minimum period length (in 15-min intervals = 1.5h) required for gap tolerance MIN_INTERVALS_FOR_GAP_TOLERANCE = 6 # Minimum period length (in 15-min intervals = 1.5h) required for gap tolerance
DEFAULT_ENABLE_MIN_PERIODS_BEST = False # Default: minimum periods feature disabled for best price DEFAULT_ENABLE_MIN_PERIODS_BEST = True # Default: minimum periods feature enabled for best price
DEFAULT_MIN_PERIODS_BEST = 2 # Default: require at least 2 best price periods (when enabled) DEFAULT_MIN_PERIODS_BEST = 2 # Default: require at least 2 best price periods (when enabled)
DEFAULT_RELAXATION_STEP_BEST = 25 # Default: 25% of original threshold per relaxation step for best price DEFAULT_RELAXATION_STEP_BEST = 25 # Default: 25% of original threshold per relaxation step for best price
DEFAULT_ENABLE_MIN_PERIODS_PEAK = False # Default: minimum periods feature disabled for peak price DEFAULT_ENABLE_MIN_PERIODS_PEAK = True # Default: minimum periods feature enabled for peak price
DEFAULT_MIN_PERIODS_PEAK = 2 # Default: require at least 2 peak price periods (when enabled) DEFAULT_MIN_PERIODS_PEAK = 2 # Default: require at least 2 peak price periods (when enabled)
DEFAULT_RELAXATION_STEP_PEAK = 25 # Default: 25% of original threshold per relaxation step for peak price DEFAULT_RELAXATION_STEP_PEAK = 25 # Default: 25% of original threshold per relaxation step for peak price
@ -193,15 +189,7 @@ VOLATILITY_OPTIONS = [
VOLATILITY_VERY_HIGH.lower(), VOLATILITY_VERY_HIGH.lower(),
] ]
# Valid options for minimum volatility filter for periods # Valid options for best price maximum level filter
MIN_VOLATILITY_FOR_PERIODS_OPTIONS = [
VOLATILITY_LOW.lower(), # Show at any volatility (≥0ct spread) - no filter
VOLATILITY_MODERATE.lower(), # Only show periods when volatility ≥ MODERATE (≥5ct)
VOLATILITY_HIGH.lower(), # Only show periods when volatility ≥ HIGH (≥15ct)
VOLATILITY_VERY_HIGH.lower(), # Only show periods when volatility ≥ VERY_HIGH (≥30ct)
]
# Valid options for best price maximum level filter (AND-linked with volatility filter)
# Sorted from cheap to expensive: user selects "up to how expensive" # Sorted from cheap to expensive: user selects "up to how expensive"
BEST_PRICE_MAX_LEVEL_OPTIONS = [ BEST_PRICE_MAX_LEVEL_OPTIONS = [
"any", # No filter, allow all price levels "any", # No filter, allow all price levels

View file

@ -30,7 +30,6 @@ from .const import (
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT, CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_BEST_PRICE_MIN_PERIOD_LENGTH, CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
CONF_BEST_PRICE_MIN_VOLATILITY,
CONF_ENABLE_MIN_PERIODS_BEST, CONF_ENABLE_MIN_PERIODS_BEST,
CONF_ENABLE_MIN_PERIODS_PEAK, CONF_ENABLE_MIN_PERIODS_PEAK,
CONF_MIN_PERIODS_BEST, CONF_MIN_PERIODS_BEST,
@ -40,7 +39,6 @@ from .const import (
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_PEAK_PRICE_MIN_LEVEL, CONF_PEAK_PRICE_MIN_LEVEL,
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH, CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
CONF_PEAK_PRICE_MIN_VOLATILITY,
CONF_PRICE_RATING_THRESHOLD_HIGH, CONF_PRICE_RATING_THRESHOLD_HIGH,
CONF_PRICE_RATING_THRESHOLD_LOW, CONF_PRICE_RATING_THRESHOLD_LOW,
CONF_RELAXATION_STEP_BEST, CONF_RELAXATION_STEP_BEST,
@ -53,7 +51,6 @@ from .const import (
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT, DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH, DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_BEST_PRICE_MIN_VOLATILITY,
DEFAULT_ENABLE_MIN_PERIODS_BEST, DEFAULT_ENABLE_MIN_PERIODS_BEST,
DEFAULT_ENABLE_MIN_PERIODS_PEAK, DEFAULT_ENABLE_MIN_PERIODS_PEAK,
DEFAULT_MIN_PERIODS_BEST, DEFAULT_MIN_PERIODS_BEST,
@ -63,7 +60,6 @@ from .const import (
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_PEAK_PRICE_MIN_LEVEL, DEFAULT_PEAK_PRICE_MIN_LEVEL,
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH, DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_PEAK_PRICE_MIN_VOLATILITY,
DEFAULT_PRICE_RATING_THRESHOLD_HIGH, DEFAULT_PRICE_RATING_THRESHOLD_HIGH,
DEFAULT_PRICE_RATING_THRESHOLD_LOW, DEFAULT_PRICE_RATING_THRESHOLD_LOW,
DEFAULT_RELAXATION_STEP_BEST, DEFAULT_RELAXATION_STEP_BEST,
@ -78,7 +74,6 @@ from .const import (
from .period_utils import ( from .period_utils import (
PeriodConfig, PeriodConfig,
calculate_periods_with_relaxation, calculate_periods_with_relaxation,
filter_periods_by_volatility,
) )
from .price_utils import ( from .price_utils import (
enrich_price_info_with_differences, enrich_price_info_with_differences,
@ -908,6 +903,10 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
# Periods shorter than MIN_INTERVALS_FOR_GAP_TOLERANCE (1.5h) use strict filtering # Periods shorter than MIN_INTERVALS_FOR_GAP_TOLERANCE (1.5h) use strict filtering
if interval_count < MIN_INTERVALS_FOR_GAP_TOLERANCE: if interval_count < MIN_INTERVALS_FOR_GAP_TOLERANCE:
_LOGGER.debug(
"Using strict filtering for short period (%d intervals)",
interval_count,
)
return self._check_short_period_strict(today_intervals, level_order, reverse_sort=reverse_sort) return self._check_short_period_strict(today_intervals, level_order, reverse_sort=reverse_sort)
# Try normal gap tolerance check first # Try normal gap tolerance check first
@ -1143,14 +1142,19 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH, DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
) )
# Check if best price periods should be shown (apply filters)
show_best_price = self._should_show_periods(price_info, reverse_sort=False) if all_prices else False
# Get relaxation configuration for best price # Get relaxation configuration for best price
enable_relaxation_best = self.config_entry.options.get( enable_relaxation_best = self.config_entry.options.get(
CONF_ENABLE_MIN_PERIODS_BEST, CONF_ENABLE_MIN_PERIODS_BEST,
DEFAULT_ENABLE_MIN_PERIODS_BEST, DEFAULT_ENABLE_MIN_PERIODS_BEST,
) )
# Check if best price periods should be shown
# If relaxation is enabled, always calculate (relaxation will try "any" filter)
# If relaxation is disabled, apply level filter check
if enable_relaxation_best:
show_best_price = bool(all_prices)
else:
show_best_price = self._should_show_periods(price_info, reverse_sort=False) if all_prices else False
min_periods_best = self.config_entry.options.get( min_periods_best = self.config_entry.options.get(
CONF_MIN_PERIODS_BEST, CONF_MIN_PERIODS_BEST,
DEFAULT_MIN_PERIODS_BEST, DEFAULT_MIN_PERIODS_BEST,
@ -1163,6 +1167,15 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
# Calculate best price periods (or return empty if filtered) # Calculate best price periods (or return empty if filtered)
if show_best_price: if show_best_price:
best_config = self._get_period_config(reverse_sort=False) best_config = self._get_period_config(reverse_sort=False)
# Get level filter configuration
max_level_best = self.config_entry.options.get(
CONF_BEST_PRICE_MAX_LEVEL,
DEFAULT_BEST_PRICE_MAX_LEVEL,
)
gap_count_best = self.config_entry.options.get(
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
DEFAULT_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
)
best_period_config = PeriodConfig( best_period_config = PeriodConfig(
reverse_sort=False, reverse_sort=False,
flex=best_config["flex"], flex=best_config["flex"],
@ -1173,6 +1186,8 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
threshold_volatility_moderate=threshold_volatility_moderate, threshold_volatility_moderate=threshold_volatility_moderate,
threshold_volatility_high=threshold_volatility_high, threshold_volatility_high=threshold_volatility_high,
threshold_volatility_very_high=threshold_volatility_very_high, threshold_volatility_very_high=threshold_volatility_very_high,
level_filter=max_level_best,
gap_count=gap_count_best,
) )
best_periods, best_relaxation = calculate_periods_with_relaxation( best_periods, best_relaxation = calculate_periods_with_relaxation(
all_prices, all_prices,
@ -1186,13 +1201,6 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
level_override=lvl, level_override=lvl,
), ),
) )
# Apply period-level volatility filter (after calculation)
min_volatility_best = self.config_entry.options.get(
CONF_BEST_PRICE_MIN_VOLATILITY,
DEFAULT_BEST_PRICE_MIN_VOLATILITY,
)
best_periods = filter_periods_by_volatility(best_periods, min_volatility_best)
else: else:
best_periods = { best_periods = {
"periods": [], "periods": [],
@ -1201,14 +1209,19 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
} }
best_relaxation = {"relaxation_active": False, "relaxation_attempted": False} best_relaxation = {"relaxation_active": False, "relaxation_attempted": False}
# Check if peak price periods should be shown (apply filters)
show_peak_price = self._should_show_periods(price_info, reverse_sort=True) if all_prices else False
# Get relaxation configuration for peak price # Get relaxation configuration for peak price
enable_relaxation_peak = self.config_entry.options.get( enable_relaxation_peak = self.config_entry.options.get(
CONF_ENABLE_MIN_PERIODS_PEAK, CONF_ENABLE_MIN_PERIODS_PEAK,
DEFAULT_ENABLE_MIN_PERIODS_PEAK, DEFAULT_ENABLE_MIN_PERIODS_PEAK,
) )
# Check if peak price periods should be shown
# If relaxation is enabled, always calculate (relaxation will try "any" filter)
# If relaxation is disabled, apply level filter check
if enable_relaxation_peak:
show_peak_price = bool(all_prices)
else:
show_peak_price = self._should_show_periods(price_info, reverse_sort=True) if all_prices else False
min_periods_peak = self.config_entry.options.get( min_periods_peak = self.config_entry.options.get(
CONF_MIN_PERIODS_PEAK, CONF_MIN_PERIODS_PEAK,
DEFAULT_MIN_PERIODS_PEAK, DEFAULT_MIN_PERIODS_PEAK,
@ -1221,6 +1234,15 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
# Calculate peak price periods (or return empty if filtered) # Calculate peak price periods (or return empty if filtered)
if show_peak_price: if show_peak_price:
peak_config = self._get_period_config(reverse_sort=True) peak_config = self._get_period_config(reverse_sort=True)
# Get level filter configuration
min_level_peak = self.config_entry.options.get(
CONF_PEAK_PRICE_MIN_LEVEL,
DEFAULT_PEAK_PRICE_MIN_LEVEL,
)
gap_count_peak = self.config_entry.options.get(
CONF_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
DEFAULT_PEAK_PRICE_MAX_LEVEL_GAP_COUNT,
)
peak_period_config = PeriodConfig( peak_period_config = PeriodConfig(
reverse_sort=True, reverse_sort=True,
flex=peak_config["flex"], flex=peak_config["flex"],
@ -1231,6 +1253,8 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
threshold_volatility_moderate=threshold_volatility_moderate, threshold_volatility_moderate=threshold_volatility_moderate,
threshold_volatility_high=threshold_volatility_high, threshold_volatility_high=threshold_volatility_high,
threshold_volatility_very_high=threshold_volatility_very_high, threshold_volatility_very_high=threshold_volatility_very_high,
level_filter=min_level_peak,
gap_count=gap_count_peak,
) )
peak_periods, peak_relaxation = calculate_periods_with_relaxation( peak_periods, peak_relaxation = calculate_periods_with_relaxation(
all_prices, all_prices,
@ -1244,13 +1268,6 @@ class TibberPricesDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
level_override=lvl, level_override=lvl,
), ),
) )
# Apply period-level volatility filter (after calculation)
min_volatility_peak = self.config_entry.options.get(
CONF_PEAK_PRICE_MIN_VOLATILITY,
DEFAULT_PEAK_PRICE_MIN_VOLATILITY,
)
peak_periods = filter_periods_by_volatility(peak_periods, min_volatility_peak)
else: else:
peak_periods = { peak_periods = {
"periods": [], "periods": [],

View file

@ -17,6 +17,7 @@ from .const import (
DEFAULT_VOLATILITY_THRESHOLD_HIGH, DEFAULT_VOLATILITY_THRESHOLD_HIGH,
DEFAULT_VOLATILITY_THRESHOLD_MODERATE, DEFAULT_VOLATILITY_THRESHOLD_MODERATE,
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH, DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
PRICE_LEVEL_MAPPING,
) )
from .price_utils import ( from .price_utils import (
aggregate_period_levels, aggregate_period_levels,
@ -49,6 +50,8 @@ class PeriodConfig(NamedTuple):
threshold_volatility_moderate: float = DEFAULT_VOLATILITY_THRESHOLD_MODERATE threshold_volatility_moderate: float = DEFAULT_VOLATILITY_THRESHOLD_MODERATE
threshold_volatility_high: float = DEFAULT_VOLATILITY_THRESHOLD_HIGH threshold_volatility_high: float = DEFAULT_VOLATILITY_THRESHOLD_HIGH
threshold_volatility_very_high: float = DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH threshold_volatility_very_high: float = DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH
level_filter: str | None = None # "any", "cheap", "expensive", etc. or None
gap_count: int = 0 # Number of allowed consecutive deviating intervals
class PeriodData(NamedTuple): class PeriodData(NamedTuple):
@ -87,6 +90,16 @@ class ThresholdConfig(NamedTuple):
reverse_sort: bool reverse_sort: bool
class IntervalCriteria(NamedTuple):
"""Criteria for checking if an interval qualifies for a period."""
ref_price: float
avg_price: float
flex: float
min_distance_from_avg: float
reverse_sort: bool
def calculate_periods( def calculate_periods(
all_prices: list[dict], all_prices: list[dict],
*, *,
@ -160,7 +173,13 @@ def calculate_periods(
"flex": flex, "flex": flex,
"min_distance_from_avg": min_distance_from_avg, "min_distance_from_avg": min_distance_from_avg,
} }
raw_periods = _build_periods(all_prices_sorted, price_context, reverse_sort=reverse_sort) raw_periods = _build_periods(
all_prices_sorted,
price_context,
reverse_sort=reverse_sort,
level_filter=config.level_filter,
gap_count=config.gap_count,
)
# Step 4: Filter by minimum length # Step 4: Filter by minimum length
raw_periods = _filter_periods_by_min_length(raw_periods, min_period_length) raw_periods = _filter_periods_by_min_length(raw_periods, min_period_length)
@ -238,11 +257,123 @@ def _calculate_reference_prices(intervals_by_day: dict[date, list[dict]], *, rev
return ref_prices return ref_prices
def _check_level_with_gap_tolerance(
interval_level: int,
level_order: int,
consecutive_gaps: int,
gap_count: int,
*,
reverse_sort: bool,
) -> tuple[bool, bool, int]:
"""
Check if interval meets level requirement with gap tolerance.
Args:
interval_level: Level value of current interval (from PRICE_LEVEL_MAPPING)
level_order: Required level value
consecutive_gaps: Current count of consecutive gap intervals
gap_count: Maximum allowed consecutive gap intervals
reverse_sort: True for peak price, False for best price
Returns:
Tuple of (meets_level, is_gap, new_consecutive_gaps):
- meets_level: True if interval qualifies (exact match or within gap tolerance)
- is_gap: True if this is a gap interval (deviates by exactly 1 step)
- new_consecutive_gaps: Updated gap counter
"""
if reverse_sort:
# Peak price: interval must be >= level_order (e.g., EXPENSIVE or higher)
meets_level_exact = interval_level >= level_order
# Gap: exactly 1 step below (e.g., NORMAL when expecting EXPENSIVE)
is_gap = interval_level == level_order - 1
else:
# Best price: interval must be <= level_order (e.g., CHEAP or lower)
meets_level_exact = interval_level <= level_order
# Gap: exactly 1 step above (e.g., NORMAL when expecting CHEAP)
is_gap = interval_level == level_order + 1
# Apply gap tolerance
if meets_level_exact:
return True, False, 0 # Meets level, not a gap, reset counter
if is_gap and consecutive_gaps < gap_count:
return True, True, consecutive_gaps + 1 # Allowed gap, increment counter
return False, False, 0 # Doesn't meet level, reset counter
def _apply_level_filter(
price_data: dict,
level_order: int | None,
consecutive_gaps: int,
gap_count: int,
*,
reverse_sort: bool,
) -> tuple[bool, int]:
"""
Apply level filter to a single interval.
Args:
price_data: Price data dict with "level" key
level_order: Required level value (from PRICE_LEVEL_MAPPING) or None if disabled
consecutive_gaps: Current count of consecutive gap intervals
gap_count: Maximum allowed consecutive gap intervals
reverse_sort: True for peak price, False for best price
Returns:
Tuple of (meets_level, new_consecutive_gaps)
"""
if level_order is None:
return True, consecutive_gaps
interval_level = PRICE_LEVEL_MAPPING.get(price_data.get("level", "NORMAL"), 0)
meets_level, _is_gap, new_consecutive_gaps = _check_level_with_gap_tolerance(
interval_level, level_order, consecutive_gaps, gap_count, reverse_sort=reverse_sort
)
return meets_level, new_consecutive_gaps
def _check_interval_criteria(
price: float,
criteria: IntervalCriteria,
) -> tuple[bool, bool]:
"""
Check if interval meets flex and minimum distance criteria.
Args:
price: Interval price
criteria: Interval criteria (ref_price, avg_price, flex, etc.)
Returns:
Tuple of (in_flex, meets_min_distance)
"""
# Calculate percentage difference from reference
percent_diff = ((price - criteria.ref_price) / criteria.ref_price) * 100 if criteria.ref_price != 0 else 0.0
# Check if interval qualifies for the period
in_flex = percent_diff >= criteria.flex * 100 if criteria.reverse_sort else percent_diff <= criteria.flex * 100
# Minimum distance from average
if criteria.reverse_sort:
# Peak price: must be at least min_distance_from_avg% above average
min_distance_threshold = criteria.avg_price * (1 + criteria.min_distance_from_avg / 100)
meets_min_distance = price >= min_distance_threshold
else:
# Best price: must be at least min_distance_from_avg% below average
min_distance_threshold = criteria.avg_price * (1 - criteria.min_distance_from_avg / 100)
meets_min_distance = price <= min_distance_threshold
return in_flex, meets_min_distance
def _build_periods( def _build_periods(
all_prices: list[dict], all_prices: list[dict],
price_context: dict[str, Any], price_context: dict[str, Any],
*, *,
reverse_sort: bool, reverse_sort: bool,
level_filter: str | None = None,
gap_count: int = 0,
) -> list[list[dict]]: ) -> list[list[dict]]:
""" """
Build periods, allowing periods to cross midnight (day boundary). Build periods, allowing periods to cross midnight (day boundary).
@ -251,15 +382,45 @@ def _build_periods(
When a day boundary is crossed, the current period is ended. When a day boundary is crossed, the current period is ended.
Adjacent periods at midnight are merged in a later step. Adjacent periods at midnight are merged in a later step.
Args:
all_prices: All price data points
price_context: Dict with ref_prices, avg_prices, flex, min_distance_from_avg
reverse_sort: True for peak price (high prices), False for best price (low prices)
level_filter: Level filter string ("cheap", "expensive", "any", None)
gap_count: Number of allowed consecutive intervals deviating by exactly 1 level step
""" """
ref_prices = price_context["ref_prices"] ref_prices = price_context["ref_prices"]
avg_prices = price_context["avg_prices"] avg_prices = price_context["avg_prices"]
flex = price_context["flex"] flex = price_context["flex"]
min_distance_from_avg = price_context["min_distance_from_avg"] min_distance_from_avg = price_context["min_distance_from_avg"]
# Calculate level_order if level_filter is active
level_order = None
level_filter_active = False
if level_filter and level_filter.lower() != "any":
level_order = PRICE_LEVEL_MAPPING.get(level_filter.upper(), 0)
level_filter_active = True
filter_direction = "" if reverse_sort else ""
gap_info = f", gap_tolerance={gap_count}" if gap_count > 0 else ""
_LOGGER.debug(
"%sLevel filter active: %s (order %s, require interval level %s filter level%s)",
INDENT_L3,
level_filter.upper(),
level_order,
filter_direction,
gap_info,
)
else:
status = "RELAXED to ANY" if (level_filter and level_filter.lower() == "any") else "DISABLED (not configured)"
_LOGGER.debug("%sLevel filter: %s (accepting all levels)", INDENT_L3, status)
periods: list[list[dict]] = [] periods: list[list[dict]] = []
current_period: list[dict] = [] current_period: list[dict] = []
last_ref_date: date | None = None last_ref_date: date | None = None
consecutive_gaps = 0 # Track consecutive intervals that deviate by 1 level step
intervals_checked = 0
intervals_filtered_by_level = 0
for price_data in all_prices: for price_data in all_prices:
starts_at = dt_util.parse_datetime(price_data["startsAt"]) starts_at = dt_util.parse_datetime(price_data["startsAt"])
@ -267,36 +428,37 @@ def _build_periods(
continue continue
starts_at = dt_util.as_local(starts_at) starts_at = dt_util.as_local(starts_at)
date_key = starts_at.date() date_key = starts_at.date()
ref_price = ref_prices[date_key]
avg_price = avg_prices[date_key]
price = float(price_data["total"]) price = float(price_data["total"])
# Calculate percentage difference from reference intervals_checked += 1
percent_diff = ((price - ref_price) / ref_price) * 100 if ref_price != 0 else 0.0
percent_diff = round(percent_diff, 2)
# Check if interval qualifies for the period # Check flex and minimum distance criteria
in_flex = percent_diff >= flex * 100 if reverse_sort else percent_diff <= flex * 100 criteria = IntervalCriteria(
ref_price=ref_prices[date_key],
avg_price=avg_prices[date_key],
flex=flex,
min_distance_from_avg=min_distance_from_avg,
reverse_sort=reverse_sort,
)
in_flex, meets_min_distance = _check_interval_criteria(price, criteria)
# Minimum distance from average # Level filter: Check if interval meets level requirement with gap tolerance
if reverse_sort: meets_level, consecutive_gaps = _apply_level_filter(
# Peak price: must be at least min_distance_from_avg% above average price_data, level_order, consecutive_gaps, gap_count, reverse_sort=reverse_sort
min_distance_threshold = avg_price * (1 + min_distance_from_avg / 100) )
meets_min_distance = price >= min_distance_threshold if not meets_level:
else: intervals_filtered_by_level += 1
# Best price: must be at least min_distance_from_avg% below average
min_distance_threshold = avg_price * (1 - min_distance_from_avg / 100)
meets_min_distance = price <= min_distance_threshold
# Split period if day changes # Split period if day changes
if last_ref_date is not None and date_key != last_ref_date and current_period: if last_ref_date is not None and date_key != last_ref_date and current_period:
periods.append(current_period) periods.append(current_period)
current_period = [] current_period = []
consecutive_gaps = 0 # Reset gap counter on day boundary
last_ref_date = date_key last_ref_date = date_key
# Add to period if all criteria are met # Add to period if all criteria are met
if in_flex and meets_min_distance: if in_flex and meets_min_distance and meets_level:
current_period.append( current_period.append(
{ {
"interval_hour": starts_at.hour, "interval_hour": starts_at.hour,
@ -310,11 +472,23 @@ def _build_periods(
# Criteria no longer met, end current period # Criteria no longer met, end current period
periods.append(current_period) periods.append(current_period)
current_period = [] current_period = []
consecutive_gaps = 0 # Reset gap counter
# Add final period if exists # Add final period if exists
if current_period: if current_period:
periods.append(current_period) periods.append(current_period)
# Log summary
if level_filter_active and intervals_checked > 0:
filtered_pct = (intervals_filtered_by_level / intervals_checked) * 100
_LOGGER.debug(
"%sLevel filter summary: %d/%d intervals filtered (%.1f%%)",
INDENT_L3,
intervals_filtered_by_level,
intervals_checked,
filtered_pct,
)
return periods return periods
@ -1196,12 +1370,29 @@ def _relax_single_day( # noqa: PLR0913 - Comprehensive filter relaxation per da
continue continue
# Calculate periods with this flex + filter combination # Calculate periods with this flex + filter combination
relaxed_config = config._replace(flex=new_flex) # Apply level override if specified
level_filter_value = lvl_override if lvl_override else config.level_filter
# Log filter changes
flex_pct = round(abs(new_flex) * 100, 1)
if lvl_override:
_LOGGER.debug(
"%sDay %s flex=%.1f%%: OVERRIDING level_filter: %s%s",
INDENT_L2,
day_label,
flex_pct,
config.level_filter or "None",
lvl_override.upper(),
)
relaxed_config = config._replace(
flex=new_flex,
level_filter=level_filter_value,
)
relaxed_result = calculate_periods(day_prices, config=relaxed_config) relaxed_result = calculate_periods(day_prices, config=relaxed_config)
new_periods = relaxed_result["periods"] new_periods = relaxed_result["periods"]
# Build relaxation level label BEFORE marking periods # Build relaxation level label BEFORE marking periods
flex_pct = round(abs(new_flex) * 100, 1)
relaxation_level = f"price_diff_{flex_pct}%{label_suffix}" relaxation_level = f"price_diff_{flex_pct}%{label_suffix}"
phases_used.append(relaxation_level) phases_used.append(relaxation_level)

View file

@ -104,7 +104,6 @@
"best_price_min_period_length": "Minimale Periodenlänge", "best_price_min_period_length": "Minimale Periodenlänge",
"best_price_flex": "Flexibilität: Maximal über dem Mindestpreis", "best_price_flex": "Flexibilität: Maximal über dem Mindestpreis",
"best_price_min_distance_from_avg": "Mindestabstand: Erforderlich unter dem Tagesdurchschnitt", "best_price_min_distance_from_avg": "Mindestabstand: Erforderlich unter dem Tagesdurchschnitt",
"best_price_min_volatility": "Mindest-Volatilitätsfilter",
"best_price_max_level": "Preisniveau-Filter (Optional)", "best_price_max_level": "Preisniveau-Filter (Optional)",
"best_price_max_level_gap_count": "Lückentoleranz für Niveaufilter", "best_price_max_level_gap_count": "Lückentoleranz für Niveaufilter",
"enable_min_periods_best": "Mindestanzahl Perioden anstreben", "enable_min_periods_best": "Mindestanzahl Perioden anstreben",
@ -112,7 +111,6 @@
"relaxation_step_best": "Lockerungsschritt" "relaxation_step_best": "Lockerungsschritt"
}, },
"data_description": { "data_description": {
"best_price_min_volatility": "Zeigt Bestpreis-Perioden nur an, wenn die interne Preisvolatilität der Periode (Preisspanne innerhalb der Periode) mindestens diesem Level entspricht. Standard: 'Niedrig' (zeigt Perioden mit beliebigem Volatilitätslevel) - ermöglicht das Finden günstiger Perioden auch wenn die Preise stabil sind. Wähle 'Moderat'/'Hoch' um nur Perioden mit signifikanten Preisschwankungen anzuzeigen, was auf dynamischere Preismöglichkeiten hinweisen kann.",
"best_price_max_level": "Zeigt Bestpreis-Perioden nur an, wenn sie Intervalle mit Preisniveaus ≤ dem gewählten Wert enthalten. Beispiel: Wahl von 'Günstig' bedeutet, dass die Periode mindestens ein 'SEHR_GÜNSTIG' oder 'GÜNSTIG' Intervall haben muss. Dies stellt sicher, dass 'Bestpreis'-Perioden nicht nur relativ günstig für den Tag sind, sondern tatsächlich günstig in absoluten Zahlen. Wähle 'Beliebig' um Bestpreise unabhängig vom absoluten Preisniveau anzuzeigen.", "best_price_max_level": "Zeigt Bestpreis-Perioden nur an, wenn sie Intervalle mit Preisniveaus ≤ dem gewählten Wert enthalten. Beispiel: Wahl von 'Günstig' bedeutet, dass die Periode mindestens ein 'SEHR_GÜNSTIG' oder 'GÜNSTIG' Intervall haben muss. Dies stellt sicher, dass 'Bestpreis'-Perioden nicht nur relativ günstig für den Tag sind, sondern tatsächlich günstig in absoluten Zahlen. Wähle 'Beliebig' um Bestpreise unabhängig vom absoluten Preisniveau anzuzeigen.",
"best_price_max_level_gap_count": "Maximale Anzahl aufeinanderfolgender Intervalle, die exakt um eine Niveaustufe vom geforderten Level abweichen dürfen. Beispiel: Bei Filter 'Günstig' und Lückentoleranz 1 wird die Sequenz 'GÜNSTIG, GÜNSTIG, NORMAL, GÜNSTIG' akzeptiert (NORMAL ist eine Stufe über GÜNSTIG). Dies verhindert, dass Perioden durch gelegentliche Niveau-Abweichungen aufgespalten werden. Standard: 0 (strenge Filterung, keine Toleranz).", "best_price_max_level_gap_count": "Maximale Anzahl aufeinanderfolgender Intervalle, die exakt um eine Niveaustufe vom geforderten Level abweichen dürfen. Beispiel: Bei Filter 'Günstig' und Lückentoleranz 1 wird die Sequenz 'GÜNSTIG, GÜNSTIG, NORMAL, GÜNSTIG' akzeptiert (NORMAL ist eine Stufe über GÜNSTIG). Dies verhindert, dass Perioden durch gelegentliche Niveau-Abweichungen aufgespalten werden. Standard: 0 (strenge Filterung, keine Toleranz).",
"enable_min_periods_best": "Wenn aktiviert, werden Filter schrittweise gelockert, falls nicht genug Perioden gefunden wurden. Dies versucht die gewünschte Mindestanzahl zu erreichen, was dazu führen kann, dass auch weniger optimale Zeiträume als Bestpreis-Perioden markiert werden.", "enable_min_periods_best": "Wenn aktiviert, werden Filter schrittweise gelockert, falls nicht genug Perioden gefunden wurden. Dies versucht die gewünschte Mindestanzahl zu erreichen, was dazu führen kann, dass auch weniger optimale Zeiträume als Bestpreis-Perioden markiert werden.",
@ -128,7 +126,6 @@
"peak_price_min_period_length": "Minimale Periodenlänge", "peak_price_min_period_length": "Minimale Periodenlänge",
"peak_price_flex": "Flexibilität: Maximal unter dem Höchstpreis (negativer Wert)", "peak_price_flex": "Flexibilität: Maximal unter dem Höchstpreis (negativer Wert)",
"peak_price_min_distance_from_avg": "Mindestabstand: Erforderlich über dem Tagesdurchschnitt", "peak_price_min_distance_from_avg": "Mindestabstand: Erforderlich über dem Tagesdurchschnitt",
"peak_price_min_volatility": "Mindest-Volatilitätsfilter",
"peak_price_min_level": "Preisniveau-Filter (Optional)", "peak_price_min_level": "Preisniveau-Filter (Optional)",
"peak_price_max_level_gap_count": "Lückentoleranz für Niveaufilter", "peak_price_max_level_gap_count": "Lückentoleranz für Niveaufilter",
"enable_min_periods_peak": "Mindestanzahl Perioden anstreben", "enable_min_periods_peak": "Mindestanzahl Perioden anstreben",
@ -136,7 +133,6 @@
"relaxation_step_peak": "Lockerungsschritt" "relaxation_step_peak": "Lockerungsschritt"
}, },
"data_description": { "data_description": {
"peak_price_min_volatility": "Zeigt Spitzenpreis-Perioden nur an, wenn die interne Preisvolatilität der Periode (Preisspanne innerhalb der Periode) mindestens diesem Level entspricht. Standard: 'Niedrig' (zeigt Perioden mit beliebigem Volatilitätslevel) - ermöglicht das Identifizieren teurer Perioden auch wenn die Preise stabil sind. Wähle 'Moderat'/'Hoch' um nur Perioden mit signifikanten Preisschwankungen anzuzeigen, was auf dringenderen Bedarf hinweisen kann, diese Zeiten zu vermeiden.",
"peak_price_min_level": "Zeigt Spitzenpreis-Perioden nur an, wenn sie Intervalle mit Preisniveaus ≥ dem gewählten Wert enthalten. Beispiel: Wahl von 'Teuer' bedeutet, dass die Periode mindestens ein 'TEUER' oder 'SEHR_TEUER' Intervall haben muss. Dies stellt sicher, dass 'Spitzenpreis'-Perioden nicht nur relativ teuer für den Tag sind, sondern tatsächlich teuer in absoluten Zahlen. Wähle 'Beliebig' um Spitzenpreise unabhängig vom absoluten Preisniveau anzuzeigen.", "peak_price_min_level": "Zeigt Spitzenpreis-Perioden nur an, wenn sie Intervalle mit Preisniveaus ≥ dem gewählten Wert enthalten. Beispiel: Wahl von 'Teuer' bedeutet, dass die Periode mindestens ein 'TEUER' oder 'SEHR_TEUER' Intervall haben muss. Dies stellt sicher, dass 'Spitzenpreis'-Perioden nicht nur relativ teuer für den Tag sind, sondern tatsächlich teuer in absoluten Zahlen. Wähle 'Beliebig' um Spitzenpreise unabhängig vom absoluten Preisniveau anzuzeigen.",
"peak_price_max_level_gap_count": "Maximale Anzahl aufeinanderfolgender Intervalle, die exakt um eine Niveaustufe vom geforderten Level abweichen dürfen. Beispiel: Bei Filter 'Teuer' und Lückentoleranz 2 wird die Sequenz 'TEUER, NORMAL, NORMAL, TEUER' akzeptiert (NORMAL ist eine Stufe unter TEUER). Dies verhindert, dass Perioden durch gelegentliche Niveau-Abweichungen aufgespalten werden. Standard: 0 (strenge Filterung, keine Toleranz).", "peak_price_max_level_gap_count": "Maximale Anzahl aufeinanderfolgender Intervalle, die exakt um eine Niveaustufe vom geforderten Level abweichen dürfen. Beispiel: Bei Filter 'Teuer' und Lückentoleranz 2 wird die Sequenz 'TEUER, NORMAL, NORMAL, TEUER' akzeptiert (NORMAL ist eine Stufe unter TEUER). Dies verhindert, dass Perioden durch gelegentliche Niveau-Abweichungen aufgespalten werden. Standard: 0 (strenge Filterung, keine Toleranz).",
"enable_min_periods_peak": "Wenn aktiviert, werden Filter schrittweise gelockert, falls nicht genug Perioden gefunden wurden. Dies versucht die gewünschte Mindestanzahl zu erreichen, um sicherzustellen, dass du auch an Tagen mit ungewöhnlichen Preismustern vor teuren Perioden gewarnt wirst.", "enable_min_periods_peak": "Wenn aktiviert, werden Filter schrittweise gelockert, falls nicht genug Perioden gefunden wurden. Dies versucht die gewünschte Mindestanzahl zu erreichen, um sicherzustellen, dass du auch an Tagen mit ungewöhnlichen Preismustern vor teuren Perioden gewarnt wirst.",

View file

@ -104,7 +104,6 @@
"best_price_min_period_length": "Minimum Period Length", "best_price_min_period_length": "Minimum Period Length",
"best_price_flex": "Flexibility: Maximum above minimum price", "best_price_flex": "Flexibility: Maximum above minimum price",
"best_price_min_distance_from_avg": "Minimum Distance: Required below daily average", "best_price_min_distance_from_avg": "Minimum Distance: Required below daily average",
"best_price_min_volatility": "Minimum Volatility Filter",
"best_price_max_level": "Price Level Filter (Optional)", "best_price_max_level": "Price Level Filter (Optional)",
"best_price_max_level_gap_count": "Level Filter Gap Tolerance", "best_price_max_level_gap_count": "Level Filter Gap Tolerance",
"enable_min_periods_best": "Try to Achieve Minimum Period Count", "enable_min_periods_best": "Try to Achieve Minimum Period Count",
@ -112,7 +111,6 @@
"relaxation_step_best": "Filter Relaxation Step Size" "relaxation_step_best": "Filter Relaxation Step Size"
}, },
"data_description": { "data_description": {
"best_price_min_volatility": "Only show best price periods when the period's internal price volatility (spread within the period) meets or exceeds this level. Default: 'Low' (show periods with any volatility level) - allows finding cheap periods even if prices are stable. Select 'Moderate'/'High' to only show periods with significant price variations, which may indicate more dynamic pricing opportunities.",
"best_price_max_level": "Only show best price periods if they contain intervals with price levels ≤ selected value. For example, selecting 'Cheap' means the period must have at least one 'VERY_CHEAP' or 'CHEAP' interval. This ensures 'best price' periods are not just relatively cheap for the day, but actually cheap in absolute terms. Select 'Any' to show best prices regardless of their absolute price level.", "best_price_max_level": "Only show best price periods if they contain intervals with price levels ≤ selected value. For example, selecting 'Cheap' means the period must have at least one 'VERY_CHEAP' or 'CHEAP' interval. This ensures 'best price' periods are not just relatively cheap for the day, but actually cheap in absolute terms. Select 'Any' to show best prices regardless of their absolute price level.",
"best_price_max_level_gap_count": "Maximum number of consecutive intervals allowed that deviate by exactly one level step from the required level. For example: with 'Cheap' filter and gap count 1, a sequence 'CHEAP, CHEAP, NORMAL, CHEAP' is accepted (NORMAL is one step above CHEAP). This prevents periods from being split by occasional level deviations. Default: 0 (strict filtering, no tolerance).", "best_price_max_level_gap_count": "Maximum number of consecutive intervals allowed that deviate by exactly one level step from the required level. For example: with 'Cheap' filter and gap count 1, a sequence 'CHEAP, CHEAP, NORMAL, CHEAP' is accepted (NORMAL is one step above CHEAP). This prevents periods from being split by occasional level deviations. Default: 0 (strict filtering, no tolerance).",
"enable_min_periods_best": "When enabled, filters will be gradually relaxed if not enough periods are found. This attempts to reach the desired minimum number of periods, which may include less optimal time windows as best-price periods.", "enable_min_periods_best": "When enabled, filters will be gradually relaxed if not enough periods are found. This attempts to reach the desired minimum number of periods, which may include less optimal time windows as best-price periods.",
@ -128,7 +126,6 @@
"peak_price_min_period_length": "Minimum Period Length", "peak_price_min_period_length": "Minimum Period Length",
"peak_price_flex": "Flexibility: Maximum below maximum price (negative value)", "peak_price_flex": "Flexibility: Maximum below maximum price (negative value)",
"peak_price_min_distance_from_avg": "Minimum Distance: Required above daily average", "peak_price_min_distance_from_avg": "Minimum Distance: Required above daily average",
"peak_price_min_volatility": "Minimum Volatility Filter",
"peak_price_min_level": "Price Level Filter (Optional)", "peak_price_min_level": "Price Level Filter (Optional)",
"peak_price_max_level_gap_count": "Level Filter Gap Tolerance", "peak_price_max_level_gap_count": "Level Filter Gap Tolerance",
"enable_min_periods_peak": "Try to Achieve Minimum Period Count", "enable_min_periods_peak": "Try to Achieve Minimum Period Count",
@ -136,7 +133,6 @@
"relaxation_step_peak": "Filter Relaxation Step Size" "relaxation_step_peak": "Filter Relaxation Step Size"
}, },
"data_description": { "data_description": {
"peak_price_min_volatility": "Only show peak price periods when the period's internal price volatility (spread within the period) meets or exceeds this level. Default: 'Low' (show periods with any volatility level) - allows identifying expensive periods even if prices are stable. Select 'Moderate'/'High' to only show periods with significant price variations, which may indicate more urgent need to avoid these times.",
"peak_price_min_level": "Only show peak price periods if they contain intervals with price levels ≥ selected value. For example, selecting 'Expensive' means the period must have at least one 'EXPENSIVE' or 'VERY_EXPENSIVE' interval. This ensures 'peak price' periods are not just relatively expensive for the day, but actually expensive in absolute terms. Select 'Any' to show peak prices regardless of their absolute price level.", "peak_price_min_level": "Only show peak price periods if they contain intervals with price levels ≥ selected value. For example, selecting 'Expensive' means the period must have at least one 'EXPENSIVE' or 'VERY_EXPENSIVE' interval. This ensures 'peak price' periods are not just relatively expensive for the day, but actually expensive in absolute terms. Select 'Any' to show peak prices regardless of their absolute price level.",
"peak_price_max_level_gap_count": "Maximum number of consecutive intervals allowed that deviate by exactly one level step from the required level. For example: with 'Expensive' filter and gap count 2, a sequence 'EXPENSIVE, NORMAL, NORMAL, EXPENSIVE' is accepted (NORMAL is one step below EXPENSIVE). This prevents periods from being split by occasional level deviations. Default: 0 (strict filtering, no tolerance).", "peak_price_max_level_gap_count": "Maximum number of consecutive intervals allowed that deviate by exactly one level step from the required level. For example: with 'Expensive' filter and gap count 2, a sequence 'EXPENSIVE, NORMAL, NORMAL, EXPENSIVE' is accepted (NORMAL is one step below EXPENSIVE). This prevents periods from being split by occasional level deviations. Default: 0 (strict filtering, no tolerance).",
"enable_min_periods_peak": "When enabled, filters will be gradually relaxed if not enough periods are found. This attempts to reach the desired minimum number of periods to ensure you're warned about expensive periods even on days with unusual price patterns.", "enable_min_periods_peak": "When enabled, filters will be gradually relaxed if not enough periods are found. This attempts to reach the desired minimum number of periods to ensure you're warned about expensive periods even on days with unusual price patterns.",

View file

@ -104,7 +104,6 @@
"best_price_min_period_length": "Minimum periodelengde", "best_price_min_period_length": "Minimum periodelengde",
"best_price_flex": "Fleksibilitet: Maksimum % over minimumspris", "best_price_flex": "Fleksibilitet: Maksimum % over minimumspris",
"best_price_min_distance_from_avg": "Minimumsavstand: Påkrevd % under daglig gjennomsnitt", "best_price_min_distance_from_avg": "Minimumsavstand: Påkrevd % under daglig gjennomsnitt",
"best_price_min_volatility": "Minimum volatilitetsfilter",
"best_price_max_level": "Prisnivåfilter (valgfritt)", "best_price_max_level": "Prisnivåfilter (valgfritt)",
"best_price_max_level_gap_count": "Gaptoleranse for nivåfilter", "best_price_max_level_gap_count": "Gaptoleranse for nivåfilter",
"enable_min_periods_best": "Prøv å oppnå minimum antall perioder", "enable_min_periods_best": "Prøv å oppnå minimum antall perioder",
@ -112,7 +111,6 @@
"relaxation_step_best": "Avslappingstrinn" "relaxation_step_best": "Avslappingstrinn"
}, },
"data_description": { "data_description": {
"best_price_min_volatility": "Vis kun beste prisperioder når periodens interne prisvolatilitet (prisspennet innenfor perioden) oppfyller eller overskrider dette nivået. Standard: 'Lav' (vis perioder med hvilket som helst volatilitetsnivå) - gjør det mulig å finne billige perioder selv om prisene er stabile. Velg 'Moderat'/'Høy' for kun å vise perioder med betydelige prisvariasjoner, noe som kan indikere mer dynamiske prismuligheter.",
"best_price_max_level": "Vis kun beste prisperioder hvis de inneholder intervaller med prisnivåer ≤ valgt verdi. For eksempel: å velge 'Billig' betyr at perioden må ha minst ett 'VELDIG_BILLIG' eller 'BILLIG' intervall. Dette sikrer at 'beste pris'-perioder ikke bare er relativt billige for dagen, men faktisk billige i absolutte tall. Velg 'Alle' for å vise beste priser uavhengig av deres absolutte prisnivå.", "best_price_max_level": "Vis kun beste prisperioder hvis de inneholder intervaller med prisnivåer ≤ valgt verdi. For eksempel: å velge 'Billig' betyr at perioden må ha minst ett 'VELDIG_BILLIG' eller 'BILLIG' intervall. Dette sikrer at 'beste pris'-perioder ikke bare er relativt billige for dagen, men faktisk billige i absolutte tall. Velg 'Alle' for å vise beste priser uavhengig av deres absolutte prisnivå.",
"enable_min_periods_best": "Når aktivert vil filtrene gradvis bli lempeligere hvis det ikke blir funnet nok perioder. Dette forsøker å nå ønsket minimum antall perioder, noe som kan føre til at mindre optimale tidsrom blir markert som beste-pris-perioder.", "enable_min_periods_best": "Når aktivert vil filtrene gradvis bli lempeligere hvis det ikke blir funnet nok perioder. Dette forsøker å nå ønsket minimum antall perioder, noe som kan føre til at mindre optimale tidsrom blir markert som beste-pris-perioder.",
"min_periods_best": "Minimum antall beste-pris-perioder å sikte mot per dag. Filtre vil bli lempet trinn for trinn for å prøve å oppnå dette antallet. Kun aktiv når 'Prøv å oppnå minimum antall perioder' er aktivert. Standard: 1", "min_periods_best": "Minimum antall beste-pris-perioder å sikte mot per dag. Filtre vil bli lempet trinn for trinn for å prøve å oppnå dette antallet. Kun aktiv når 'Prøv å oppnå minimum antall perioder' er aktivert. Standard: 1",
@ -128,7 +126,6 @@
"peak_price_min_period_length": "Minimum periodelengde", "peak_price_min_period_length": "Minimum periodelengde",
"peak_price_flex": "Fleksibilitet: Maksimum % under maksimumspris (negativ verdi)", "peak_price_flex": "Fleksibilitet: Maksimum % under maksimumspris (negativ verdi)",
"peak_price_min_distance_from_avg": "Minimumsavstand: Påkrevd % over daglig gjennomsnitt", "peak_price_min_distance_from_avg": "Minimumsavstand: Påkrevd % over daglig gjennomsnitt",
"peak_price_min_volatility": "Minimum volatilitetsfilter",
"peak_price_min_level": "Prisnivåfilter (valgfritt)", "peak_price_min_level": "Prisnivåfilter (valgfritt)",
"peak_price_max_level_gap_count": "Gaptoleranse for nivåfilter", "peak_price_max_level_gap_count": "Gaptoleranse for nivåfilter",
"enable_min_periods_peak": "Prøv å oppnå minimum antall perioder", "enable_min_periods_peak": "Prøv å oppnå minimum antall perioder",
@ -136,7 +133,6 @@
"relaxation_step_peak": "Avslappingstrinn" "relaxation_step_peak": "Avslappingstrinn"
}, },
"data_description": { "data_description": {
"peak_price_min_volatility": "Vis kun topprisperioder når periodens interne prisvolatilitet (prisspennet innenfor perioden) oppfyller eller overskrider dette nivået. Standard: 'Lav' (vis perioder med hvilket som helst volatilitetsnivå) - gjør det mulig å identifisere dyre perioder selv om prisene er stabile. Velg 'Moderat'/'Høy' for kun å vise perioder med betydelige prisvariasjoner, noe som kan indikere mer presserende behov for å unngå disse tidspunktene.",
"peak_price_min_level": "Vis kun topprisperioder hvis de inneholder intervaller med prisnivåer ≥ valgt verdi. For eksempel: å velge 'Dyr' betyr at perioden må ha minst ett 'DYR' eller 'VELDIG_DYR' intervall. Dette sikrer at 'topppris'-perioder ikke bare er relativt dyre for dagen, men faktisk dyre i absolutte tall. Velg 'Alle' for å vise topppriser uavhengig av deres absolutte prisnivå.", "peak_price_min_level": "Vis kun topprisperioder hvis de inneholder intervaller med prisnivåer ≥ valgt verdi. For eksempel: å velge 'Dyr' betyr at perioden må ha minst ett 'DYR' eller 'VELDIG_DYR' intervall. Dette sikrer at 'topppris'-perioder ikke bare er relativt dyre for dagen, men faktisk dyre i absolutte tall. Velg 'Alle' for å vise topppriser uavhengig av deres absolutte prisnivå.",
"enable_min_periods_peak": "Når aktivert vil filtrene gradvis bli lempeligere hvis det ikke blir funnet nok perioder. Dette forsøker å nå ønsket minimum antall perioder for å sikre at du blir advart om dyre perioder selv på dager med uvanlige prismønstre.", "enable_min_periods_peak": "Når aktivert vil filtrene gradvis bli lempeligere hvis det ikke blir funnet nok perioder. Dette forsøker å nå ønsket minimum antall perioder for å sikre at du blir advart om dyre perioder selv på dager med uvanlige prismønstre.",
"min_periods_peak": "Minimum antall topp-pris-perioder å sikte mot per dag. Filtre vil bli lempet trinn for trinn for å prøve å oppnå dette antallet. Kun aktiv når 'Prøv å oppnå minimum antall perioder' er aktivert. Standard: 1", "min_periods_peak": "Minimum antall topp-pris-perioder å sikte mot per dag. Filtre vil bli lempet trinn for trinn for å prøve å oppnå dette antallet. Kun aktiv når 'Prøv å oppnå minimum antall perioder' er aktivert. Standard: 1",
@ -515,4 +511,4 @@
} }
}, },
"title": "Tibber Prisinformasjon & Vurderinger" "title": "Tibber Prisinformasjon & Vurderinger"
} }

View file

@ -104,7 +104,6 @@
"best_price_min_period_length": "Minimale periode lengte", "best_price_min_period_length": "Minimale periode lengte",
"best_price_flex": "Flexibiliteit: Maximaal % boven minimumprijs", "best_price_flex": "Flexibiliteit: Maximaal % boven minimumprijs",
"best_price_min_distance_from_avg": "Minimale afstand: Vereist % onder dagelijks gemiddelde", "best_price_min_distance_from_avg": "Minimale afstand: Vereist % onder dagelijks gemiddelde",
"best_price_min_volatility": "Minimum volatiliteitsfilter",
"best_price_max_level": "Prijsniveaufilter (Optioneel)", "best_price_max_level": "Prijsniveaufilter (Optioneel)",
"best_price_max_level_gap_count": "Gaptolerantie voor niveaufilter", "best_price_max_level_gap_count": "Gaptolerantie voor niveaufilter",
"enable_min_periods_best": "Probeer minimum aantal periodes te bereiken", "enable_min_periods_best": "Probeer minimum aantal periodes te bereiken",
@ -112,7 +111,6 @@
"relaxation_step_best": "Ontspanningsstap" "relaxation_step_best": "Ontspanningsstap"
}, },
"data_description": { "data_description": {
"best_price_min_volatility": "Toon alleen beste prijsperiodes wanneer de interne prijsvolatiliteit van de periode (prijsspanne binnen de periode) dit niveau bereikt of overschrijdt. Standaard: 'Laag' (toon periodes met elk volatiliteitsniveau) - maakt het mogelijk om goedkope periodes te vinden zelfs als de prijzen stabiel zijn. Selecteer 'Matig'/'Hoog' om alleen periodes met significante prijsvariaties te tonen, wat kan wijzen op meer dynamische prijsmogelijkheden.",
"best_price_max_level": "Toon alleen beste prijsperiodes als ze intervallen bevatten met prijsniveaus ≤ geselecteerde waarde. Bijvoorbeeld: selecteren van 'Goedkoop' betekent dat de periode minstens één 'ZEER_GOEDKOOP' of 'GOEDKOOP' interval moet hebben. Dit zorgt ervoor dat 'beste prijs'-periodes niet alleen relatief goedkoop zijn voor de dag, maar daadwerkelijk goedkoop in absolute termen. Selecteer 'Alle' om beste prijzen te tonen ongeacht hun absolute prijsniveau.", "best_price_max_level": "Toon alleen beste prijsperiodes als ze intervallen bevatten met prijsniveaus ≤ geselecteerde waarde. Bijvoorbeeld: selecteren van 'Goedkoop' betekent dat de periode minstens één 'ZEER_GOEDKOOP' of 'GOEDKOOP' interval moet hebben. Dit zorgt ervoor dat 'beste prijs'-periodes niet alleen relatief goedkoop zijn voor de dag, maar daadwerkelijk goedkoop in absolute termen. Selecteer 'Alle' om beste prijzen te tonen ongeacht hun absolute prijsniveau.",
"enable_min_periods_best": "Wanneer ingeschakeld worden filters geleidelijk versoepeld als er niet genoeg periodes worden gevonden. Dit probeert het gewenste minimum aantal periodes te bereiken om ervoor te zorgen dat je kansen hebt om van lage prijzen te profiteren, zelfs op dagen met ongebruikelijke prijspatronen.", "enable_min_periods_best": "Wanneer ingeschakeld worden filters geleidelijk versoepeld als er niet genoeg periodes worden gevonden. Dit probeert het gewenste minimum aantal periodes te bereiken om ervoor te zorgen dat je kansen hebt om van lage prijzen te profiteren, zelfs op dagen met ongebruikelijke prijspatronen.",
"min_periods_best": "Minimum aantal beste prijsperiodes om naar te streven per dag. Filters worden stap voor stap versoepeld om dit aantal te proberen bereiken. Alleen actief wanneer 'Probeer minimum aantal periodes te bereiken' is ingeschakeld. Standaard: 1", "min_periods_best": "Minimum aantal beste prijsperiodes om naar te streven per dag. Filters worden stap voor stap versoepeld om dit aantal te proberen bereiken. Alleen actief wanneer 'Probeer minimum aantal periodes te bereiken' is ingeschakeld. Standaard: 1",
@ -128,7 +126,6 @@
"peak_price_min_period_length": "Minimale periode lengte", "peak_price_min_period_length": "Minimale periode lengte",
"peak_price_flex": "Flexibiliteit: Maximaal % onder maximumprijs (negatieve waarde)", "peak_price_flex": "Flexibiliteit: Maximaal % onder maximumprijs (negatieve waarde)",
"peak_price_min_distance_from_avg": "Minimale afstand: Vereist % boven dagelijks gemiddelde", "peak_price_min_distance_from_avg": "Minimale afstand: Vereist % boven dagelijks gemiddelde",
"peak_price_min_volatility": "Minimum volatiliteitsfilter",
"peak_price_min_level": "Prijsniveaufilter (Optioneel)", "peak_price_min_level": "Prijsniveaufilter (Optioneel)",
"peak_price_max_level_gap_count": "Gaptolerantie voor niveaufilter", "peak_price_max_level_gap_count": "Gaptolerantie voor niveaufilter",
"enable_min_periods_peak": "Probeer minimum aantal periodes te bereiken", "enable_min_periods_peak": "Probeer minimum aantal periodes te bereiken",
@ -136,7 +133,6 @@
"relaxation_step_peak": "Ontspanningsstap" "relaxation_step_peak": "Ontspanningsstap"
}, },
"data_description": { "data_description": {
"peak_price_min_volatility": "Toon alleen piekprijsperiodes wanneer de interne prijsvolatiliteit van de periode (prijsspanne binnen de periode) dit niveau bereikt of overschrijdt. Standaard: 'Laag' (toon periodes met elk volatiliteitsniveau) - maakt het mogelijk om dure periodes te identificeren zelfs als de prijzen stabiel zijn. Selecteer 'Matig'/'Hoog' om alleen periodes met significante prijsvariaties te tonen, wat kan wijzen op een urgenter noodzaak om deze tijden te vermijden.",
"peak_price_min_level": "Toon alleen piekprijsperiodes als ze intervallen bevatten met prijsniveaus ≥ geselecteerde waarde. Bijvoorbeeld: selecteren van 'Duur' betekent dat de periode minstens één 'DUUR' of 'ZEER_DUUR' interval moet hebben. Dit zorgt ervoor dat 'piekprijs'-periodes niet alleen relatief duur zijn voor de dag, maar daadwerkelijk duur in absolute termen. Selecteer 'Alle' om piekprijzen te tonen ongeacht hun absolute prijsniveau.", "peak_price_min_level": "Toon alleen piekprijsperiodes als ze intervallen bevatten met prijsniveaus ≥ geselecteerde waarde. Bijvoorbeeld: selecteren van 'Duur' betekent dat de periode minstens één 'DUUR' of 'ZEER_DUUR' interval moet hebben. Dit zorgt ervoor dat 'piekprijs'-periodes niet alleen relatief duur zijn voor de dag, maar daadwerkelijk duur in absolute termen. Selecteer 'Alle' om piekprijzen te tonen ongeacht hun absolute prijsniveau.",
"enable_min_periods_peak": "Wanneer ingeschakeld worden filters geleidelijk versoepeld als er niet genoeg periodes worden gevonden. Dit probeert het gewenste minimum aantal periodes te bereiken om ervoor te zorgen dat je wordt gewaarschuwd voor dure periodes, zelfs op dagen met ongebruikelijke prijspatronen.", "enable_min_periods_peak": "Wanneer ingeschakeld worden filters geleidelijk versoepeld als er niet genoeg periodes worden gevonden. Dit probeert het gewenste minimum aantal periodes te bereiken om ervoor te zorgen dat je wordt gewaarschuwd voor dure periodes, zelfs op dagen met ongebruikelijke prijspatronen.",
"min_periods_peak": "Minimum aantal piekprijsperiodes om naar te streven per dag. Filters worden stap voor stap versoepeld om dit aantal te proberen bereiken. Alleen actief wanneer 'Probeer minimum aantal periodes te bereiken' is ingeschakeld. Standaard: 1", "min_periods_peak": "Minimum aantal piekprijsperiodes om naar te streven per dag. Filters worden stap voor stap versoepeld om dit aantal te proberen bereiken. Alleen actief wanneer 'Probeer minimum aantal periodes te bereiken' is ingeschakeld. Standaard: 1",
@ -515,4 +511,4 @@
} }
}, },
"title": "Tibber Prijsinformatie & Beoordelingen" "title": "Tibber Prijsinformatie & Beoordelingen"
} }

View file

@ -104,7 +104,6 @@
"best_price_min_period_length": "Minsta periodlängd", "best_price_min_period_length": "Minsta periodlängd",
"best_price_flex": "Flexibilitet: Maximalt % över minimumpris", "best_price_flex": "Flexibilitet: Maximalt % över minimumpris",
"best_price_min_distance_from_avg": "Minimiavstånd: Krävd % under dagligt genomsnitt", "best_price_min_distance_from_avg": "Minimiavstånd: Krävd % under dagligt genomsnitt",
"best_price_min_volatility": "Minimum volatilitetsfilter",
"best_price_max_level": "Prisnivåfilter (Valfritt)", "best_price_max_level": "Prisnivåfilter (Valfritt)",
"best_price_max_level_gap_count": "Gaptolerens för nivåfilter", "best_price_max_level_gap_count": "Gaptolerens för nivåfilter",
"enable_min_periods_best": "Försök uppnå minsta antal perioder", "enable_min_periods_best": "Försök uppnå minsta antal perioder",
@ -112,7 +111,6 @@
"relaxation_step_best": "Avslappningssteg" "relaxation_step_best": "Avslappningssteg"
}, },
"data_description": { "data_description": {
"best_price_min_volatility": "Visa endast bästa prisperioder när periodens interna prisvolatilitet (prisspann inom perioden) uppfyller eller överskrider denna nivå. Standard: 'Låg' (visa perioder med valfri volatilitetsnivå) - möjliggör att hitta billiga perioder även om priserna är stabila. Välj 'Måttlig'/'Hög' för att endast visa perioder med betydande prisvariationer, vilket kan indikera mer dynamiska prismöjligheter.",
"best_price_max_level": "Visa endast bästa prisperioder om de innehåller intervall med prisnivåer ≤ valt värde. Till exempel: att välja 'Billigt' betyder att perioden måste ha minst ett 'MYCKET_BILLIGT' eller 'BILLIGT' intervall. Detta säkerställer att 'bästa pris'-perioder inte bara är relativt billiga för dagen, utan faktiskt billiga i absoluta tal. Välj 'Alla' för att visa bästa priser oavsett deras absoluta prisnivå.", "best_price_max_level": "Visa endast bästa prisperioder om de innehåller intervall med prisnivåer ≤ valt värde. Till exempel: att välja 'Billigt' betyder att perioden måste ha minst ett 'MYCKET_BILLIGT' eller 'BILLIGT' intervall. Detta säkerställer att 'bästa pris'-perioder inte bara är relativt billiga för dagen, utan faktiskt billiga i absoluta tal. Välj 'Alla' för att visa bästa priser oavsett deras absoluta prisnivå.",
"enable_min_periods_best": "När aktiverad kommer filtren att gradvis luckras upp om inte tillräckligt många perioder hittas. Detta försöker uppnå det önskade minsta antalet perioder för att säkerställa att du har möjligheter att dra nytta av låga priser även på dagar med ovanliga prismönster.", "enable_min_periods_best": "När aktiverad kommer filtren att gradvis luckras upp om inte tillräckligt många perioder hittas. Detta försöker uppnå det önskade minsta antalet perioder för att säkerställa att du har möjligheter att dra nytta av låga priser även på dagar med ovanliga prismönster.",
"min_periods_best": "Minsta antal bästa prisperioder att sträva efter per dag. Filtren kommer att luckras upp steg för steg för att försöka uppnå detta antal. Endast aktiv när 'Försök uppnå minsta antal perioder' är aktiverad. Standard: 1", "min_periods_best": "Minsta antal bästa prisperioder att sträva efter per dag. Filtren kommer att luckras upp steg för steg för att försöka uppnå detta antal. Endast aktiv när 'Försök uppnå minsta antal perioder' är aktiverad. Standard: 1",
@ -128,7 +126,6 @@
"peak_price_min_period_length": "Minsta periodlängd", "peak_price_min_period_length": "Minsta periodlängd",
"peak_price_flex": "Flexibilitet: Maximalt % under maximumpris (negativt värde)", "peak_price_flex": "Flexibilitet: Maximalt % under maximumpris (negativt värde)",
"peak_price_min_distance_from_avg": "Minimiavstånd: Krävd % över dagligt genomsnitt", "peak_price_min_distance_from_avg": "Minimiavstånd: Krävd % över dagligt genomsnitt",
"peak_price_min_volatility": "Minimum volatilitetsfilter",
"peak_price_min_level": "Prisnivåfilter (Valfritt)", "peak_price_min_level": "Prisnivåfilter (Valfritt)",
"peak_price_max_level_gap_count": "Gaptolerens för nivåfilter", "peak_price_max_level_gap_count": "Gaptolerens för nivåfilter",
"enable_min_periods_peak": "Försök uppnå minsta antal perioder", "enable_min_periods_peak": "Försök uppnå minsta antal perioder",
@ -136,7 +133,6 @@
"relaxation_step_peak": "Avslappningssteg" "relaxation_step_peak": "Avslappningssteg"
}, },
"data_description": { "data_description": {
"peak_price_min_volatility": "Visa endast topprisperioder om de har en intern prisvolatilitet (prisspann inom perioden) som uppfyller eller överskrider denna nivå. Standard: 'Låg' (visa oavsett periodens volatilitet) - toppvarningar är relevanta även vid låg intern spridning. Högre volatilitet inom en period kan indikera mer brådskande behov att undvika dessa tider (eftersom priserna varierar kraftigt även inom den korta perioden).",
"peak_price_min_level": "Visa endast topprisperioder om de innehåller intervall med prisnivåer ≥ valt värde. Till exempel måste perioden om du väljer 'Dyr' ha minst ett 'DYR' eller 'MYCKET_DYR' intervall. Detta säkerställer att 'toppris'-perioder inte bara är relativt dyra för dagen, utan faktiskt dyra i absoluta termer (inte bara 'lite dyrare än genomsnittet på en billig dag').", "peak_price_min_level": "Visa endast topprisperioder om de innehåller intervall med prisnivåer ≥ valt värde. Till exempel måste perioden om du väljer 'Dyr' ha minst ett 'DYR' eller 'MYCKET_DYR' intervall. Detta säkerställer att 'toppris'-perioder inte bara är relativt dyra för dagen, utan faktiskt dyra i absoluta termer (inte bara 'lite dyrare än genomsnittet på en billig dag').",
"enable_min_periods_peak": "När aktiverad kommer filtren att gradvis luckras upp om inte tillräckligt många perioder hittas. Detta försöker uppnå det önskade minsta antalet perioder för att säkerställa att du blir varnad för dyra perioder även på dagar med ovanliga prismönster.", "enable_min_periods_peak": "När aktiverad kommer filtren att gradvis luckras upp om inte tillräckligt många perioder hittas. Detta försöker uppnå det önskade minsta antalet perioder för att säkerställa att du blir varnad för dyra perioder även på dagar med ovanliga prismönster.",
"min_periods_peak": "Minsta antal topprisperioder att sträva efter per dag. Filtren kommer att luckras upp steg för steg för att försöka uppnå detta antal. Endast aktiv när 'Försök uppnå minsta antal perioder' är aktiverad. Standard: 1", "min_periods_peak": "Minsta antal topprisperioder att sträva efter per dag. Filtren kommer att luckras upp steg för steg för att försöka uppnå detta antal. Endast aktiv när 'Försök uppnå minsta antal perioder' är aktiverad. Standard: 1",
@ -515,4 +511,4 @@
} }
}, },
"title": "Tibber Prisinformation & Betyg" "title": "Tibber Prisinformation & Betyg"
} }

File diff suppressed because it is too large Load diff