Skip to content

Commit 9c956dc

Browse files
Merge pull request #43 from BottlecapDave/develop
Next release
2 parents 9e90984 + 3d4ba9d commit 9c956dc

19 files changed

+1431
-503
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: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,22 +96,34 @@ There may be times that you want the target timeframe sensors to not take into a
9696

9797
If hours mode is set to **minimum**, then a minimum and/or maximum rate must be specified in order for the target timeframe sensor to know what the cut off is for discovered times.
9898

99-
### Weighting
99+
### Weighting/Multipliers
100100

101101
!!! info
102102

103-
This is only available for **continuous** target timeframe sensors in **exact** hours mode.
103+
This is only available for **continuous** target value sensors in **exact** hours mode.
104104

105-
There may be times when the device you're wanting the target timeframe sensor to turn on doesn't have a consistent power draw. You can specify a weighting which can be applied to each discovered 30 minute slot. This can be specified in a few different ways. Take the following example weighting for a required 2 hours.
105+
There may be times when the device you're wanting the target value sensor to turn on doesn't have a consistent power draw. You can specify a weighting/multiplier which can be applied to the value of each discovered 30 minute slot. This can be specified in a few different ways. Take the following example weighting/multiplier for a required 2 hours.
106106

107-
* `1,1,2,1` - This applies a weighting of 1 to the first, second and forth slot and a weighting of 2 to the third slot. This will try and make the lowest slot fall on the third slot, as long as the surrounding slots are cheaper than other continuous slots.
108-
* `*,2,1` - This applies a weighting of 1 to the first, second and forth slot and a weighting of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting of 1 for all slots before the ones specified.
109-
* `1,1,2,*` - This applies a weighting of 1 to the first, second and forth slot and a weighting of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting of 1 for all slots after the ones specified.
110-
* `2,*,2` - This applies a weighting of 2 to the first and forth slot and a weighting of 1 to all slots in between. The `*` can be used as a placeholder for the standard weighting of 1 for all slots in between the specified slots.
107+
* `1,1,2,1` - This applies a weighting/multiplier of 1 to the first, second and forth slot and a weighting/multiplier of 2 to the third slot. This will try and make the lowest slot fall on the third slot, as long as the surrounding slots are cheaper than other continuous slots.
108+
* `*,2,1` - This applies a weighting/multiplier of 1 to the first, second and forth slot and a weighting/multiplier of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting/multiplier of 1 for all slots before the ones specified.
109+
* `1,1,2,*` - This applies a weighting/multiplier of 1 to the first, second and forth slot and a weighting/multiplier of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting/multiplier of 1 for all slots after the ones specified.
110+
* `2,*,2` - This applies a weighting/multiplier of 2 to the first and forth slot and a weighting/multiplier of 1 to all slots in between. The `*` can be used as a placeholder for the standard weighting/multiplier of 1 for all slots in between the specified slots.
111111

112-
Each slot weighting must be a whole number or decimal number and be positive.
112+
Each slot weighting/multiplier must be a whole number or decimal number and be positive.
113113

114-
You can also use weightings to ignore slots. This can be done by assigning a value of 0 for the desired slot.
114+
You can also use weightings/multipliers to ignore slots. This can be done by assigning a value of 0 for the desired slot.
115+
116+
### Dangerous settings
117+
118+
These settings can have undesired effects and are not recommended to be changed, but there might be certain scenarios where this is the desired outcome.
119+
120+
#### Calculate with incomplete data
121+
122+
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).
123+
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.
115127

116128
## Attributes
117129

