feat: Add minimum period length configuration for best and peak price sensors

This commit is contained in:
Julian Pawlowski 2025-11-07 15:16:16 +00:00
parent f4ae8422f2
commit 1ed2c08f34
8 changed files with 89 additions and 0 deletions

View file

@ -29,14 +29,18 @@ if TYPE_CHECKING:
from .const import ( from .const import (
CONF_BEST_PRICE_FLEX, CONF_BEST_PRICE_FLEX,
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
CONF_EXTENDED_DESCRIPTIONS, CONF_EXTENDED_DESCRIPTIONS,
CONF_PEAK_PRICE_FLEX, CONF_PEAK_PRICE_FLEX,
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_BEST_PRICE_FLEX, DEFAULT_BEST_PRICE_FLEX,
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_EXTENDED_DESCRIPTIONS, DEFAULT_EXTENDED_DESCRIPTIONS,
DEFAULT_PEAK_PRICE_FLEX, DEFAULT_PEAK_PRICE_FLEX,
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
async_get_entity_description, async_get_entity_description,
get_entity_description, get_entity_description,
) )
@ -501,6 +505,37 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity):
periods.append(current_period) periods.append(current_period)
return periods return periods
def _filter_periods_by_min_length(self, periods: list[list[dict]], *, reverse_sort: bool) -> list[list[dict]]:
"""
Filter periods to only include those meeting the minimum length requirement.
Args:
periods: List of periods (each period is a list of interval dicts)
reverse_sort: True for peak price, False for best price
Returns:
Filtered list of periods that meet minimum length requirement
"""
options = self.coordinator.config_entry.options
data = self.coordinator.config_entry.data
# Use appropriate config based on sensor type
if reverse_sort: # Peak price
conf_key = CONF_PEAK_PRICE_MIN_PERIOD_LENGTH
default = DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH
else: # Best price
conf_key = CONF_BEST_PRICE_MIN_PERIOD_LENGTH
default = DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH
min_period_length = options.get(conf_key, data.get(conf_key, default))
# Convert minutes to number of 15-minute intervals
min_intervals = min_period_length // MINUTES_PER_INTERVAL
# Filter out periods that are too short
return [period for period in periods if len(period) >= min_intervals]
def _add_interval_ends(self, periods: list[list[dict]]) -> None: def _add_interval_ends(self, periods: list[list[dict]]) -> None:
"""Add interval_end to each interval using per-interval interval_length.""" """Add interval_end to each interval using per-interval interval_length."""
for period in periods: for period in periods:
@ -580,6 +615,8 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity):
price_context, price_context,
reverse_sort=reverse_sort, reverse_sort=reverse_sort,
) )
# Filter periods by minimum length requirement
periods = self._filter_periods_by_min_length(periods, reverse_sort=reverse_sort)
self._add_interval_ends(periods) self._add_interval_ends(periods)
# Only use periods relevant for today/tomorrow for annotation and attribute calculation # Only use periods relevant for today/tomorrow for annotation and attribute calculation
filtered_periods = self._filter_periods_today_tomorrow(periods) filtered_periods = self._filter_periods_today_tomorrow(periods)

View file

