Skip to content

Commit 3d4ba9d

Browse files
feat: Added configuration for standard and rolling target timeframes to determine minimum minutes for a slot to be evaluated (3.5 hours dev time)
1 parent a61edfa commit 3d4ba9d

18 files changed

+789
-572
lines changed

_docs/faq.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44

55
For some reason when you update the configuration via the integration page, the associated entities don't update. You'll need to reload the parent entry to get the configuration to take effect. This is something I'm currently investigating.
66

7+
## I've setup a target timeframe with the default time period (00:00-00:00) or a rolling target timeframe looking ahead for 24 hours but it's not updating. Is something broken?
8+
9+
By default, the target timeframe sensors require the supporting data for the specified time periods to be available in order to be calculate. For example if it was `00:00` on `1/12/2025`, then the standard target timeframe would require data for _at least_ between `2025-12-01T00:00` and `2025-12-01T00:00`. If this is not the case, then the sensor will not be evaluated. This is made clearer by the `values_incomplete` attributes of the [target timeframe](./setup/target_timeframe.md#attributes) and [rolling target timeframe](./setup/rolling_target_timeframe.md#attributes).
10+
11+
For some data sources, this might cause issues due to the data available (e.g. When you're on the Agile tariff of [Octopus Energy UK](./blueprints.md#octopus-energy) where data is available in advanced up to `23:00`).
12+
13+
In this scenario, you have two options.
14+
15+
1. The recommended approach would be to adjust the time period that the target timeframe looks at. See below for example suggestions
16+
17+
| Data Source | Standard Target Timeframe Recommendation | Rolling Target Timeframe Recommendation |
18+
|-|-|-|
19+
| Agile tariff for [Octopus Energy UK](./blueprints.md#octopus-energy) | Have an end time before or equal to `23:00` (e.g. `23:00-23:00` if you want to look at a full 24 hours) | Because data refreshes around `16:00` and will go up to `23:00`, then your look ahead hours should be no more than `7` to ensure it's working `99%` of the time |
20+
21+
2. Set the configuration option to [calculate with incomplete data](./setup/target_timeframe.md#calculate-with-incomplete-data). This _could_ have undesired consequences in the calculations (e.g. picking times that look odd retrospectively because the full data wasn't available at the time of picking), so use with caution.
22+
723
## How do I increase the logs for the integration?
824

925
If you are having issues, it would be helpful to include Home Assistant logs as part of any raised issue. This can be done by following the [instructions](https://www.home-assistant.io/docs/configuration/troubleshooting/#enabling-debug-logging) outlined by Home Assistant.

_docs/setup/rolling_target_timeframe.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ These settings can have undesired effects and are not recommended to be changed,
121121

122122
By default, the target timeframe isn't calculated if there isn't enough data for the period of time being evaluated. For example, if you have a look ahead hours set to 4 hours, it's 9pm and you only have data up to midnight, then the next target timeframe will not be calculated. If you turn this setting on, then the sensor will attempt to look for 4 hours worth of data if available, otherwise it will evaluate with whatever data is available (in this scenario 2 hours between 10pm and 12am).
123123

124+
#### Minimum required minutes in slots
125+
126+
By default, 30 minute slots that are part way through are not considered when evaluating rolling target time frames. For example, if you are looking for the best slots for the next 4 hours and it's 10:01, then only slots between 10:30 to 14:30 will be evaluated. This threshold can be changed here to a lower value if you want to take account of slots that are partially in the past. For example if this was set to 29, then the previous example would evaluate slots between 10:00 to 14:00.
127+
124128
## Attributes
125129

126130
The following attributes are available on each sensor

_docs/setup/target_timeframe.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ These settings can have undesired effects and are not recommended to be changed,
131131

132132
By default, the target timeframe isn't calculated if there isn't enough data for the period of time being evaluated. For example, if you have a timeframe looking between 10pm and 2am, it's 9pm and you only have data up to midnight, then the next target timeframe will not be calculated. If you turn this setting on, then the sensor will attempt to look for data between 10pm and 2am if available, otherwise it will evaluate with whatever data is available (in this scenario 10pm to 12am).
133133

134+
#### Minimum required minutes in slots
135+
136+
By default, 30 minute slots that are part way through are not considered when evaluating target time frames. For example, if you are looking for the best slots between 10:00 to 12:00 and it's 10:01, then only slots between 10:30 to 12:00 will be evaluated. This threshold can be changed here to a lower value if you want to take account of slots that are partially in the past. For example if this was set to 29, then the previous example would evaluate slots between 10:00 to 12:00.
137+
138+
!!! warn
139+
140+
Changing this can cause sensors to not come on for the correct amount of time by up to 30 minutes.
141+
134142
## Attributes
135143

136144
The following attributes are available on each sensor
@@ -163,6 +171,7 @@ The following attributes are available on each sensor
163171
| `next_max_value` | `float` | The average value for the next continuous discovered period. This will only be populated if `target_times` has been calculated and at least one period/block is in the future. |
164172
| `target_times_last_evaluated` | datetime | The datetime the target times collection was last evaluated. This will occur if all previous target times are in the past and all values are available for the requested future time period. For example, if you are targeting 16:00 (day 1) to 16:00 (day 2), and you only have values up to 23:00 (day 1), then the target values won't be calculated. |
165173
| `calculate_with_incomplete_data` | boolean | Determines if calculations should occur when there isn't enough data to satisfy the look ahead hours |
174+
| `minimum_required_minutes_in_slot` | integer | Determines the configured minimum number of minutes to be present in a slot for it to be considered |
166175

167176
## Services
168177

custom_components/target_timeframes/config/rolling_target_timeframe.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22

33
from ..const import (
44
CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD,
5+
CONFIG_TARGET_DANGEROUS_SETTINGS,
56
CONFIG_TARGET_HOURS,
67
CONFIG_TARGET_HOURS_MODE,
78
CONFIG_TARGET_HOURS_MODE_EXACT,
89
CONFIG_TARGET_HOURS_MODE_MINIMUM,
910
CONFIG_TARGET_MAX_VALUE,
1011
CONFIG_TARGET_MIN_VALUE,
12+
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT,
1113
CONFIG_TARGET_NAME,
1214
CONFIG_TARGET_OFFSET,
1315
CONFIG_TARGET_TYPE,
1416
CONFIG_TARGET_TYPE_CONTINUOUS,
1517
CONFIG_TARGET_WEIGHTING,
1618
REGEX_ENTITY_NAME,
1719
REGEX_HOURS,
20+
REGEX_INTEGER,
1821
REGEX_OFFSET_PARTS,
1922
REGEX_VALUE,
2023
REGEX_WEIGHTING
@@ -133,4 +136,25 @@ def validate_rolling_target_timeframe_config(data):
133136
if CONFIG_TARGET_HOURS not in errors and CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD not in errors and data[CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD] < data[CONFIG_TARGET_HOURS]:
134137
errors[CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD] = "look_ahead_hours_not_long_enough"
135138

139+
minimum_required_minutes_in_slot: int | None = None
140+
if (CONFIG_TARGET_DANGEROUS_SETTINGS in data and
141+
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT in data[CONFIG_TARGET_DANGEROUS_SETTINGS] and
142+
data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT] is not None):
143+
144+
if isinstance(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT], int) == False:
145+
if isinstance(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT], str):
146+
matches = re.search(REGEX_INTEGER, data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT])
147+
if matches is None:
148+
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_integer"
149+
else:
150+
minimum_required_minutes_in_slot = int(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT])
151+
data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT] = minimum_required_minutes_in_slot
152+
else:
153+
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_integer"
154+
else:
155+
minimum_required_minutes_in_slot = data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT]
156+
157+
if minimum_required_minutes_in_slot is not None and (minimum_required_minutes_in_slot < 1 or minimum_required_minutes_in_slot > 30):
158+
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_minimum_required_minutes_in_slot"
159+
136160
return errors

