Skip to content

Commit d146fb4

Browse files
committed
fix: Fixed updating configuration when only some values are updated
1 parent c09d012 commit d146fb4

File tree

11 files changed

+136
-114
lines changed

11 files changed

+136
-114
lines changed

_docs/services.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ actions:
104104
- action: target_timeframes.update_target_timeframe_config
105105
data:
106106
target_hours: >
107-
"{{ states('input_number.target_timeframes_target_hours') | string }}"
107+
{{ states('input_number.target_timeframes_target_hours') }}
108108
target_start_time: >
109109
{{ states('input_text.target_timeframes_target_from') }}
110110
target_end_time: >
@@ -171,9 +171,9 @@ actions:
171171
- action: target_timeframes.update_target_timeframe_config
172172
data:
173173
target_hours: >
174-
"{{ states('input_number.target_timeframes_target_hours') | string }}"
174+
{{ states('input_number.target_timeframes_target_hours') }}
175175
target_look_ahead_hours: >
176-
"{{ states('input_number.target_timeframes_rolling_target_look_ahead_hours') | string }}"
176+
{{ states('input_number.target_timeframes_rolling_target_look_ahead_hours') }}
177177
target_offset: >
178178
{{ states('input_text.target_timeframes_target_offset') }}
179179
target:

custom_components/target_timeframes/binary_sensor.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ async def async_setup_entry(hass, entry, async_add_entities):
5050
vol.All(
5151
cv.make_entity_service_schema(
5252
{
53-
vol.Optional("target_hours"): str,
53+
vol.Optional("target_hours"): vol.Coerce(float),
5454
vol.Optional("target_start_time"): str,
5555
vol.Optional("target_end_time"): str,
5656
vol.Optional("target_offset"): str,
57-
vol.Optional("target_minimum_value"): str,
58-
vol.Optional("target_maximum_value"): str,
57+
vol.Optional("target_minimum_value"): vol.Coerce(float),
58+
vol.Optional("target_maximum_value"): vol.Coerce(float),
5959
vol.Optional("target_weighting"): str,
6060
vol.Optional("persist_changes"): bool,
6161
},
@@ -65,19 +65,19 @@ async def async_setup_entry(hass, entry, async_add_entities):
6565
"target_hours", "target_start_time", "target_end_time", "target_offset", "target_minimum_value", "target_maximum_value"
6666
),
6767
),
68-
"async_update_target_rate_config",
68+
"async_update_target_timeframe_config",
6969
)
7070
else:
7171
platform.async_register_entity_service(
7272
"update_rolling_target_timeframe_config",
7373
vol.All(
7474
cv.make_entity_service_schema(
7575
{
76-
vol.Optional("target_hours"): str,
77-
vol.Optional("target_look_ahead_hours"): str,
76+
vol.Optional("target_hours"): vol.Coerce(float),
77+
vol.Optional("target_look_ahead_hours"): vol.Coerce(float),
7878
vol.Optional("target_offset"): str,
79-
vol.Optional("target_minimum_value"): str,
80-
vol.Optional("target_maximum_value"): str,
79+
vol.Optional("target_minimum_value"): vol.Coerce(float),
80+
vol.Optional("target_maximum_value"): vol.Coerce(float),
8181
vol.Optional("target_weighting"): str,
8282
vol.Optional("persist_changes"): bool,
8383
},
@@ -87,7 +87,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
8787
"target_hours", "target_look_ahead_hours", "target_offset", "target_minimum_value", "target_maximum_value"
8888
),
8989
),
90-
"async_update_rolling_target_rate_config",
90+
"async_update_rolling_target_timeframe_config",
9191
)
9292

9393
async_add_entities(entities, config_subentry_id=sub_entry_id)