@ -40,16 +40,20 @@ from .api import (
from .const import ( from .const import (
CONF_BEST_PRICE_FLEX, CONF_BEST_PRICE_FLEX,
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
CONF_EXTENDED_DESCRIPTIONS, CONF_EXTENDED_DESCRIPTIONS,
CONF_PEAK_PRICE_FLEX, CONF_PEAK_PRICE_FLEX,
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
CONF_PRICE_RATING_THRESHOLD_HIGH, CONF_PRICE_RATING_THRESHOLD_HIGH,
CONF_PRICE_RATING_THRESHOLD_LOW, CONF_PRICE_RATING_THRESHOLD_LOW,
DEFAULT_BEST_PRICE_FLEX, DEFAULT_BEST_PRICE_FLEX,
DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_EXTENDED_DESCRIPTIONS, DEFAULT_EXTENDED_DESCRIPTIONS,
DEFAULT_PEAK_PRICE_FLEX, DEFAULT_PEAK_PRICE_FLEX,
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG, DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG,
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_PRICE_RATING_THRESHOLD_HIGH, DEFAULT_PRICE_RATING_THRESHOLD_HIGH,
DEFAULT_PRICE_RATING_THRESHOLD_LOW, DEFAULT_PRICE_RATING_THRESHOLD_LOW,
DOMAIN, DOMAIN,
@ -526,6 +530,23 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
step_id="best_price", step_id="best_price",
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Optional(
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
default=int(
self.config_entry.options.get(
CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_BEST_PRICE_MIN_PERIOD_LENGTH,
)
),
): NumberSelector(
NumberSelectorConfig(
min=15,
max=240,
step=15,
unit_of_measurement="min",
mode=NumberSelectorMode.SLIDER,
),
),
vol.Optional( vol.Optional(
CONF_BEST_PRICE_FLEX, CONF_BEST_PRICE_FLEX,
default=int( default=int(
@ -572,6 +593,23 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
step_id="peak_price", step_id="peak_price",
data_schema=vol.Schema( data_schema=vol.Schema(
{ {
vol.Optional(
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
default=int(
self.config_entry.options.get(
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH,
DEFAULT_PEAK_PRICE_MIN_PERIOD_LENGTH,
)
),
): NumberSelector(
NumberSelectorConfig(
min=15,
max=240,
step=15,
unit_of_measurement="min",
mode=NumberSelectorMode.SLIDER,
),
),
vol.Optional( vol.Optional(
CONF_PEAK_PRICE_FLEX, CONF_PEAK_PRICE_FLEX,
default=int( default=int(

View file

@ -25,6 +25,8 @@ CONF_BEST_PRICE_FLEX = "best_price_flex"
CONF_PEAK_PRICE_FLEX = "peak_price_flex" CONF_PEAK_PRICE_FLEX = "peak_price_flex"
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG = "best_price_min_distance_from_avg" CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG = "best_price_min_distance_from_avg"
CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG = "peak_price_min_distance_from_avg" CONF_PEAK_PRICE_MIN_DISTANCE_FROM_AVG = "peak_price_min_distance_from_avg"
CONF_BEST_PRICE_MIN_PERIOD_LENGTH = "best_price_min_period_length"
CONF_PEAK_PRICE_MIN_PERIOD_LENGTH = "peak_price_min_period_length"
CONF_PRICE_RATING_THRESHOLD_LOW = "price_rating_threshold_low" CONF_PRICE_RATING_THRESHOLD_LOW = "price_rating_threshold_low"
CONF_PRICE_RATING_THRESHOLD_HIGH = "price_rating_threshold_high" CONF_PRICE_RATING_THRESHOLD_HIGH = "price_rating_threshold_high"
@ -37,6 +39,8 @@ DEFAULT_BEST_PRICE_FLEX = 15 # 15% flexibility for best price (user-facing, per
DEFAULT_PEAK_PRICE_FLEX = -15 # 15% flexibility for peak price (user-facing, percent) DEFAULT_PEAK_PRICE_FLEX = -15 # 15% 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 = 2 # 2% minimum distance from daily average for best price
DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG = 2 # 2% minimum distance from daily average for peak price DEFAULT_PEAK_PRICE_MIN_DISTANCE_FROM_AVG = 2 # 2% minimum distance from daily average for peak price
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_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

View file

@ -96,6 +96,7 @@
"title": "Bestpreis-Periode Einstellungen", "title": "Bestpreis-Periode Einstellungen",
"description": "Konfiguration für den Bestpreis-Periode Binärsensor. Dieser Sensor ist während der Zeiträume mit den niedrigsten Strompreisen aktiv.", "description": "Konfiguration für den Bestpreis-Periode Binärsensor. Dieser Sensor ist während der Zeiträume mit den niedrigsten Strompreisen aktiv.",
"data": { "data": {
"best_price_min_period_length": "Minimale Periodenlänge",
"best_price_flex": "Flexibilität: Maximale % über dem Mindestpreis", "best_price_flex": "Flexibilität: Maximale % über dem Mindestpreis",
"best_price_min_distance_from_avg": "Mindestabstand: Erforderliche % unter dem Tagesdurchschnitt" "best_price_min_distance_from_avg": "Mindestabstand: Erforderliche % unter dem Tagesdurchschnitt"
} }
@ -104,6 +105,7 @@
"title": "Spitzenpreis-Periode Einstellungen", "title": "Spitzenpreis-Periode Einstellungen",
"description": "Konfiguration für den Spitzenpreis-Periode Binärsensor. Dieser Sensor ist während der Zeiträume mit den höchsten Strompreisen aktiv.", "description": "Konfiguration für den Spitzenpreis-Periode Binärsensor. Dieser Sensor ist während der Zeiträume mit den höchsten Strompreisen aktiv.",
"data": { "data": {
"peak_price_min_period_length": "Minimale Periodenlänge",
"peak_price_flex": "Flexibilität: Maximale % unter dem Höchstpreis (negativer Wert)", "peak_price_flex": "Flexibilität: Maximale % unter dem Höchstpreis (negativer Wert)",
"peak_price_min_distance_from_avg": "Mindestabstand: Erforderliche % über dem Tagesdurchschnitt" "peak_price_min_distance_from_avg": "Mindestabstand: Erforderliche % über dem Tagesdurchschnitt"
} }

View file

@ -96,6 +96,7 @@
"title": "Best Price Period Settings", "title": "Best Price Period Settings",
"description": "Configure settings for the Best Price Period binary sensor. This sensor is active during periods with the lowest electricity prices.", "description": "Configure settings for the Best Price Period binary sensor. This sensor is active during periods with the lowest electricity prices.",
"data": { "data": {
"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"
} }
@ -104,6 +105,7 @@
"title": "Peak Price Period Settings", "title": "Peak Price Period Settings",
"description": "Configure settings for the Peak Price Period binary sensor. This sensor is active during periods with the highest electricity prices.", "description": "Configure settings for the Peak Price Period binary sensor. This sensor is active during periods with the highest electricity prices.",
"data": { "data": {
"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"
} }

View file

@ -96,6 +96,7 @@
"title": "Innstillinger for beste prisperiode", "title": "Innstillinger for beste prisperiode",
"description": "Konfigurer innstillinger for binærsensoren Beste prisperiode. Denne sensoren er aktiv i perioder med de laveste strømprisene.", "description": "Konfigurer innstillinger for binærsensoren Beste prisperiode. Denne sensoren er aktiv i perioder med de laveste strømprisene.",
"data": { "data": {
"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"
} }
@ -104,6 +105,7 @@
"title": "Innstillinger for topprisperiode", "title": "Innstillinger for topprisperiode",
"description": "Konfigurer innstillinger for binærsensoren Topprisperiode. Denne sensoren er aktiv i perioder med de høyeste strømprisene.", "description": "Konfigurer innstillinger for binærsensoren Topprisperiode. Denne sensoren er aktiv i perioder med de høyeste strømprisene.",
"data": { "data": {
"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"
} }

View file

@ -96,6 +96,7 @@
"title": "Instellingen beste prijsperiode", "title": "Instellingen beste prijsperiode",
"description": "Configureer instellingen voor de Beste Prijsperiode binaire sensor. Deze sensor is actief tijdens perioden met de laagste elektriciteitsprijzen.", "description": "Configureer instellingen voor de Beste Prijsperiode binaire sensor. Deze sensor is actief tijdens perioden met de laagste elektriciteitsprijzen.",
"data": { "data": {
"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"
} }
@ -104,6 +105,7 @@
"title": "Instellingen piekprijsperiode", "title": "Instellingen piekprijsperiode",
"description": "Configureer instellingen voor de Piekprijsperiode binaire sensor. Deze sensor is actief tijdens perioden met de hoogste elektriciteitsprijzen.", "description": "Configureer instellingen voor de Piekprijsperiode binaire sensor. Deze sensor is actief tijdens perioden met de hoogste elektriciteitsprijzen.",
"data": { "data": {
"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"
} }

View file

@ -96,6 +96,7 @@
"title": "Inställningar för bästa prisperiod", "title": "Inställningar för bästa prisperiod",
"description": "Konfigurera inställningar för Bästa Prisperiod binärsensor. Denna sensor är aktiv under perioder med de lägsta elpriserna.", "description": "Konfigurera inställningar för Bästa Prisperiod binärsensor. Denna sensor är aktiv under perioder med de lägsta elpriserna.",
"data": { "data": {
"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"
} }
@ -104,6 +105,7 @@
"title": "Inställningar för topprisperiod", "title": "Inställningar för topprisperiod",
"description": "Konfigurera inställningar för Topprisperiod binärsensor. Denna sensor är aktiv under perioder med de högsta elpriserna.", "description": "Konfigurera inställningar för Topprisperiod binärsensor. Denna sensor är aktiv under perioder med de högsta elpriserna.",
"data": { "data": {
"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"
} }