@@ -141,6 +153,7 @@ The following attributes are available on each sensor
141153
| `next_min_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. |
142154
| `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. |
143155
| `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 timeframes won't be calculated. |
156+
| `calculate_with_incomplete_data` | boolean | Determines if calculations should occur when there isn't enough data to satisfy the look ahead hours |
144157

145158
## Services
146159

_docs/setup/target_timeframe.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,22 +106,38 @@ There may be times that you want the target timeframe sensors to not take into a
106106

107107
If hours mode is set to **minimum**, then a minimum and/or maximum value must be specified in order for the target timeframe sensor to know what the cut off is for discovered times.
108108

109-
### Weighting
109+
### Weighting/Multipliers
110110

111111
!!! info
112112

113113
This is only available for **continuous** target value sensors in **exact** hours mode.
114114

115-
There may be times when the device you're wanting the target value sensor to turn on doesn't have a consistent power draw. You can specify a weighting which can be applied to each discovered 30 minute slot. This can be specified in a few different ways. Take the following example weighting for a required 2 hours.
115+
There may be times when the device you're wanting the target value sensor to turn on doesn't have a consistent power draw. You can specify a weighting/multiplier which can be applied to the value of each discovered 30 minute slot. This can be specified in a few different ways. Take the following example weighting/multiplier for a required 2 hours.
116116

117-
* `1,1,2,1` - This applies a weighting of 1 to the first, second and forth slot and a weighting of 2 to the third slot. This will try and make the lowest slot fall on the third slot, as long as the surrounding slots are cheaper than other continuous slots.
118-
* `*,2,1` - This applies a weighting of 1 to the first, second and forth slot and a weighting of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting of 1 for all slots before the ones specified.
119-
* `1,1,2,*` - This applies a weighting of 1 to the first, second and forth slot and a weighting of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting of 1 for all slots after the ones specified.
120-
* `2,*,2` - This applies a weighting of 2 to the first and forth slot and a weighting of 1 to all slots in between. The `*` can be used as a placeholder for the standard weighting of 1 for all slots in between the specified slots.
117+
* `1,1,2,1` - This applies a weighting/multiplier of 1 to the first, second and forth slot and a weighting/multiplier of 2 to the third slot. This will try and make the lowest slot fall on the third slot, as long as the surrounding slots are cheaper than other continuous slots.
118+
* `*,2,1` - This applies a weighting/multiplier of 1 to the first, second and forth slot and a weighting/multiplier of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting/multiplier of 1 for all slots before the ones specified.
119+
* `1,1,2,*` - This applies a weighting/multiplier of 1 to the first, second and forth slot and a weighting/multiplier of 2 to the third slot. The `*` can be used as a placeholder for the standard weighting/multiplier of 1 for all slots after the ones specified.
120+
* `2,*,2` - This applies a weighting/multiplier of 2 to the first and forth slot and a weighting/multiplier of 1 to all slots in between. The `*` can be used as a placeholder for the standard weighting/multiplier of 1 for all slots in between the specified slots.
121121

122-
Each slot weighting must be a whole number or decimal number and be positive.
122+
Each slot weighting/multiplier must be a whole number or decimal number and be positive.
123123

124-
You can also use weightings to ignore slots. This can be done by assigning a value of 0 for the desired slot.
124+
You can also use weightings/multipliers to ignore slots. This can be done by assigning a value of 0 for the desired slot.
125+
126+
### Dangerous settings
127+
128+
These settings can have undesired effects and are not recommended to be changed, but there might be certain scenarios where this is the desired outcome.
129+
130+
#### Calculate with incomplete data
131+
132+
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).
133+
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.
125141

126142
## Attributes
127143

@@ -154,6 +170,8 @@ The following attributes are available on each sensor
154170
| `next_min_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. |
155171
| `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. |
156172
| `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. |
173+
| `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 |
157175

158176
## Services
159177

custom_components/target_timeframes/config/rolling_target_timeframe.py

Lines changed: 37 additions & 2 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
@@ -81,21 +84,32 @@ def validate_rolling_target_timeframe_config(data):
8184
if matches is None:
8285
errors[CONFIG_TARGET_OFFSET] = "invalid_offset"
8386

87+
minimum_value: float | None = None
8488
if CONFIG_TARGET_MIN_VALUE in data and data[CONFIG_TARGET_MIN_VALUE] is not None:
8589
if isinstance(data[CONFIG_TARGET_MIN_VALUE], float) == False:
8690
matches = re.search(REGEX_VALUE, data[CONFIG_TARGET_MIN_VALUE])
8791
if matches is None:
8892
errors[CONFIG_TARGET_MIN_VALUE] = "invalid_value"
8993
else:
90-
data[CONFIG_TARGET_MIN_VALUE] = float(data[CONFIG_TARGET_MIN_VALUE])
94+
minimum_value = float(data[CONFIG_TARGET_MIN_VALUE])
95+
data[CONFIG_TARGET_MIN_VALUE] = minimum_value
96+
else:
97+
minimum_value = data[CONFIG_TARGET_MIN_VALUE]
9198

99+
maximum_value: float | None = None
92100
if CONFIG_TARGET_MAX_VALUE in data and data[CONFIG_TARGET_MAX_VALUE] is not None:
93101
if isinstance(data[CONFIG_TARGET_MAX_VALUE], float) == False:
94102
matches = re.search(REGEX_VALUE, data[CONFIG_TARGET_MAX_VALUE])
95103
if matches is None:
96104
errors[CONFIG_TARGET_MAX_VALUE] = "invalid_value"
97105
else:
98-
data[CONFIG_TARGET_MAX_VALUE] = float(data[CONFIG_TARGET_MAX_VALUE])
106+
maximum_value = float(data[CONFIG_TARGET_MAX_VALUE])
107+
data[CONFIG_TARGET_MAX_VALUE] = maximum_value
108+
else:
109+
maximum_value = data[CONFIG_TARGET_MAX_VALUE]
110+
111+
if minimum_value is not None and maximum_value is not None and minimum_value > maximum_value:
112+
errors[CONFIG_TARGET_MIN_VALUE] = "minimum_value_not_less_than_maximum_value"
99113

100114
if CONFIG_TARGET_WEIGHTING in data and data[CONFIG_TARGET_WEIGHTING] is not None:
101115
matches = re.search(REGEX_WEIGHTING, data[CONFIG_TARGET_WEIGHTING])
@@ -122,4 +136,25 @@ def validate_rolling_target_timeframe_config(data):
122136
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]:
123137
errors[CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD] = "look_ahead_hours_not_long_enough"
124138

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+
125160
return errors

0 commit comments

Comments
 (0)