Skip to content

Commit f3f3e12

Browse files
Merge pull request #55 from BottlecapDave/develop
Next release
2 parents ca5f24d + 45f1086 commit f3f3e12

File tree

8 files changed

+206
-16
lines changed

8 files changed

+206
-16
lines changed

_docs/blueprints.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,9 @@ This blueprint will provide the data source for Octopus Energy rates as provided
4747
!!! warning
4848

4949
This automation will run when any of the underlying entities update. This make take a while initially. If you want the data available immediately, then you'll need to run the automation manually.
50+
51+
### Weather
52+
53+
[Install blueprint](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fbottlecapdave.github.io%2FHomeAssistant-TargetTimeframes%2Fblueprints%2Ftarget_timeframes_weather.yaml) | [Source](./blueprints/target_timeframes_weather.yaml)
54+
55+
This blueprint will provide the data source based on the forecast of the provide weather sensor and will use the specified `Forecast attribute` as the value for the calculation of the target timeframes. The forecast will be refreshed every 30 minutes.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
blueprint:
2+
name: Target Timeframes - Weather source
3+
description: Configures a target timeframe data source from Weather
4+
domain: automation
5+
author: BottlecapDave
6+
input:
7+
target_timeframe_data_source_sensor:
8+
name: Target timeframe data source sensor
9+
description: The data source sensor which represents the data source to update
10+
selector:
11+
entity:
12+
filter:
13+
- domain:
14+
- sensor
15+
integration: target_timeframes
16+
multiple: false
17+
weather_sensor:
18+
name: Weather sensor
19+
description: The weather sensor to get the forecast for.
20+
selector:
21+
entity:
22+
filter:
23+
- domain:
24+
- weather
25+
multiple: false
26+
forecast_attribute:
27+
name: Forecast attribute
28+
description: Type in the name of the desired forecast attribute to use as the value (e.g. "precipitation", "uv_index" or "temperature"). See the metadata of the result for possible options
29+
default: "condition"
30+
selector:
31+
text:
32+
variables:
33+
target_timeframe_data_source_sensor: !input target_timeframe_data_source_sensor
34+
weather_sensor: !input weather_sensor
35+
forecast_attribute: !input forecast_attribute
36+
millisecond_jitter: >
37+
{{ range(1, 1000) | random }}
38+
mode: queued
39+
max: 4
40+
triggers:
41+
- trigger: time_pattern
42+
minutes: '/30'
43+
condition: []
44+
action:
45+
# Add a bit of jitter so the API isn't hit at once
46+
- delay:
47+
milliseconds: >
48+
{{ millisecond_jitter }}
49+
- action: weather.get_forecasts
50+
target:
51+
entity_id: !input weather_sensor
52+
data:
53+
type: hourly
54+
response_variable: weather_forecast
55+
- variables:
56+
data_source_data: >
57+
{%- set forecast_items = weather_forecast[weather_sensor]["forecast"] -%}
58+
{%- set data = namespace(items=[]) -%}
59+
{%- for forecast in forecast_items -%}
60+
{%- set start = forecast["datetime"] | as_timestamp | timestamp_utc -%}
61+
{%- set end = ((start | as_datetime) + timedelta(minutes=30)) | as_timestamp | timestamp_utc -%}
62+
{%- set value = forecast[forecast_attribute] | float -%}
63+
{%- set new_item = [{ 'start': start , 'end': end, 'value': value, 'metadata': forecast }] -%}
64+
65+
{%- set data.items = data.items + new_item -%}
66+
67+
{%- set start = end -%}
68+
{%- set end = ((start | as_datetime) + timedelta(minutes=30)) | as_timestamp | timestamp_utc -%}
69+
{%- set new_item = [{ 'start': start , 'end': end, 'value': value, 'metadata': forecast }] -%}
70+
71+
{%- set data.items = data.items + new_item -%}
72+
{%- endfor -%}
73+
{{ data.items }}
74+
- action: target_timeframes.update_target_timeframe_data_source
75+
data: >
76+
{{ { 'data': data_source_data } }}
77+
target:
78+
entity_id: !input target_timeframe_data_source_sensor

_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": {

0 commit comments

Comments
 (0)