Skip to content

Commit 45f1086

Browse files
committed
feat: added support for always evaluation mode for standard target timeframes (1 hour dev time)
1 parent 5f6af77 commit 45f1086

File tree

6 files changed

+122
-16
lines changed

6 files changed

+122
-16
lines changed

_docs/setup/target_timeframe.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,27 @@ This will only evaluate target times if no target times have been calculated or
6464

6565
For example, lets say we have a continuous target which looks between `00:00` and `08:00` has existing target times from `2023-01-02T01:00` to `2023-01-02T02:00`.
6666

67-
* If the current time is `2023-01-02T00:59`, then the target times will be re-evaluated and might change if the target period (i.e. `2023-01-02T00:30` to `2023-01-02T08:30`) has better values than the existing target times (e.g. the external weightings have changed).
67+
* If the current time is `2023-01-02T00:59`, then the target times will be re-evaluated and might change if the target period (i.e. `2023-01-02T00:30` to `2023-01-02T08:00`) has better values than the existing target times (e.g. the external weightings have changed).
6868
* If the current time is `2023-01-02T01:00`, the the target times will not be re-evaluated because we've entered our current target times, even if the evaluation period has cheaper times.
69-
* If the current time is `2023-01-02T02:01`, the the target times will be re-evaluated because our existing target times are in the past and will find the best times in the new rolling target period (i.e. `2023-01-02T02:00` to `2023-01-02T10:00`).
69+
* If the current time is `2023-01-02T02:01`, the the target times will be re-evaluated because our existing target times are in the past and will find the best times in the new target period (i.e. `2023-01-02T02:00` to `2023-01-02T08:00`).
70+
71+
#### Always
72+
73+
This will always evaluate the best target times for the target period, even if the sensor is in the middle of an existing target time period.
74+
75+
For example, lets say we have a continuous target which looks between `00:00` and `08:00` and has existing target times from `2023-01-02T01:00` to `2023-01-02T02:00`.
76+
77+
* If the current time is `2023-01-02T00:59`, then the target times will be re-evaluated and might change if the new target period (i.e. `2023-01-02T00:30` to `2023-01-02T08:30`) has better times than the existing target times.
78+
* If the current time is `2023-01-02T01:31`, then the target times will be re-evaluated and might change if the new target period (i.e. `2023-01-02T01:30` to `2023-01-02T08:30`) has better times than the existing target times.
79+
* If the current time is `2023-01-02T02:01`, the the target times will be re-evaluated because our existing target times are in the past and will find the best times in the new target period (i.e. `2023-01-02T02:00` to `2023-01-02T08:00`).
80+
81+
!!! note
82+
83+
This is only supported when [Re-evaluate within time frame](#re-evaluate-within-time-frame) is enabled, otherwise it will behave the same as the other options.
84+
85+
!!! warning
86+
87+
This setting means that you could end up with the sensor not turning on for the fully requested hours as the target times might be moved ahead half way through the picked times. It also could mean that the sensor doesn't come on at all during the requested look ahead hours (e.g. 8) because the lowest period kept moving back.
7088

7189
### Offset
7290

custom_components/target_timeframes/config/target_timeframe.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT,
1616
CONFIG_TARGET_NAME,
1717
CONFIG_TARGET_OFFSET,
18+
CONFIG_TARGET_ROLLING_TARGET,
1819
CONFIG_TARGET_START_TIME,
20+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE,
21+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS,
1922
CONFIG_TARGET_TYPE,
2023
CONFIG_TARGET_TYPE_CONTINUOUS,
2124
CONFIG_TARGET_WEIGHTING,
@@ -186,4 +189,8 @@ def validate_target_timeframe_config(data):
186189
if minimum_required_minutes_in_slot is not None and (minimum_required_minutes_in_slot < 1 or minimum_required_minutes_in_slot > 30):
187190
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_minimum_required_minutes_in_slot"
188191

192+
if (data[CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE] == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS and
193+
(CONFIG_TARGET_ROLLING_TARGET not in data or data[CONFIG_TARGET_ROLLING_TARGET] == False)):
194+
errors[CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE] = "always_evaluation_not_supported"
195+
189196
return errors

custom_components/target_timeframes/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
options=[
113113
selector.SelectOptionDict(value=CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST, label="All existing target rates are in the past"),
114114
selector.SelectOptionDict(value=CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_FUTURE_OR_PAST, label="Existing target rates haven't started or finished"),
115+
selector.SelectOptionDict(value=CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS, label="Always"),
115116
],
116117
mode=selector.SelectSelectorMode.DROPDOWN,
117118
)

