Skip to content

Commit fe54ba0

Browse files
committed
fix: Fixed issue where started target timeframes are calculated and it starts in the past (2 hours dev time)
1 parent 6627bb8 commit fe54ba0

10 files changed

+547
-285
lines changed

custom_components/target_timeframes/entities/__init__.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ def apply_offset(date_time: datetime, offset: str, inverse = False):
3333

3434
return date_time + timedelta(hours=hours, minutes=minutes, seconds=seconds)
3535

36-
def is_target_timeframe_complete_in_period(current_date: datetime, applicable_time_periods: list | None, target_timeframes: list | None):
37-
if applicable_time_periods is None or target_timeframes is None or len(applicable_time_periods) < 1 or len(target_timeframes) < 1:
36+
def is_target_timeframe_complete_in_period(current_date: datetime, start_time: datetime, end_time: datetime, target_timeframes: list | None):
37+
if target_timeframes is None or len(target_timeframes) < 1:
3838
return False
3939

4040
return (
41-
applicable_time_periods[0]["start"] <= target_timeframes[0]["start"] and
42-
applicable_time_periods[-1]["end"] >= target_timeframes[-1]["end"] and
41+
start_time <= target_timeframes[0]["start"] and
42+
end_time >= target_timeframes[-1]["end"] and
4343
target_timeframes[-1]["end"] <= current_date
4444
)
4545

