diff --git a/custom_components/tibber_prices/binary_sensor.py b/custom_components/tibber_prices/binary_sensor.py index cfa13bd..67861c1 100644 --- a/custom_components/tibber_prices/binary_sensor.py +++ b/custom_components/tibber_prices/binary_sensor.py @@ -30,7 +30,6 @@ from .const import ( CONF_BEST_PRICE_MAX_LEVEL, CONF_BEST_PRICE_MIN_VOLATILITY, CONF_EXTENDED_DESCRIPTIONS, - CONF_MIN_VOLATILITY_FOR_PERIODS, CONF_PEAK_PRICE_MIN_LEVEL, CONF_PEAK_PRICE_MIN_VOLATILITY, CONF_VOLATILITY_THRESHOLD_HIGH, @@ -39,7 +38,6 @@ from .const import ( DEFAULT_BEST_PRICE_MAX_LEVEL, DEFAULT_BEST_PRICE_MIN_VOLATILITY, DEFAULT_EXTENDED_DESCRIPTIONS, - DEFAULT_MIN_VOLATILITY_FOR_PERIODS, DEFAULT_PEAK_PRICE_MIN_LEVEL, DEFAULT_PEAK_PRICE_MIN_VOLATILITY, DEFAULT_VOLATILITY_THRESHOLD_HIGH, @@ -47,7 +45,6 @@ from .const import ( DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH, PRICE_LEVEL_MAPPING, VOLATILITY_HIGH, - VOLATILITY_LOW, VOLATILITY_MODERATE, VOLATILITY_VERY_HIGH, async_get_entity_description, @@ -420,29 +417,21 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity): # Peak price sensor min_volatility = self.coordinator.config_entry.options.get( CONF_PEAK_PRICE_MIN_VOLATILITY, - # Migration: fall back to old global filter, then to new default - self.coordinator.config_entry.options.get( - CONF_MIN_VOLATILITY_FOR_PERIODS, - DEFAULT_PEAK_PRICE_MIN_VOLATILITY, - ), + DEFAULT_PEAK_PRICE_MIN_VOLATILITY, ) else: # Best price sensor min_volatility = self.coordinator.config_entry.options.get( CONF_BEST_PRICE_MIN_VOLATILITY, - # Migration: fall back to old global filter, then to new default - self.coordinator.config_entry.options.get( - CONF_MIN_VOLATILITY_FOR_PERIODS, - DEFAULT_BEST_PRICE_MIN_VOLATILITY, - ), + DEFAULT_BEST_PRICE_MIN_VOLATILITY, ) - # "LOW" means no filtering (show at any volatility ≥0ct) - if min_volatility == VOLATILITY_LOW: + # "low" means no filtering (show at any volatility ≥0ct) + if min_volatility == "low": return True - # Legacy migration: "ANY" also means no filtering - if min_volatility == "ANY": + # "any" is legacy alias for "low" (no filtering) + if min_volatility == "any": return True # Get today's price data to calculate volatility @@ -506,8 +495,8 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity): DEFAULT_BEST_PRICE_MAX_LEVEL, ) - # "ANY" means no level filtering - if level_config == "ANY": + # "any" means no level filtering + if level_config == "any": return True # Get today's intervals @@ -518,7 +507,8 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity): return True # If no data, don't filter # Check if ANY interval today meets the level requirement - level_order = PRICE_LEVEL_MAPPING.get(level_config, 0) + # Note: level_config is lowercase from selector, but PRICE_LEVEL_MAPPING uses uppercase + level_order = PRICE_LEVEL_MAPPING.get(level_config.upper(), 0) if reverse_sort: # Peak price: level >= min_level (show if ANY interval is expensive enough) @@ -563,10 +553,17 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity): A dict with empty periods and a reason attribute explaining why. """ - min_volatility = self.coordinator.config_entry.options.get( - CONF_MIN_VOLATILITY_FOR_PERIODS, - DEFAULT_MIN_VOLATILITY_FOR_PERIODS, - ) + # Get appropriate volatility config based on sensor type + if reverse_sort: + min_volatility = self.coordinator.config_entry.options.get( + CONF_PEAK_PRICE_MIN_VOLATILITY, + DEFAULT_PEAK_PRICE_MIN_VOLATILITY, + ) + else: + min_volatility = self.coordinator.config_entry.options.get( + CONF_BEST_PRICE_MIN_VOLATILITY, + DEFAULT_BEST_PRICE_MIN_VOLATILITY, + ) # Get appropriate level config based on sensor type if reverse_sort: @@ -584,10 +581,10 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity): # Build reason string explaining which filter(s) prevented display reasons = [] - if min_volatility != "ANY" and not self._check_volatility_filter(reverse_sort=reverse_sort): - reasons.append(f"volatility_below_{min_volatility.lower()}") - if level_config != "ANY" and not self._check_level_filter(reverse_sort=reverse_sort): - reasons.append(f"level_{level_filter_type}_{level_config.lower()}") + if min_volatility != "any" and not self._check_volatility_filter(reverse_sort=reverse_sort): + reasons.append(f"volatility_below_{min_volatility}") + if level_config != "any" and not self._check_level_filter(reverse_sort=reverse_sort): + reasons.append(f"level_{level_filter_type}_{level_config}") # Join multiple reasons with "and" reason = "_and_".join(reasons) if reasons else "filtered" diff --git a/custom_components/tibber_prices/const.py b/custom_components/tibber_prices/const.py index 3027043..626037f 100644 --- a/custom_components/tibber_prices/const.py +++ b/custom_components/tibber_prices/const.py @@ -33,7 +33,6 @@ CONF_VOLATILITY_THRESHOLD_HIGH = "volatility_threshold_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_MIN_VOLATILITY_FOR_PERIODS = "min_volatility_for_periods" # Deprecated: Use CONF_BEST_PRICE_MIN_VOLATILITY CONF_BEST_PRICE_MAX_LEVEL = "best_price_max_level" CONF_PEAK_PRICE_MIN_LEVEL = "peak_price_min_level" @@ -55,11 +54,10 @@ 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_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_BEST_PRICE_MIN_VOLATILITY = "LOW" # Show best price at any volatility (optimization always useful) -DEFAULT_PEAK_PRICE_MIN_VOLATILITY = "LOW" # Always show peak price (warning relevant even at low spreads) -DEFAULT_MIN_VOLATILITY_FOR_PERIODS = "LOW" # Deprecated: Use DEFAULT_BEST_PRICE_MIN_VOLATILITY -DEFAULT_BEST_PRICE_MAX_LEVEL = "ANY" # Default: show best price periods regardless of price level -DEFAULT_PEAK_PRICE_MIN_LEVEL = "ANY" # Default: show peak price periods regardless of price level +DEFAULT_BEST_PRICE_MIN_VOLATILITY = "low" # Show best price at any volatility (optimization always useful) +DEFAULT_PEAK_PRICE_MIN_VOLATILITY = "low" # Always show peak price (warning relevant even at low spreads) +DEFAULT_BEST_PRICE_MAX_LEVEL = "any" # Default: show best price periods regardless of price level +DEFAULT_PEAK_PRICE_MIN_LEVEL = "any" # Default: show peak price periods regardless of price level # Home types HOME_TYPE_APARTMENT = "APARTMENT" @@ -227,35 +225,32 @@ VOLATILITY_OPTIONS = [ # Valid options for minimum volatility filter for periods MIN_VOLATILITY_FOR_PERIODS_OPTIONS = [ - VOLATILITY_LOW, # Show at any volatility (≥0ct spread) - no filter - VOLATILITY_MODERATE, # Only show periods when volatility ≥ MODERATE (≥5ct) - VOLATILITY_HIGH, # Only show periods when volatility ≥ HIGH (≥15ct) - VOLATILITY_VERY_HIGH, # Only show periods when volatility ≥ VERY_HIGH (≥30ct) + 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" BEST_PRICE_MAX_LEVEL_OPTIONS = [ - "ANY", # No filter, allow all price levels - PRICE_LEVEL_VERY_CHEAP, # Only show if level ≤ VERY_CHEAP - PRICE_LEVEL_CHEAP, # Only show if level ≤ CHEAP - PRICE_LEVEL_NORMAL, # Only show if level ≤ NORMAL - PRICE_LEVEL_EXPENSIVE, # Only show if level ≤ EXPENSIVE + "any", # No filter, allow all price levels + PRICE_LEVEL_VERY_CHEAP.lower(), # Only show if level ≤ VERY_CHEAP + PRICE_LEVEL_CHEAP.lower(), # Only show if level ≤ CHEAP + PRICE_LEVEL_NORMAL.lower(), # Only show if level ≤ NORMAL + PRICE_LEVEL_EXPENSIVE.lower(), # Only show if level ≤ EXPENSIVE ] # Valid options for peak price minimum level filter (AND-linked with volatility filter) # Sorted from expensive to cheap: user selects "starting from how expensive" PEAK_PRICE_MIN_LEVEL_OPTIONS = [ - "ANY", # No filter, allow all price levels - PRICE_LEVEL_EXPENSIVE, # Only show if level ≥ EXPENSIVE - PRICE_LEVEL_NORMAL, # Only show if level ≥ NORMAL - PRICE_LEVEL_CHEAP, # Only show if level ≥ CHEAP - PRICE_LEVEL_VERY_CHEAP, # Only show if level ≥ VERY_CHEAP + "any", # No filter, allow all price levels + PRICE_LEVEL_EXPENSIVE.lower(), # Only show if level ≥ EXPENSIVE + PRICE_LEVEL_NORMAL.lower(), # Only show if level ≥ NORMAL + PRICE_LEVEL_CHEAP.lower(), # Only show if level ≥ CHEAP + PRICE_LEVEL_VERY_CHEAP.lower(), # Only show if level ≥ VERY_CHEAP ] -# Deprecated: Use BEST_PRICE_MAX_LEVEL_OPTIONS or PEAK_PRICE_MIN_LEVEL_OPTIONS instead -MAX_LEVEL_FOR_PERIODS_OPTIONS = BEST_PRICE_MAX_LEVEL_OPTIONS - # Mapping for comparing price levels (used for sorting) PRICE_LEVEL_MAPPING = { PRICE_LEVEL_VERY_CHEAP: -2, diff --git a/custom_components/tibber_prices/translations/de.json b/custom_components/tibber_prices/translations/de.json index e315a95..a0bc4a2 100644 --- a/custom_components/tibber_prices/translations/de.json +++ b/custom_components/tibber_prices/translations/de.json @@ -479,20 +479,20 @@ "selector": { "volatility": { "options": { - "LOW": "Niedrig", - "MODERATE": "Moderat", - "HIGH": "Hoch", - "VERY_HIGH": "Sehr hoch" + "low": "Niedrig", + "moderate": "Moderat", + "high": "Hoch", + "very_high": "Sehr hoch" } }, "price_level": { "options": { - "ANY": "Beliebig", - "VERY_CHEAP": "Sehr günstig", - "CHEAP": "Günstig", - "NORMAL": "Normal", - "EXPENSIVE": "Teuer", - "VERY_EXPENSIVE": "Sehr teuer" + "any": "Beliebig", + "very_cheap": "Sehr günstig", + "cheap": "Günstig", + "normal": "Normal", + "expensive": "Teuer", + "very_expensive": "Sehr teuer" } } }, diff --git a/custom_components/tibber_prices/translations/en.json b/custom_components/tibber_prices/translations/en.json index 8e705d6..289d27a 100644 --- a/custom_components/tibber_prices/translations/en.json +++ b/custom_components/tibber_prices/translations/en.json @@ -475,20 +475,20 @@ "selector": { "volatility": { "options": { - "LOW": "Low", - "MODERATE": "Moderate", - "HIGH": "High", - "VERY_HIGH": "Very high" + "low": "Low", + "moderate": "Moderate", + "high": "High", + "very_high": "Very high" } }, "price_level": { "options": { - "ANY": "Any", - "VERY_CHEAP": "Very cheap", - "CHEAP": "Cheap", - "NORMAL": "Normal", - "EXPENSIVE": "Expensive", - "VERY_EXPENSIVE": "Very expensive" + "any": "Any", + "very_cheap": "Very cheap", + "cheap": "Cheap", + "normal": "Normal", + "expensive": "Expensive", + "very_expensive": "Very expensive" } } }, diff --git a/custom_components/tibber_prices/translations/nb.json b/custom_components/tibber_prices/translations/nb.json index 41d262e..fe3ee86 100644 --- a/custom_components/tibber_prices/translations/nb.json +++ b/custom_components/tibber_prices/translations/nb.json @@ -475,20 +475,20 @@ "selector": { "volatility": { "options": { - "LOW": "Lav", - "MODERATE": "Moderat", - "HIGH": "Høy", - "VERY_HIGH": "Svært høy" + "low": "Lav", + "moderate": "Moderat", + "high": "Høy", + "very_high": "Svært høy" } }, "price_level": { "options": { - "ANY": "Alle", - "VERY_CHEAP": "Svært billig", - "CHEAP": "Billig", - "NORMAL": "Normal", - "EXPENSIVE": "Dyr", - "VERY_EXPENSIVE": "Svært dyr" + "any": "Alle", + "very_cheap": "Svært billig", + "cheap": "Billig", + "normal": "Normal", + "expensive": "Dyr", + "very_expensive": "Svært dyr" } } }, diff --git a/custom_components/tibber_prices/translations/nl.json b/custom_components/tibber_prices/translations/nl.json index 3c620b4..04e28f5 100644 --- a/custom_components/tibber_prices/translations/nl.json +++ b/custom_components/tibber_prices/translations/nl.json @@ -475,20 +475,20 @@ "selector": { "volatility": { "options": { - "LOW": "Laag", - "MODERATE": "Matig", - "HIGH": "Hoog", - "VERY_HIGH": "Zeer hoog" + "low": "Laag", + "moderate": "Matig", + "high": "Hoog", + "very_high": "Zeer hoog" } }, "price_level": { "options": { - "ANY": "Alle", - "VERY_CHEAP": "Zeer goedkoop", - "CHEAP": "Goedkoop", - "NORMAL": "Normaal", - "EXPENSIVE": "Duur", - "VERY_EXPENSIVE": "Zeer duur" + "any": "Alle", + "very_cheap": "Zeer goedkoop", + "cheap": "Goedkoop", + "normal": "Normaal", + "expensive": "Duur", + "very_expensive": "Zeer duur" } } }, diff --git a/custom_components/tibber_prices/translations/sv.json b/custom_components/tibber_prices/translations/sv.json index c958ac3..f44e8eb 100644 --- a/custom_components/tibber_prices/translations/sv.json +++ b/custom_components/tibber_prices/translations/sv.json @@ -475,20 +475,20 @@ "selector": { "volatility": { "options": { - "LOW": "Låg", - "MODERATE": "Måttlig", - "HIGH": "Hög", - "VERY_HIGH": "Mycket hög" + "low": "Låg", + "moderate": "Måttlig", + "high": "Hög", + "very_high": "Mycket hög" } }, "price_level": { "options": { - "ANY": "Alla", - "VERY_CHEAP": "Mycket billigt", - "CHEAP": "Billigt", - "NORMAL": "Normalt", - "EXPENSIVE": "Dyrt", - "VERY_EXPENSIVE": "Mycket dyrt" + "any": "Alla", + "very_cheap": "Mycket billigt", + "cheap": "Billigt", + "normal": "Normalt", + "expensive": "Dyrt", + "very_expensive": "Mycket dyrt" } } },