custom_components/target_timeframes/entities/target_timeframe.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ async def async_update(self):
190190
is_target_timeframe_complete = is_rolling_target == False and is_target_timeframe_complete_in_period(
191191
current_local_date,
192192
applicable_target_start,
193-
applicable_target_end, self._target_timeframes,
193+
applicable_target_end,
194+
self._target_timeframes,
194195
self._config[CONFIG_TARGET_NAME]
195196
)
196197

custom_components/target_timeframes/translations/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@
133133
"minimum_or_maximum_value_not_specified": "Either minimum and/or maximum value must be specified for minimum hours mode",
134134
"minimum_value_not_less_than_maximum_value": "Minimum value must be less or equal to the maximum value if both are specified",
135135
"invalid_integer": "Value must be a number with no decimal places",
136-
"invalid_minimum_required_minutes_in_slot": "Value must be between 1 and 30"
136+
"invalid_minimum_required_minutes_in_slot": "Value must be between 1 and 30",
137+
"always_evaluation_not_supported": "Always evaluation mode is only supported when \"Re-evaluate multiple times a day\" is enabled"
137138
}
138139
},
139140
"rolling_target_time_period": {

tests/unit/config/test_validate_target_timeframe_config.py

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from homeassistant.util.dt import (as_utc, parse_datetime)
44
from custom_components.target_timeframes.config.target_timeframe import validate_target_timeframe_config
5-
from custom_components.target_timeframes.const import CONFIG_TARGET_DANGEROUS_SETTINGS, CONFIG_TARGET_END_TIME, CONFIG_TARGET_HOURS, CONFIG_TARGET_HOURS_MODE, CONFIG_TARGET_HOURS_MODE_EXACT, CONFIG_TARGET_HOURS_MODE_MAXIMUM, CONFIG_TARGET_HOURS_MODE_MINIMUM, CONFIG_TARGET_MAX_VALUE, CONFIG_TARGET_MIN_VALUE, CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT, CONFIG_TARGET_NAME, CONFIG_TARGET_OFFSET, CONFIG_TARGET_START_TIME, CONFIG_TARGET_TYPE, CONFIG_TARGET_TYPE_CONTINUOUS, CONFIG_TARGET_TYPE_INTERMITTENT, CONFIG_TARGET_WEIGHTING, DATA_SCHEMA_ROLLING_TARGET_TIME_PERIOD, DATA_SCHEMA_TARGET_TIME_PERIOD
5+
from custom_components.target_timeframes.const import CONFIG_TARGET_DANGEROUS_SETTINGS, CONFIG_TARGET_END_TIME, CONFIG_TARGET_HOURS, CONFIG_TARGET_HOURS_MODE, CONFIG_TARGET_HOURS_MODE_EXACT, CONFIG_TARGET_HOURS_MODE_MAXIMUM, CONFIG_TARGET_HOURS_MODE_MINIMUM, CONFIG_TARGET_MAX_VALUE, CONFIG_TARGET_MIN_VALUE, CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT, CONFIG_TARGET_NAME, CONFIG_TARGET_OFFSET, CONFIG_TARGET_ROLLING_TARGET, CONFIG_TARGET_START_TIME, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_FUTURE_OR_PAST, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS, CONFIG_TARGET_TYPE, CONFIG_TARGET_TYPE_CONTINUOUS, CONFIG_TARGET_TYPE_INTERMITTENT, CONFIG_TARGET_WEIGHTING, DATA_SCHEMA_ROLLING_TARGET_TIME_PERIOD, DATA_SCHEMA_TARGET_TIME_PERIOD
66
from ..config import assert_errors_not_present, get_schema_keys
77

88
now = as_utc(parse_datetime("2023-08-20T10:00:00Z"))
@@ -22,7 +22,8 @@ async def test_when_config_is_valid_no_errors_returned():
2222
CONFIG_TARGET_MIN_VALUE: "0",
2323
CONFIG_TARGET_MAX_VALUE: "10",
2424
CONFIG_TARGET_WEIGHTING: "2,*,2",
25-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT
25+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
26+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
2627
}
2728

