Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions _docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@

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.

## 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?

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).

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`).

In this scenario, you have two options.

1. The recommended approach would be to adjust the time period that the target timeframe looks at. See below for example suggestions

| Data Source | Standard Target Timeframe Recommendation | Rolling Target Timeframe Recommendation |
|-|-|-|
| 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 |

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.

## How do I increase the logs for the integration?

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.
Expand Down
31 changes: 22 additions & 9 deletions _docs/setup/rolling_target_timeframe.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,34 @@ There may be times that you want the target timeframe sensors to not take into a

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.

### Weighting
### Weighting/Multipliers

!!! info

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

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.
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.

* `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.
* `*,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.
* `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.
* `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.
* `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.
* `*,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.
* `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.
* `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.

Each slot weighting must be a whole number or decimal number and be positive.
Each slot weighting/multiplier must be a whole number or decimal number and be positive.

You can also use weightings to ignore slots. This can be done by assigning a value of 0 for the desired slot.
You can also use weightings/multipliers to ignore slots. This can be done by assigning a value of 0 for the desired slot.

### Dangerous settings

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.

#### Calculate with incomplete data

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).

#### Minimum required minutes in slots

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.

## Attributes

Expand Down Expand Up @@ -141,6 +153,7 @@ The following attributes are available on each sensor
| `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. |
| `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. |
| `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. |
| `calculate_with_incomplete_data` | boolean | Determines if calculations should occur when there isn't enough data to satisfy the look ahead hours |

## Services

Expand Down
34 changes: 26 additions & 8 deletions _docs/setup/target_timeframe.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,38 @@ There may be times that you want the target timeframe sensors to not take into a

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.

### Weighting
### Weighting/Multipliers

!!! info

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

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.
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.

* `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.
* `*,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.
* `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.
* `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.
* `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.
* `*,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.
* `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.
* `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.

Each slot weighting must be a whole number or decimal number and be positive.
Each slot weighting/multiplier must be a whole number or decimal number and be positive.

You can also use weightings to ignore slots. This can be done by assigning a value of 0 for the desired slot.
You can also use weightings/multipliers to ignore slots. This can be done by assigning a value of 0 for the desired slot.

### Dangerous settings

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.

#### Calculate with incomplete data

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).

#### Minimum required minutes in slots

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.

!!! warn

Changing this can cause sensors to not come on for the correct amount of time by up to 30 minutes.

## Attributes

Expand Down Expand Up @@ -154,6 +170,8 @@ The following attributes are available on each sensor
| `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. |
| `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. |
| `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. |
| `calculate_with_incomplete_data` | boolean | Determines if calculations should occur when there isn't enough data to satisfy the look ahead hours |
| `minimum_required_minutes_in_slot` | integer | Determines the configured minimum number of minutes to be present in a slot for it to be considered |

## Services

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

from ..const import (
CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD,
CONFIG_TARGET_DANGEROUS_SETTINGS,
CONFIG_TARGET_HOURS,
CONFIG_TARGET_HOURS_MODE,
CONFIG_TARGET_HOURS_MODE_EXACT,
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_TYPE,
CONFIG_TARGET_TYPE_CONTINUOUS,
CONFIG_TARGET_WEIGHTING,
REGEX_ENTITY_NAME,
REGEX_HOURS,
REGEX_INTEGER,
REGEX_OFFSET_PARTS,
REGEX_VALUE,
REGEX_WEIGHTING
Expand Down Expand Up @@ -81,21 +84,32 @@ def validate_rolling_target_timeframe_config(data):
if matches is None:
errors[CONFIG_TARGET_OFFSET] = "invalid_offset"

minimum_value: float | None = None
if CONFIG_TARGET_MIN_VALUE in data and data[CONFIG_TARGET_MIN_VALUE] is not None:
if isinstance(data[CONFIG_TARGET_MIN_VALUE], float) == False:
matches = re.search(REGEX_VALUE, data[CONFIG_TARGET_MIN_VALUE])
if matches is None:
errors[CONFIG_TARGET_MIN_VALUE] = "invalid_value"
else:
data[CONFIG_TARGET_MIN_VALUE] = float(data[CONFIG_TARGET_MIN_VALUE])
minimum_value = float(data[CONFIG_TARGET_MIN_VALUE])
data[CONFIG_TARGET_MIN_VALUE] = minimum_value
else:
minimum_value = data[CONFIG_TARGET_MIN_VALUE]

maximum_value: float | None = None
if CONFIG_TARGET_MAX_VALUE in data and data[CONFIG_TARGET_MAX_VALUE] is not None:
if isinstance(data[CONFIG_TARGET_MAX_VALUE], float) == False:
matches = re.search(REGEX_VALUE, data[CONFIG_TARGET_MAX_VALUE])
if matches is None:
errors[CONFIG_TARGET_MAX_VALUE] = "invalid_value"
else:
data[CONFIG_TARGET_MAX_VALUE] = float(data[CONFIG_TARGET_MAX_VALUE])
maximum_value = float(data[CONFIG_TARGET_MAX_VALUE])
data[CONFIG_TARGET_MAX_VALUE] = maximum_value
else:
maximum_value = data[CONFIG_TARGET_MAX_VALUE]

if minimum_value is not None and maximum_value is not None and minimum_value > maximum_value:
errors[CONFIG_TARGET_MIN_VALUE] = "minimum_value_not_less_than_maximum_value"

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

minimum_required_minutes_in_slot: int | None = None
if (CONFIG_TARGET_DANGEROUS_SETTINGS in data and
CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT in data[CONFIG_TARGET_DANGEROUS_SETTINGS] and
data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT] is not None):

if isinstance(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT], int) == False:
if isinstance(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT], str):
matches = re.search(REGEX_INTEGER, data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT])
if matches is None:
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_integer"
else:
minimum_required_minutes_in_slot = int(data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT])
data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT] = minimum_required_minutes_in_slot
else:
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_integer"
else:
minimum_required_minutes_in_slot = data[CONFIG_TARGET_DANGEROUS_SETTINGS][CONFIG_TARGET_MINIMUM_REQUIRED_MINUTES_IN_SLOT]

if minimum_required_minutes_in_slot is not None and (minimum_required_minutes_in_slot < 1 or minimum_required_minutes_in_slot > 30):
errors[CONFIG_TARGET_DANGEROUS_SETTINGS] = "invalid_minimum_required_minutes_in_slot"

return errors
Loading