custom_components/target_timeframes/config/target_timeframe.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
from homeassistant.util.dt import (parse_datetime)
55

66
from ..const import (
7+
CONFIG_TARGET_DANGEROUS_SETTINGS,
78
CONFIG_TARGET_END_TIME,
89
CONFIG_TARGET_HOURS,
910
CONFIG_TARGET_HOURS_MODE,
1011
CONFIG_TARGET_HOURS_MODE_EXACT,
1112
CONFIG_TARGET_HOURS_MODE_MINIMUM,
1213
CONFIG_TARGET_MAX_VALUE,
1314
CONFIG_TARGET_MIN_VALUE,
15+
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT,
1416
CONFIG_TARGET_NAME,
1517
CONFIG_TARGET_OFFSET,
1618
CONFIG_TARGET_START_TIME,
@@ -19,6 +21,7 @@
1921
CONFIG_TARGET_WEIGHTING,
2022
REGEX_ENTITY_NAME,
2123
REGEX_HOURS,
24+
REGEX_INTEGER,
2225
REGEX_OFFSET_PARTS,
2326
REGEX_VALUE,
2427
REGEX_TIME,
@@ -162,4 +165,25 @@ def validate_target_timeframe_config(data):
162165
if is_time_frame_long_enough(data[CONFIG_TARGET_HOURS], start_time, end_time) == False:
163166
errors[CONFIG_TARGET_HOURS] = "invalid_hours_time_frame"
164167

168+
minimum_required_minutes_in_slot: int | None = None
169+
if (CONFIG_TARGET_DANGEROUS_SETTINGS in data and
170+
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT in data[CONFIG_TARGET_DANGEROUS_SETTINGS] and
171+
data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT] is not None):
172+
173+
if isinstance(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT], int) == False:
174+
if isinstance(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT], str):
175+
matches = re.search(REGEX_INTEGER, data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT])
176+
if matches is None:
177+
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_integer"
178+
else:
179+
minimum_required_minutes_in_slot = int(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT])
180+
data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT] = minimum_required_minutes_in_slot
181+
else:
182+
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_integer"
183+
else:
184+
minimum_required_minutes_in_slot = data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT]
185+
186+
if minimum_required_minutes_in_slot is not None and (minimum_required_minutes_in_slot < 1 or minimum_required_minutes_in_slot > 30):
187+
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_minimum_required_minutes_in_slot"
188+
165189
return errors