2829
# Act
@@ -44,7 +45,8 @@ async def test_when_optional_config_is_valid_no_errors_returned():
4445
CONFIG_TARGET_MIN_VALUE: None,
4546
CONFIG_TARGET_MAX_VALUE: None,
4647
CONFIG_TARGET_WEIGHTING: None,
47-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT
48+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
49+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
4850
}
4951

5052
# Act
@@ -68,7 +70,8 @@ async def test_when_config_has_invalid_name_then_errors_returned(name):
6870
CONFIG_TARGET_START_TIME: "00:00",
6971
CONFIG_TARGET_END_TIME: "16:00",
7072
CONFIG_TARGET_OFFSET: "-00:00:00",
71-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT
73+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
74+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
7275
}
7376

7477
# Act
@@ -89,7 +92,8 @@ async def test_when_config_has_valid_hours_then_no_errors_returned():
8992
CONFIG_TARGET_START_TIME: "00:00",
9093
CONFIG_TARGET_END_TIME: "16:00",
9194
CONFIG_TARGET_OFFSET: "-00:00:00",
92-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT
95+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
96+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
9397
}
9498

9599
# Act
@@ -117,7 +121,8 @@ async def test_when_config_has_invalid_hours_then_errors_returned(hours):
117121
CONFIG_TARGET_START_TIME: "00:00",
118122
CONFIG_TARGET_END_TIME: "16:00",
119123
CONFIG_TARGET_OFFSET: "-00:00:00",
120-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT
124+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
125+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
121126
}
122127

123128
# Act
@@ -147,7 +152,8 @@ async def test_when_config_has_invalid_start_time_then_errors_returned(start_tim
147152
CONFIG_TARGET_START_TIME: start_time,
148153
CONFIG_TARGET_END_TIME: "16:00",
149154
CONFIG_TARGET_OFFSET: "-00:00:00",
150-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT
155+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
156+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
151157
}
152158

153159
# Act
@@ -178,6 +184,7 @@ async def test_when_config_has_invalid_end_time_then_errors_returned(end_time):
178184
CONFIG_TARGET_END_TIME: end_time,
179185
CONFIG_TARGET_OFFSET: "-00:00:00",
180186
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
187+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
181188
}
182189

183190
# Act
@@ -213,6 +220,7 @@ async def test_when_config_has_invalid_offset_then_errors_returned(offset):
213220
CONFIG_TARGET_END_TIME: "16:00",
214221
CONFIG_TARGET_OFFSET: offset,
215222
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
223+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
216224
}
217225

218226
# Act
@@ -238,6 +246,7 @@ async def test_when_hours_exceed_selected_time_frame_then_errors_returned(start_
238246
CONFIG_TARGET_END_TIME: end_time,
239247
CONFIG_TARGET_OFFSET: "-00:00:00",
240248
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
249+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
241250
}
242251