custom_components/target_timeframes/config/rolling_target_timeframe.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ async def async_migrate_rolling_target_timeframe_config(version: int, data: {},
2626

2727
return new_data
2828

29-
def merge_rolling_target_rate_config(data: dict, options: dict, updated_config: dict = None):
29+
def merge_rolling_target_timeframe_config(data: dict, updated_config: dict = None):
3030
config = dict(data)
31-
if options is not None:
32-
config.update(options)
3331

3432
if updated_config is not None:
3533
config.update(updated_config)

custom_components/target_timeframes/config/target_timeframe.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,30 @@ async def async_migrate_target_timeframe_config(version: int, data: {}, get_entr
3232

3333
return new_data
3434

35-
def merge_target_timeframe_config(data: dict, options: dict, updated_config: dict = None):
35+
def merge_target_timeframe_config(data: dict, updated_config: dict = None):
3636
config = dict(data)
37-
if options is not None:
38-
config.update(options)
3937

4038
if updated_config is not None:
4139
config.update(updated_config)
4240

41+
if CONFIG_TARGET_START_TIME not in updated_config and CONFIG_TARGET_START_TIME in config:
42+
config[CONFIG_TARGET_START_TIME] = None
43+
44+
if CONFIG_TARGET_END_TIME not in updated_config and CONFIG_TARGET_END_TIME in config:
45+
config[CONFIG_TARGET_END_TIME] = None
46+
47+
if CONFIG_TARGET_OFFSET not in updated_config and CONFIG_TARGET_OFFSET in config:
48+
config[CONFIG_TARGET_OFFSET] = None
49+
50+
if CONFIG_TARGET_MIN_VALUE not in updated_config and CONFIG_TARGET_MIN_VALUE in config:
51+
config[CONFIG_TARGET_MIN_VALUE] = None
52+
53+
if CONFIG_TARGET_MAX_VALUE not in updated_config and CONFIG_TARGET_MAX_VALUE in config:
54+
config[CONFIG_TARGET_MAX_VALUE] = None
55+
56+
if CONFIG_TARGET_WEIGHTING not in updated_config and CONFIG_TARGET_WEIGHTING in config:
57+
config[CONFIG_TARGET_WEIGHTING] = None
58+
4359
return config
4460

4561
def is_time_frame_long_enough(hours, start_time, end_time):

custom_components/target_timeframes/config_flow.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
from homeassistant.config_entries import (ConfigFlow, ConfigEntry, ConfigSubentryFlow, SubentryFlowResult)
33
from homeassistant.core import callback
44

5-
from .config.target_timeframe import validate_target_timeframe_config
6-
from .config.rolling_target_timeframe import validate_rolling_target_timeframe_config
5+
from .config.target_timeframe import merge_target_timeframe_config, validate_target_timeframe_config
6+
from .config.rolling_target_timeframe import merge_rolling_target_timeframe_config, validate_rolling_target_timeframe_config
77

88
from .const import (
99
CONFIG_DATA_SOURCE_ID,
@@ -50,25 +50,30 @@ async def async_step_user(self, user_input):
5050
)
5151

5252
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
53+
config = dict()
54+
config.update(self._get_reconfigure_entry().data)
55+
5356
if user_input is not None:
54-
errors = validate_source_config(user_input)
57+
config.update(user_input)
58+
errors = validate_source_config(config)
5559

5660
# Setup our basic sensors
5761
if len(errors) < 1:
58-
await self.async_set_unique_id(user_input[CONFIG_DATA_SOURCE_ID])
62+
await self.async_set_unique_id(config[CONFIG_DATA_SOURCE_ID])
5963
self._abort_if_unique_id_mismatch()
6064

6165
return self.async_update_reload_and_abort(
6266
self._get_reconfigure_entry(),
63-
data_updates=user_input,
67+
data_updates=config,
6468
)
6569

6670
return self.async_show_form(
6771
step_id="reconfigure",
6872
data_schema=self.add_suggested_values_to_schema(
6973
DATA_SCHEMA_SOURCE,
70-
user_input if user_input is not None else self._get_reconfigure_entry().data
74+
config
7175
),
76+
errors=errors
7277
)
7378

7479
@classmethod
@@ -108,8 +113,8 @@ async def async_step_user(
108113
)
109114

110115
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
111-
config = dict(user_input) if user_input is not None else None
112-
errors = validate_target_timeframe_config(config) if config is not None else {}
116+
config = merge_target_timeframe_config(self._get_reconfigure_subentry().data, user_input)
117+
errors = validate_target_timeframe_config(config)
113118

114119
if len(errors) < 1 and user_input is not None:
115120
return self.async_update_and_abort(
@@ -122,8 +127,9 @@ async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None)
122127
step_id="reconfigure",
123128
data_schema=self.add_suggested_values_to_schema(
124129
DATA_SCHEMA_TARGET_TIME_PERIOD,
125-
user_input if user_input is not None else self._get_reconfigure_subentry().data
130+
config
126131
),
132+
errors=errors
127133
)
128134

129135
class RollingTargetTimePeriodSubentryFlowHandler(ConfigSubentryFlow):
@@ -152,8 +158,8 @@ async def async_step_user(
152158
)
153159

154160
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
155-
config = dict(user_input) if user_input is not None else None
156-
errors = validate_rolling_target_timeframe_config(config) if config is not None else {}
161+
config = merge_rolling_target_timeframe_config(self._get_reconfigure_subentry().data, user_input)
162+
errors = validate_rolling_target_timeframe_config(config)
157163

158164
if len(errors) < 1 and user_input is not None:
159165
return self.async_update_and_abort(
@@ -166,6 +172,7 @@ async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None)
166172
step_id="reconfigure",
167173
data_schema=self.add_suggested_values_to_schema(
168174
DATA_SCHEMA_ROLLING_TARGET_TIME_PERIOD,
169-
user_input if user_input is not None else self._get_reconfigure_subentry().data
175+
config
170176
),
177+
errors=errors
171178
)