custom_components/target_timeframes/const.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS = "always"
4141
CONFIG_TARGET_DANGEROUS_SETTINGS = "dangerous_settings"
4242
CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA = "calculate_with_incomplete_data"
43+
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT = "minimum_required_minutes_in_slot"
44+
CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT = 29
4345

4446
CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD = "look_ahead_hours"
4547

@@ -67,6 +69,7 @@
6769
REGEX_OFFSET_PARTS = "^(-)?([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"
6870
REGEX_DATE = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
6971
REGEX_VALUE = "^(-)?[0-9]+(\\.[0-9]+)*$"
72+
REGEX_INTEGER = "^(-)?[0-9]+$"
7073

7174
REGEX_WEIGHTING_NUMBERS = "([0-9]+\\.?[0-9]*(,[0-9]+\\.?[0-9]*+)*)"
7275
REGEX_WEIGHTING_START = "(\\*(,[0-9]+\\.?[0-9]*+)+)"
@@ -123,6 +126,7 @@
123126
vol.Schema(
124127
{
125128
vol.Required(CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA, default=False): bool,
129+
vol.Required(CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT, default=CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT): int,
126130
}
127131
),
128132
{"collapsed": True},
@@ -172,6 +176,7 @@
172176
vol.Schema(
173177
{
174178
vol.Required(CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA, default=False): bool,
179+
vol.Required(CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT, default=CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT): int,
175180
}
176181
),
177182
{"collapsed": True},

custom_components/target_timeframes/entities/__init__.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from homeassistant.util.dt import (as_utc, parse_datetime)
88

9-
from ..const import 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_HOURS_MODE_EXACT, CONFIG_TARGET_HOURS_MODE_MAXIMUM, CONFIG_TARGET_HOURS_MODE_MINIMUM, CONFIG_TARGET_KEYS, REGEX_OFFSET_PARTS, REGEX_WEIGHTING
9+
from ..const import CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT, 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_HOURS_MODE_EXACT, CONFIG_TARGET_HOURS_MODE_MAXIMUM, CONFIG_TARGET_HOURS_MODE_MINIMUM, CONFIG_TARGET_KEYS, REGEX_OFFSET_PARTS, REGEX_WEIGHTING
1010

1111
_LOGGER = logging.getLogger(__name__)
1212

@@ -43,7 +43,7 @@ def is_target_timeframe_complete_in_period(current_date: datetime, start_time: d
4343
target_timeframes[-1]["end"] <= current_date
4444
)
4545