243252
# Act
@@ -260,6 +269,7 @@ async def test_when_config_is_valid_and_not_agile_then_no_errors_returned(start_
260269
CONFIG_TARGET_NAME: "test",
261270
CONFIG_TARGET_HOURS: "1.5",
262271
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
272+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
263273
}
264274

265275
if start_time is not None:
@@ -296,6 +306,7 @@ async def test_when_config_is_valid_and_agile_then_no_errors_returned(start_time
296306
CONFIG_TARGET_NAME: "test",
297307
CONFIG_TARGET_HOURS: "1.5",
298308
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
309+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
299310
}
300311

301312
if start_time is not None:
@@ -331,6 +342,7 @@ async def test_when_weighting_is_invalid_then_weighting_error_returned(weighting
331342
CONFIG_TARGET_HOURS: "1.5",
332343
CONFIG_TARGET_WEIGHTING: weighting,
333344
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
345+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
334346
}
335347

336348
# Act
@@ -353,6 +365,7 @@ async def test_when_weighting_set_and_type_invalid_then_weighting_error_returned
353365
CONFIG_TARGET_HOURS: "1.5",
354366
CONFIG_TARGET_WEIGHTING: "1,2,3",
355367
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
368+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
356369
}
357370

358371
# Act
@@ -389,7 +402,8 @@ async def test_when_hour_mode_is_minimum_and_minimum_or_maximum_value_is_specifi
389402
CONFIG_TARGET_OFFSET: "-00:30:00",
390403
CONFIG_TARGET_MIN_VALUE: min_value,
391404
CONFIG_TARGET_MAX_VALUE: max_value,
392-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_MINIMUM
405+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_MINIMUM,
406+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
393407
}
394408

395409
# Act
@@ -416,7 +430,8 @@ async def test_when_minimum_value_greater_to_maximum_value_is_specified_then_err
416430
CONFIG_TARGET_OFFSET: "-00:30:00",
417431
CONFIG_TARGET_MIN_VALUE: min_value,
418432
CONFIG_TARGET_MAX_VALUE: max_value,
419-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_MINIMUM
433+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_MINIMUM,
434+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
420435
}
421436

422437
# Act
@@ -443,7 +458,8 @@ async def test_when_hour_mode_is_not_exact_and_weighting_specified_then_error_re
443458
CONFIG_TARGET_OFFSET: "-00:30:00",
444459
CONFIG_TARGET_WEIGHTING: "2,*,2",
445460
CONFIG_TARGET_MIN_VALUE: "0.18",
446-
CONFIG_TARGET_HOURS_MODE: hour_mode
461+
CONFIG_TARGET_HOURS_MODE: hour_mode,
462+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
447463
}
448464

449465
# Act
@@ -464,7 +480,8 @@ async def test_when_hour_mode_is_minimum_and_minimum_and_maximum_value_is_not_sp
464480
CONFIG_TARGET_START_TIME: "00:00",
465481
CONFIG_TARGET_END_TIME: "00:00",
466482
CONFIG_TARGET_OFFSET: "-00:30:00",
467-
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_MINIMUM
483+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_MINIMUM,
484+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST
468485
}
469486

