Adapt next step based on filter type

This commit is contained in:
G Johansson 2024-07-10 07:55:00 +00:00
parent 2e8f7c1998
commit cb04224b3f
3 changed files with 250 additions and 112 deletions

View file

@ -22,7 +22,6 @@ from homeassistant.helpers.selector import (
NumberSelector,
NumberSelectorConfig,
NumberSelectorMode,
Selector,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
@ -63,85 +62,9 @@ FILTERS = [
]
async def get_options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
"""Return schema for options."""
filter_schema: dict[vol.Marker, Selector] = {}
if handler.options[CONF_FILTER_NAME] == FILTER_NAME_OUTLIER:
filter_schema = {
vol.Optional(
CONF_FILTER_WINDOW_SIZE, default=DEFAULT_WINDOW_SIZE
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
vol.Optional(
CONF_FILTER_RADIUS, default=DEFAULT_FILTER_RADIUS
): NumberSelector(
NumberSelectorConfig(min=0, step="any", mode=NumberSelectorMode.BOX)
),
}
elif handler.options[CONF_FILTER_NAME] == FILTER_NAME_LOWPASS:
filter_schema = {
vol.Optional(
CONF_FILTER_WINDOW_SIZE, default=DEFAULT_WINDOW_SIZE
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
vol.Optional(
CONF_FILTER_TIME_CONSTANT, default=DEFAULT_FILTER_TIME_CONSTANT
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
}
elif handler.options[CONF_FILTER_NAME] == FILTER_NAME_RANGE:
filter_schema = {
vol.Optional(CONF_FILTER_LOWER_BOUND): NumberSelector(
NumberSelectorConfig(min=0, step="any", mode=NumberSelectorMode.BOX)
),
vol.Optional(CONF_FILTER_UPPER_BOUND): NumberSelector(
NumberSelectorConfig(min=0, step="any", mode=NumberSelectorMode.BOX)
),
}
elif handler.options[CONF_FILTER_NAME] == FILTER_NAME_TIME_SMA:
filter_schema = {
vol.Optional(CONF_TIME_SMA_TYPE, default=TIME_SMA_LAST): SelectSelector(
SelectSelectorConfig(
options=[TIME_SMA_LAST],
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_TIME_SMA_TYPE,
)
),
vol.Required(CONF_FILTER_WINDOW_SIZE): DurationSelector(
DurationSelectorConfig(enable_day=False, allow_negative=False)
),
}
elif handler.options[CONF_FILTER_NAME] == FILTER_NAME_THROTTLE:
filter_schema = {
vol.Optional(
CONF_FILTER_WINDOW_SIZE, default=DEFAULT_WINDOW_SIZE
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
}
elif handler.options[CONF_FILTER_NAME] == FILTER_NAME_TIME_THROTTLE:
filter_schema = {
vol.Required(CONF_FILTER_WINDOW_SIZE): DurationSelector(
DurationSelectorConfig(enable_day=False, allow_negative=False)
),
}
base_schema: dict[vol.Marker, Selector] = {
vol.Optional(CONF_FILTER_PRECISION, default=DEFAULT_PRECISION): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
)
}
return vol.Schema({**filter_schema, **base_schema})
async def get_next_step(user_input: dict[str, Any]) -> str:
"""Return next step for options."""
return cast(str, user_input[CONF_FILTER_NAME])
async def validate_options(
@ -181,21 +104,130 @@ DATA_SCHEMA_SETUP = vol.Schema(
}
)
BASE_OPTIONS_SCHEMA = {
vol.Optional(CONF_FILTER_PRECISION, default=DEFAULT_PRECISION): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
)
}
OUTLIER_SCHEMA = vol.Schema(
{
vol.Optional(
CONF_FILTER_WINDOW_SIZE, default=DEFAULT_WINDOW_SIZE
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
vol.Optional(CONF_FILTER_RADIUS, default=DEFAULT_FILTER_RADIUS): NumberSelector(
NumberSelectorConfig(min=0, step="any", mode=NumberSelectorMode.BOX)
),
}
).extend(BASE_OPTIONS_SCHEMA)
LOWPASS_SCHEMA = vol.Schema(
{
vol.Optional(
CONF_FILTER_WINDOW_SIZE, default=DEFAULT_WINDOW_SIZE
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
vol.Optional(
CONF_FILTER_TIME_CONSTANT, default=DEFAULT_FILTER_TIME_CONSTANT
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
}
).extend(BASE_OPTIONS_SCHEMA)
RANGE_SCHEMA = vol.Schema(
{
vol.Optional(CONF_FILTER_LOWER_BOUND): NumberSelector(
NumberSelectorConfig(min=0, step="any", mode=NumberSelectorMode.BOX)
),
vol.Optional(CONF_FILTER_UPPER_BOUND): NumberSelector(
NumberSelectorConfig(min=0, step="any", mode=NumberSelectorMode.BOX)
),
}
).extend(BASE_OPTIONS_SCHEMA)
TIME_SMA_SCHEMA = vol.Schema(
{
vol.Optional(CONF_TIME_SMA_TYPE, default=TIME_SMA_LAST): SelectSelector(
SelectSelectorConfig(
options=[TIME_SMA_LAST],
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_TIME_SMA_TYPE,
)
),
vol.Required(CONF_FILTER_WINDOW_SIZE): DurationSelector(
DurationSelectorConfig(enable_day=False, allow_negative=False)
),
}
).extend(BASE_OPTIONS_SCHEMA)
THROTTLE_SCHEMA = vol.Schema(
{
vol.Optional(
CONF_FILTER_WINDOW_SIZE, default=DEFAULT_WINDOW_SIZE
): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
),
}
).extend(BASE_OPTIONS_SCHEMA)
TIME_THROTTLE_SCHEMA = vol.Schema(
{
vol.Required(CONF_FILTER_WINDOW_SIZE): DurationSelector(
DurationSelectorConfig(enable_day=False, allow_negative=False)
),
}
).extend(BASE_OPTIONS_SCHEMA)
CONFIG_FLOW = {
"user": SchemaFlowFormStep(
schema=DATA_SCHEMA_SETUP,
next_step="options",
next_step=get_next_step,
),
"options": SchemaFlowFormStep(
schema=get_options_schema,
validate_user_input=validate_options,
"lowpass": SchemaFlowFormStep(
schema=LOWPASS_SCHEMA, validate_user_input=validate_options
),
"outlier": SchemaFlowFormStep(
schema=OUTLIER_SCHEMA, validate_user_input=validate_options
),
"range": SchemaFlowFormStep(
schema=RANGE_SCHEMA, validate_user_input=validate_options
),
"time_simple_moving_average": SchemaFlowFormStep(
schema=TIME_SMA_SCHEMA, validate_user_input=validate_options
),
"throttle": SchemaFlowFormStep(
schema=THROTTLE_SCHEMA, validate_user_input=validate_options
),
"time_throttle": SchemaFlowFormStep(
schema=TIME_THROTTLE_SCHEMA, validate_user_input=validate_options
),
}
OPTIONS_FLOW = {
"init": SchemaFlowFormStep(
get_options_schema,
validate_user_input=validate_options,
schema=None,
next_step=get_next_step,
),
"lowpass": SchemaFlowFormStep(
schema=LOWPASS_SCHEMA, validate_user_input=validate_options
),
"outlier": SchemaFlowFormStep(
schema=OUTLIER_SCHEMA, validate_user_input=validate_options
),
"range": SchemaFlowFormStep(
schema=RANGE_SCHEMA, validate_user_input=validate_options
),
"time_simple_moving_average": SchemaFlowFormStep(
schema=TIME_SMA_SCHEMA, validate_user_input=validate_options
),
"throttle": SchemaFlowFormStep(
schema=THROTTLE_SCHEMA, validate_user_input=validate_options
),
"time_throttle": SchemaFlowFormStep(
schema=TIME_THROTTLE_SCHEMA, validate_user_input=validate_options
),
}

View file

@ -17,26 +17,79 @@
"filter": "Select filter to configure."
}
},
"options": {
"outlier": {
"description": "Read the documentation for further details on how to configure the filter sensor using these options.",
"data": {
"window_size": "Window size",
"precision": "Precision",
"radius": "Radius",
"time_constant": "Time constant",
"lower_bound": "Lower bound",
"upper_bound": "Upper bound",
"type": "Type"
"radius": "Radius"
},
"data_description": {
"window_size": "Size of the window of previous states.",
"precision": "Defines the number of decimal places of the calculated sensor value.",
"radius": "Band radius from median of previous states.",
"time_constant": "Loosely relates to the amount of time it takes for a state to influence the output.",
"radius": "Band radius from median of previous states."
}
},
"lowpass": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"time_constant": "Time constant"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"time_constant": "Loosely relates to the amount of time it takes for a state to influence the output."
}
},
"range": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"lower_bound": "Lower bound",
"upper_bound": "Upper bound"
},
"data_description": {
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"lower_bound": "Lower bound for filter range.",
"upper_bound": "Upper bound for filter range.",
"upper_bound": "Upper bound for filter range."
}
},
"time_simple_moving_average": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"type": "Type"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"type": "Defines the type of Simple Moving Average."
}
},
"throttle": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]"
}
},
"time_throttle": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]"
}
}
}
},
@ -45,25 +98,78 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
},
"step": {
"init": {
"description": "[%key:component::filter::config::step::options::description%]",
"outlier": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::options::data::window_size%]",
"precision": "[%key:component::filter::config::step::options::data::precision%]",
"radius": "[%key:component::filter::config::step::options::data::radius%]",
"time_constant": "[%key:component::filter::config::step::options::data::time_constant%]",
"lower_bound": "[%key:component::filter::config::step::options::data::lower_bound%]",
"upper_bound": "[%key:component::filter::config::step::options::data::upper_bound%]",
"type": "[%key:component::filter::config::step::options::data::type%]"
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"radius": "[%key:component::filter::config::step::outlier::data::radius%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::options::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::options::data_description::precision%]",
"radius": "[%key:component::filter::config::step::options::data_description::radius%]",
"time_constant": "[%key:component::filter::config::step::options::data_description::time_constant%]",
"lower_bound": "[%key:component::filter::config::step::options::data_description::lower_bound%]",
"upper_bound": "[%key:component::filter::config::step::options::data_description::upper_bound%]",
"type": "[%key:component::filter::config::step::options::data_description::type%]"
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"radius": "[%key:component::filter::config::step::outlier::data_description::radius%]"
}
},
"lowpass": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"time_constant": "[%key:component::filter::config::step::lowpass::data::time_constant%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"time_constant": "[%key:component::filter::config::step::lowpass::data_description::time_constant%]"
}
},
"range": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"lower_bound": "[%key:component::filter::config::step::range::data::lower_bound%]",
"upper_bound": "[%key:component::filter::config::step::range::data::upper_bound%]"
},
"data_description": {
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"lower_bound": "[%key:component::filter::config::step::range::data_description::lower_bound%]",
"upper_bound": "[%key:component::filter::config::step::range::data_description::upper_bound%]"
}
},
"time_simple_moving_average": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]",
"type": "[%key:component::filter::config::step::time_simple_moving_average::data::type%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]",
"type": "[%key:component::filter::config::step::time_simple_moving_average::data_description::type%]"
}
},
"throttle": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]"
}
},
"time_throttle": {
"description": "[%key:component::filter::config::step::outlier::description%]",
"data": {
"window_size": "[%key:component::filter::config::step::outlier::data::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data::precision%]"
},
"data_description": {
"window_size": "[%key:component::filter::config::step::outlier::data_description::window_size%]",
"precision": "[%key:component::filter::config::step::outlier::data_description::precision%]"
}
}
}

View file

@ -162,7 +162,7 @@ async def test_options_flow(
result = await hass.config_entries.options.async_init(loaded_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
assert result["step_id"] == "outlier"
result = await hass.config_entries.options.async_configure(
result["flow_id"],