46-
def get_fixed_applicable_time_periods(current_date: datetime, target_start_time: str, target_end_time: str, time_period_values: list, is_rolling_target = True):
46+
def get_start_and_end_times(current_date: datetime, target_start_time: str, target_end_time: str, start_time_not_in_past = True):
4747
if (target_start_time is not None):
4848
target_start = parse_datetime(current_date.strftime(f"%Y-%m-%dT{target_start_time}:00%z"))
4949
else:
@@ -65,7 +65,7 @@ def get_fixed_applicable_time_periods(current_date: datetime, target_start_time:
6565
target_end = target_end + timedelta(days=1)
6666

6767
# If our start date has passed, reset it to current_date to avoid picking a slot in the past
68-
if (is_rolling_target == True and target_start < current_date and current_date < target_end):
68+
if (start_time_not_in_past == True and target_start < current_date and current_date < target_end):
6969
_LOGGER.debug(f'Rolling target and {target_start} is in the past. Setting start to {current_date}')
7070
target_start = current_date
7171

@@ -74,6 +74,9 @@ def get_fixed_applicable_time_periods(current_date: datetime, target_start_time:
7474
target_start = target_start + timedelta(days=1)
7575
target_end = target_end + timedelta(days=1)
7676

77+
return (target_start, target_end)
78+
79+
def get_fixed_applicable_time_periods(target_start: datetime, target_end: datetime, time_period_values: list):
7780
_LOGGER.debug(f'Finding rates between {target_start} and {target_end}')
7881

7982
# Retrieve the rates that are applicable for our target rate
@@ -434,4 +437,4 @@ def should_evaluate_target_timeframes(current_date: datetime, target_timeframes:
434437

435438
return ((evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST and all_rates_in_past) or
436439
(evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_FUTURE_OR_PAST and (one_rate_in_past == False or all_rates_in_past)) or
437-
(evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS))
440+
(evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS))

custom_components/target_timeframes/entities/target_timeframe.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
create_weighting,
4949
extract_config,
5050
get_fixed_applicable_time_periods,
51+
get_start_and_end_times,
5152
get_target_time_period_info,
5253
is_target_timeframe_complete_in_period,
5354
should_evaluate_target_timeframes
@@ -166,22 +167,24 @@ async def async_update(self):
166167
if CONFIG_TARGET_MAX_VALUE in self._config:
167168
max_rate = self._config[CONFIG_TARGET_MAX_VALUE]
168169

170+
target_start, target_end = get_start_and_end_times(current_local_date, start_time, end_time, True)
169171
applicable_time_periods = get_fixed_applicable_time_periods(
170-
current_local_date,
171-
start_time,
172-
end_time,
173-
self._data_source_data,
174-
is_rolling_target
172+
target_start,
173+
target_end,
174+
self._data_source_data
175175
)
176176

177-
is_target_timeframe_complete = is_rolling_target == False and is_target_timeframe_complete_in_period(current_local_date, applicable_time_periods, self._target_timeframes)
177+
# Make sure we haven't already completed for the current target timeframe
178+
applicable_target_start, applicable_target_end = get_start_and_end_times(current_local_date, start_time, end_time, False)
179+
is_target_timeframe_complete = is_rolling_target == False and is_target_timeframe_complete_in_period(current_local_date, applicable_target_start, applicable_target_end, self._target_timeframes)
178180

179181
if applicable_time_periods is not None and is_target_timeframe_complete == False:
180182
number_of_slots = math.ceil(target_hours * 2)
181183
weighting = create_weighting(self._config[CONFIG_TARGET_WEIGHTING] if CONFIG_TARGET_WEIGHTING in self._config else None, number_of_slots)
182184

185+
proposed_target_timeframes = None
183186
if (self._config[CONFIG_TARGET_TYPE] == CONFIG_TARGET_TYPE_CONTINUOUS):
184-
self._target_timeframes = calculate_continuous_times(
187+
proposed_target_timeframes = calculate_continuous_times(
185188
applicable_time_periods,
186189
target_hours,
187190
find_highest_values,
@@ -192,7 +195,7 @@ async def async_update(self):
192195
hours_mode = self._config[CONFIG_TARGET_HOURS_MODE]
193196
)
194197
elif (self._config[CONFIG_TARGET_TYPE] == CONFIG_TARGET_TYPE_INTERMITTENT):
195-
self._target_timeframes = calculate_intermittent_times(
198+
proposed_target_timeframes = calculate_intermittent_times(
196199
applicable_time_periods,
197200
target_hours,
198201
find_highest_values,
@@ -204,6 +207,7 @@ async def async_update(self):
204207
else:
205208
_LOGGER.error(f"Unexpected target type: {self._config[CONFIG_TARGET_TYPE]}")
206209

210+
self._target_timeframes = proposed_target_timeframes
207211
self._attributes["target_times"] = self._target_timeframes
208212
self._attributes["target_times_last_evaluated"] = current_date
209213
_LOGGER.debug(f"calculated rates: {self._target_timeframes}")

tests/unit/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
_LOGGER = logging.getLogger(__name__)
99

1010
def create_data_source_data(period_from: datetime, period_to: datetime, expected_values: list):
11-
rates = []
11+
values = []
1212
current_valid_from = period_from
1313
current_valid_to = None
1414

1515
rate_index = 0
1616
while current_valid_to is None or current_valid_to < period_to:
1717
current_valid_to = current_valid_from + timedelta(minutes=30)
1818

19-
rates.append({
19+
values.append({
2020
"start": current_valid_from,
2121
"end": current_valid_to,
2222
"value": expected_values[rate_index],
@@ -28,19 +28,19 @@ def create_data_source_data(period_from: datetime, period_to: datetime, expected
2828
if (rate_index > (len(expected_values) - 1)):
2929
rate_index = 0
3030

31-
return rates
31+
return values
3232

3333
def get_start(rate):
3434
return rate["start"]
3535

3636
def values_to_thirty_minute_increments(items: list, period_from: datetime, period_to: datetime):
37-
"""Process the collection of rates to ensure they're in 30 minute periods"""
37+
"""Process the collection of values to ensure they're in 30 minute periods"""
3838
starting_period_from = period_from
3939
results = []
4040

4141
items.sort(key=get_start)
4242

43-
# We need to normalise our data into 30 minute increments so that all of our rates across all tariffs are the same and it's
43+
# We need to normalise our data into 30 minute increments so that all of our values across all tariffs are the same and it's
4444
# easier to calculate our target rate sensors
4545
for item in items:
4646
value = float(item["value"])
@@ -53,7 +53,7 @@ def values_to_thirty_minute_increments(items: list, period_from: datetime, perio
5353
else:
5454
start = starting_period_from
5555

56-
# Some rates don't have end dates, so we should treat this as our period to target
56+
# Some values don't have end dates, so we should treat this as our period to target
5757
if "end" in item and item["end"] is not None:
5858
target_date = as_utc(parse_datetime(item["end"]))
5959

0 commit comments

Comments
 (0)