470487
# Act
@@ -494,6 +511,7 @@ async def test_when_minimum_required_minutes_set_to_valid_integer_then_no_error_
494511
CONFIG_TARGET_MAX_VALUE: "10",
495512
CONFIG_TARGET_WEIGHTING: "2,*,2",
496513
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
514+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
497515
CONFIG_TARGET_DANGEROUS_SETTINGS: {
498516
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT: minimum_required_minutes
499517
}
@@ -524,6 +542,7 @@ async def test_when_minimum_required_minutes_set_to_invalid_integer_then_error_r
524542
CONFIG_TARGET_MAX_VALUE: "10",
525543
CONFIG_TARGET_WEIGHTING: "2,*,2",
526544
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
545+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
527546
CONFIG_TARGET_DANGEROUS_SETTINGS: {
528547
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT: minimum_required_minutes
529548
}
@@ -557,6 +576,7 @@ async def test_when_minimum_required_minutes_set_to_invalid_value_then_error_ret
557576
CONFIG_TARGET_MAX_VALUE: "10",
558577
CONFIG_TARGET_WEIGHTING: "2,*,2",
559578
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
579+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST,
560580
CONFIG_TARGET_DANGEROUS_SETTINGS: {
561581
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT: minimum_required_minutes
562582
}
@@ -568,4 +588,62 @@ async def test_when_minimum_required_minutes_set_to_invalid_value_then_error_ret
568588
# Assert
569589
assert CONFIG_TARGET_DANGEROUS_SETTINGS in errors
570590
assert errors[CONFIG_TARGET_DANGEROUS_SETTINGS] == "invalid_minimum_required_minutes_in_slot"
571-
assert_errors_not_present(errors, default_keys, CONFIG_TARGET_DANGEROUS_SETTINGS)
591+
assert_errors_not_present(errors, default_keys, CONFIG_TARGET_DANGEROUS_SETTINGS)
592+
593+
@pytest.mark.asyncio
594+
async def test_when_evaluation_mode_is_always_and_rolling_turned_off_then_error_returned():
595+
# Arrange
596+
data = {
597+
CONFIG_TARGET_TYPE: CONFIG_TARGET_TYPE_CONTINUOUS,
598+
CONFIG_TARGET_NAME: "test",
599+
CONFIG_TARGET_HOURS: "1.5",
600+
CONFIG_TARGET_START_TIME: "00:00",
601+
CONFIG_TARGET_END_TIME: "00:00",
602+
CONFIG_TARGET_OFFSET: "-00:30:00",
603+
CONFIG_TARGET_MIN_VALUE: "0",
604+
CONFIG_TARGET_MAX_VALUE: "10",
605+
CONFIG_TARGET_WEIGHTING: "2,*,2",
606+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
607+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS,
608+
609+
}
610+
611+
# Act
612+
errors = validate_target_timeframe_config(data)
613+
614+
# Assert
615+
assert CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE in errors
616+
assert errors[CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE] == "always_evaluation_not_supported"
617+
assert_errors_not_present(errors, default_keys, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE)
618+
619+
@pytest.mark.asyncio
620+
@pytest.mark.parametrize("rolling_target,evaluation_mode",[
621+
(True, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS),
622+
(True, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST),
623+
(False, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST),
624+
(True, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_FUTURE_OR_PAST),
625+
(False, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_FUTURE_OR_PAST),
626+
])
627+
async def test_when_evaluation_mode_is_not_always_and_rolling_turned_off_then_no_errors_returned(rolling_target: bool, evaluation_mode: str):
628+
# Arrange
629+
data = {
630+
CONFIG_TARGET_TYPE: CONFIG_TARGET_TYPE_CONTINUOUS,
631+
CONFIG_TARGET_NAME: "test",
632+
CONFIG_TARGET_HOURS: "1.5",
633+
CONFIG_TARGET_START_TIME: "00:00",
634+
CONFIG_TARGET_END_TIME: "00:00",
635+
CONFIG_TARGET_OFFSET: "-00:30:00",
636+
CONFIG_TARGET_MIN_VALUE: "0",
637+
CONFIG_TARGET_MAX_VALUE: "10",
638+
CONFIG_TARGET_WEIGHTING: "2,*,2",
639+
CONFIG_TARGET_HOURS_MODE: CONFIG_TARGET_HOURS_MODE_EXACT,
640+
CONFIG_TARGET_ROLLING_TARGET: rolling_target,
641+
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE: evaluation_mode,
642+
643+
}
644+
645+
# Act
646+
errors = validate_target_timeframe_config(data)
647+
648+
# Assert
649+
assert_errors_not_present(errors, default_keys, CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE)

0 commit comments

Comments
 (0)