46-
def get_start_and_end_times(current_date: datetime, target_start_time: str, target_end_time: str, start_time_not_in_past = True, context: str = None):
46+
def get_start_and_end_times(current_date: datetime, target_start_time: str, target_end_time: str, minimum_slot_minutes = None, context: str = None):
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:
@@ -64,10 +64,15 @@ def get_start_and_end_times(current_date: datetime, target_start_time: str, targ
6464
else:
6565
target_end = target_end + timedelta(days=1)
6666

67-
# If our start date has passed, reset it to current_date to avoid picking a slot in the past
68-
if (start_time_not_in_past == True and target_start < current_date and current_date < target_end):
69-
_LOGGER.debug(f'{context} - Rolling target and {target_start} is in the past. Setting start to {current_date}')
70-
target_start = current_date
67+
if (minimum_slot_minutes is not None and target_start < current_date and current_date < target_end):
68+
current_date_start = current_date.replace(minute=30 if current_date.minute >= 30 else 0, second=0, microsecond=0)
69+
minutes_remaining_in_current_slot = 30 - ((current_date.replace(second=0, microsecond=0) - current_date_start).total_seconds() / 60)
70+
if (minutes_remaining_in_current_slot >= minimum_slot_minutes):
71+
_LOGGER.debug(f'{context} - Current slot is sufficient for minimum slot minutes, so using current date start: {current_date_start}')
72+
target_start = current_date_start
73+
else:
74+
target_start = current_date_start + timedelta(minutes=30)
75+
_LOGGER.debug(f'{context} - Current slot is not sufficient for minimum slot minutes, so using next slot start: {target_start}')
7176

7277
# If our start and end are both in the past, then look to the next day
7378
if (target_start < current_date and target_end < current_date):
@@ -98,15 +103,22 @@ def get_fixed_applicable_time_periods(target_start: datetime, target_end: dateti
98103

99104
return applicable_rates
100105

101-
def get_rolling_applicable_time_periods(current_date: datetime, time_period_values: list, target_hours: float, calculate_with_incomplete_data = False, context: str = None):
106+
def get_rolling_applicable_time_periods(current_date: datetime,
107+
time_period_values: list,
108+
target_hours: float,
109+
minimum_slot_minutes: int = CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT,
110+
calculate_with_incomplete_data = False,
111+
context: str = None):
102112
# Retrieve the rates that are applicable for our target rate
103113
applicable_time_periods = []
104114
periods = target_hours * 2
105115

106116
if time_period_values is not None:
107-
for rate in time_period_values:
108-
if rate["end"] >= current_date:
109-
new_rate = dict(rate)
117+
for time_period in time_period_values:
118+
119+
minutes_remaining_in_current_slot = 30 - ((current_date.replace(second=0, microsecond=0) - time_period["start"]).total_seconds() / 60)
120+
if minutes_remaining_in_current_slot >= minimum_slot_minutes and time_period["end"] >= current_date:
121+
new_rate = dict(time_period)
110122
applicable_time_periods.append(new_rate)
111123

112124
if len(applicable_time_periods) >= periods:

custom_components/target_timeframes/entities/rolling_target_timeframe.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD,
2323
CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA,
2424
CONFIG_TARGET_DANGEROUS_SETTINGS,
25+
CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT,
26+
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT,
2527
CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE,
2628
CONFIG_TARGET_HOURS_MODE,
2729
CONFIG_TARGET_MAX_VALUE,
@@ -147,10 +149,15 @@ async def async_update(self):
147149
if CONFIG_TARGET_DANGEROUS_SETTINGS in self._config and CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA in self._config[CONFIG_TARGET_DANGEROUS_SETTINGS]:
148150
calculate_with_incomplete_data = self._config[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA]
149151

152+
minimum_slot_minutes = CONFIG_TARGET_DEFAULT_MINIMUM_REQUIRED_MINUTES_IN_SLOT
153+
if CONFIG_TARGET_DANGEROUS_SETTINGS in self._config and CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT in self._config[CONFIG_TARGET_DANGEROUS_SETTINGS]:
154+
minimum_slot_minutes = self._config[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT]
155+
150156
applicable_time_periods = get_rolling_applicable_time_periods(
151157
current_local_date,
152158
self._data_source_data,
153159
self._config[CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD],
160+
minimum_slot_minutes,
154161
calculate_with_incomplete_data,
155162
self._config[CONFIG_TARGET_NAME]
156163
)
@@ -332,5 +339,7 @@ def update_default_attributes(self):
332339
calculate_with_incomplete_data = False
333340
if CONFIG_TARGET_DANGEROUS_SETTINGS in self._config and CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA in self._config[CONFIG_TARGET_DANGEROUS_SETTINGS]:
334341
calculate_with_incomplete_data = self._config[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA]
335-
del self._attributes[CONFIG_TARGET_DANGEROUS_SETTINGS]
336-
self._attributes[CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA] = calculate_with_incomplete_data
342+
self._attributes[CONFIG_TARGET_CALCULATE_WITH_INCOMPLETE_DATA] = calculate_with_incomplete_data
343+
344+
if CONFIG_TARGET_DANGEROUS_SETTINGS in self._attributes:
345+
del self._attributes[CONFIG_TARGET_DANGEROUS_SETTINGS]

0 commit comments

Comments
 (0)