custom_components/target_timeframes/const.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676

7777
DATA_SCHEMA_TARGET_TIME_PERIOD = vol.Schema({
7878
vol.Required(CONFIG_TARGET_NAME): str,
79-
vol.Required(CONFIG_TARGET_HOURS): str,
79+
vol.Required(CONFIG_TARGET_HOURS): vol.Coerce(float),
8080
vol.Required(CONFIG_TARGET_HOURS_MODE, default=CONFIG_TARGET_HOURS_MODE_EXACT): selector.SelectSelector(
8181
selector.SelectSelectorConfig(
8282
options=[
@@ -111,14 +111,14 @@
111111
vol.Optional(CONFIG_TARGET_ROLLING_TARGET, default=False): bool,
112112
vol.Optional(CONFIG_TARGET_LATEST_VALUES, default=False): bool,
113113
vol.Optional(CONFIG_TARGET_FIND_HIGHEST_VALUES, default=False): bool,
114-
vol.Optional(CONFIG_TARGET_MIN_VALUE): str,
115-
vol.Optional(CONFIG_TARGET_MAX_VALUE): str,
114+
vol.Optional(CONFIG_TARGET_MIN_VALUE): vol.Coerce(float),
115+
vol.Optional(CONFIG_TARGET_MAX_VALUE): vol.Coerce(float),
116116
vol.Optional(CONFIG_TARGET_WEIGHTING): str,
117117
})
118118

119119
DATA_SCHEMA_ROLLING_TARGET_TIME_PERIOD = vol.Schema({
120120
vol.Required(CONFIG_TARGET_NAME): str,
121-
vol.Required(CONFIG_TARGET_HOURS): str,
121+
vol.Required(CONFIG_TARGET_HOURS): vol.Coerce(float),
122122
vol.Required(CONFIG_TARGET_HOURS_MODE, default=CONFIG_TARGET_HOURS_MODE_EXACT): selector.SelectSelector(
123123
selector.SelectSelectorConfig(
124124
options=[
@@ -138,7 +138,7 @@
138138
mode=selector.SelectSelectorMode.DROPDOWN,
139139
)
140140
),
141-
vol.Required(CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD): str,
141+
vol.Required(CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD): vol.Coerce(float),
142142
vol.Optional(CONFIG_TARGET_OFFSET): str,
143143
vol.Required(CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE, default=CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST): selector.SelectSelector(
144144
selector.SelectSelectorConfig(
@@ -152,8 +152,8 @@
152152
),
153153
vol.Optional(CONFIG_TARGET_LATEST_VALUES): bool,
154154
vol.Optional(CONFIG_TARGET_FIND_HIGHEST_VALUES): bool,
155-
vol.Optional(CONFIG_TARGET_MIN_VALUE): str,
156-
vol.Optional(CONFIG_TARGET_MAX_VALUE): str,
155+
vol.Optional(CONFIG_TARGET_MIN_VALUE): vol.Coerce(float),
156+
vol.Optional(CONFIG_TARGET_MAX_VALUE): vol.Coerce(float),
157157
vol.Optional(CONFIG_TARGET_WEIGHTING): str,
158158
})
159159

custom_components/target_timeframes/entities/rolling_target_timeframe.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -246,57 +246,51 @@ async def async_added_to_hass(self):
246246
)
247247

248248
@callback
249-
async def async_update_rolling_target_rate_config(self, target_hours=None, target_look_ahead_hours=None, target_offset=None, target_minimum_value=None, target_maximum_value=None, target_weighting=None, persist_changes=False):
249+
async def async_update_rolling_target_timeframe_config(self, target_hours=None, target_look_ahead_hours=None, target_offset=None, target_minimum_value=None, target_maximum_value=None, target_weighting=None, persist_changes=False):
250250
"""Update sensors config"""
251251

252252
config = dict(self._config)
253253
if target_hours is not None:
254254
# Inputs from automations can include quotes, so remove these
255-
trimmed_target_hours = target_hours.strip('\"')
256255
config.update({
257-
CONFIG_TARGET_HOURS: trimmed_target_hours
256+
CONFIG_TARGET_HOURS: target_hours if isinstance(target_hours, str) == False else (target_hours.strip('\"') if target_hours != "" else None)
258257
})
259258

260259
if target_look_ahead_hours is not None:
261260
# Inputs from automations can include quotes, so remove these
262-
trimmed_target_look_ahead_hours = target_look_ahead_hours.strip('\"')
263261
config.update({
264-
CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD: trimmed_target_look_ahead_hours
262+
CONFIG_ROLLING_TARGET_HOURS_LOOK_AHEAD: target_look_ahead_hours if isinstance(target_look_ahead_hours, str) == False else (target_look_ahead_hours.strip('\"') if target_look_ahead_hours != "" else None)
265263
})
266264

267265
if target_offset is not None:
268266
# Inputs from automations can include quotes, so remove these
269-
trimmed_target_offset = target_offset.strip('\"')
270267
config.update({
271-
CONFIG_TARGET_OFFSET: trimmed_target_offset
268+
CONFIG_TARGET_OFFSET: target_offset if isinstance(target_offset, str) == False else (target_offset.strip('\"') if target_offset != "" else None)
272269
})
273270

274271
if target_minimum_value is not None:
275272
# Inputs from automations can include quotes, so remove these
276-
trimmed_target_minimum_value = target_minimum_value.strip('\"')
277273
config.update({
278-
CONFIG_TARGET_MIN_VALUE: trimmed_target_minimum_value if trimmed_target_minimum_value != "" else None
274+
CONFIG_TARGET_MIN_VALUE: target_minimum_value if isinstance(target_minimum_value, str) == False else (target_minimum_value.strip('\"') if target_minimum_value != "" else None)
279275
})
280276

281277
if target_maximum_value is not None:
282278
# Inputs from automations can include quotes, so remove these
283-
trimmed_target_maximum_value = target_maximum_value.strip('\"')
284279
config.update({
285-
CONFIG_TARGET_MAX_VALUE: trimmed_target_maximum_value if trimmed_target_maximum_value != "" else None
280+
CONFIG_TARGET_MAX_VALUE: target_maximum_value if isinstance(target_maximum_value, str) == False else (target_maximum_value.strip('\"') if target_maximum_value != "" else None)
286281
})
287282

288283
if target_weighting is not None:
289284
# Inputs from automations can include quotes, so remove these
290-
trimmed_target_weighting = target_weighting.strip('\"')
291285
config.update({
292-
CONFIG_TARGET_WEIGHTING: trimmed_target_weighting if trimmed_target_weighting != "" else None
286+
CONFIG_TARGET_WEIGHTING: target_weighting if isinstance(target_weighting, str) == False else (target_weighting.strip('\"') if target_weighting != "" else None)
293287
})
294288

295289
errors = validate_rolling_target_timeframe_config(config)
296290
keys = list(errors.keys())
297291
if (len(keys)) > 0:
298-
translations = await translation.async_get_translations(self._hass, self._hass.config.language, "options", {DOMAIN})
299-
raise vol.Invalid(translations[f'component.{DOMAIN}.options.error.{errors[keys[0]]}'])
292+
translations = await translation.async_get_translations(self._hass, self._hass.config.language, "config_subentries", {DOMAIN})
293+
raise vol.Invalid(translations[f'component.{DOMAIN}.config_subentries.rolling_target_time_period.error.{errors[keys[0]]}'])
300294

301295
self._config = config
302296
self._attributes = self._config.copy()

custom_components/target_timeframes/entities/target_timeframe.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -263,64 +263,57 @@ async def async_added_to_hass(self):
263263
)
264264

265265
@callback
266-
async def async_update_target_rate_config(self, target_start_time=None, target_end_time=None, target_hours=None, target_offset=None, target_minimum_value=None, target_maximum_value=None, target_weighting=None, persist_changes=False):
266+
async def async_update_target_timeframe_config(self, target_start_time=None, target_end_time=None, target_hours=None, target_offset=None, target_minimum_value=None, target_maximum_value=None, target_weighting=None, persist_changes=False):
267267
"""Update sensors config"""
268268

269269
config = dict(self._config)
270270
if target_hours is not None:
271271
# Inputs from automations can include quotes, so remove these
272-
trimmed_target_hours = target_hours.strip('\"')
273272
config.update({
274-
CONFIG_TARGET_HOURS: trimmed_target_hours
273+
CONFIG_TARGET_HOURS: target_hours if isinstance(target_hours, str) == False else (target_hours.strip('\"') if target_hours != "" else None)
275274
})
276275

277276
if target_start_time is not None:
278277
# Inputs from automations can include quotes, so remove these
279-
trimmed_target_start_time = target_start_time.strip('\"')
280278
config.update({
281-
CONFIG_TARGET_START_TIME: trimmed_target_start_time
279+
CONFIG_TARGET_START_TIME: target_start_time if isinstance(target_start_time, str) == False else (target_start_time.strip('\"') if target_start_time != "" else None)
282280
})
283281

284282
if target_end_time is not None:
285283
# Inputs from automations can include quotes, so remove these
286-
trimmed_target_end_time = target_end_time.strip('\"')
287284
config.update({
288-
CONFIG_TARGET_END_TIME: trimmed_target_end_time
285+
CONFIG_TARGET_END_TIME: target_end_time if isinstance(target_end_time, str) == False else (target_end_time.strip('\"') if target_end_time != "" else None)
289286
})
290287

291288
if target_offset is not None:
292289
# Inputs from automations can include quotes, so remove these
293-
trimmed_target_offset = target_offset.strip('\"')
294290
config.update({
295-
CONFIG_TARGET_OFFSET: trimmed_target_offset
291+
CONFIG_TARGET_OFFSET: target_offset if isinstance(target_offset, str) == False else (target_offset.strip('\"') if target_offset != "" else None)
296292
})
297293

298294
if target_minimum_value is not None:
299295
# Inputs from automations can include quotes, so remove these
300-
trimmed_target_minimum_value = target_minimum_value.strip('\"')
301296
config.update({
302-
CONFIG_TARGET_MIN_VALUE: trimmed_target_minimum_value if trimmed_target_minimum_value != "" else None
297+
CONFIG_TARGET_MIN_VALUE: target_minimum_value if isinstance(target_minimum_value, str) == False else (target_minimum_value.strip('\"') if target_minimum_value != "" else None)
303298
})
304299

305300
if target_maximum_value is not None:
306301
# Inputs from automations can include quotes, so remove these
307-
trimmed_target_maximum_value = target_maximum_value.strip('\"')
308302
config.update({
309-
CONFIG_TARGET_MAX_VALUE: trimmed_target_maximum_value if trimmed_target_maximum_value != "" else None
303+
CONFIG_TARGET_MAX_VALUE: target_maximum_value if isinstance(target_maximum_value, str) == False else (target_maximum_value.strip('\"') if target_maximum_value != "" else None)
310304
})
311305

312306
if target_weighting is not None:
313307
# Inputs from automations can include quotes, so remove these
314-
trimmed_target_weighting = target_weighting.strip('\"')
315308
config.update({
316-
CONFIG_TARGET_WEIGHTING: trimmed_target_weighting if trimmed_target_weighting != "" else None
309+
CONFIG_TARGET_WEIGHTING: target_weighting if isinstance(target_weighting, str) == False else (target_weighting.strip('\"') if target_weighting != "" else None)
317310
})
318311

319312
errors = validate_target_timeframe_config(config)
320313
keys = list(errors.keys())
321314
if (len(keys)) > 0:
322-
translations = await translation.async_get_translations(self._hass, self._hass.config.language, "options", {DOMAIN})
323-
raise vol.Invalid(translations[f'component.{DOMAIN}.options.error.{errors[keys[0]]}'])
315+
translations = await translation.async_get_translations(self._hass, self._hass.config.language, "config_subentries", {DOMAIN})
316+
raise vol.Invalid(translations[f'component.{DOMAIN}.config_subentries.target_time_period.error.{errors[keys[0]]}'])
324317

325318
self._config = config
326319
self._attributes = self._config.copy()

0 commit comments

Comments
 (0)