Add ability to edit sensors in scrape config flow (#82926)

* Add ability to edit sensors in scrape config flow

* Fix docstring

* Update homeassistant/components/scrape/config_flow.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* docstring

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
epenet 2022-11-30 14:16:28 +01:00 committed by GitHub
parent 92fef0f2ba
commit cbf8a41eed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 172 additions and 18 deletions

View File

@ -87,7 +87,6 @@ RESOURCE_SETUP = {
}
SENSOR_SETUP = {
vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(),
vol.Required(CONF_SELECT): TextSelector(),
vol.Optional(CONF_INDEX, default=0): NumberSelector(
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
@ -146,6 +145,49 @@ async def validate_sensor_setup(
return {}
async def validate_select_sensor(
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Store sensor index in flow state."""
handler.flow_state["_idx"] = int(user_input[CONF_INDEX])
return {}
async def get_select_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
"""Return schema for selecting a sensor."""
return vol.Schema(
{
vol.Required(CONF_INDEX): vol.In(
{
str(index): config[CONF_NAME]
for index, config in enumerate(handler.options[SENSOR_DOMAIN])
},
)
}
)
async def get_edit_sensor_suggested_values(
handler: SchemaCommonFlowHandler,
) -> dict[str, Any]:
"""Return suggested values for sensor editing."""
idx: int = handler.flow_state["_idx"]
return handler.options[SENSOR_DOMAIN][idx]
async def validate_sensor_edit(
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Update edited sensor."""
user_input[CONF_INDEX] = int(user_input[CONF_INDEX])
# Standard behavior is to merge the result with the options.
# In this case, we want to add a sub-item so we update the options directly.
idx: int = handler.flow_state["_idx"]
handler.options[SENSOR_DOMAIN][idx].update(user_input)
return {}
async def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
"""Return schema for sensor removal."""
return vol.Schema(
@ -183,7 +225,13 @@ async def validate_remove_sensor(
DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP)
DATA_SCHEMA_SENSOR = vol.Schema(SENSOR_SETUP)
DATA_SCHEMA_EDIT_SENSOR = vol.Schema(SENSOR_SETUP)
DATA_SCHEMA_SENSOR = vol.Schema(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(),
**SENSOR_SETUP,
}
)
CONFIG_FLOW = {
"user": SchemaFlowFormStep(
@ -197,7 +245,9 @@ CONFIG_FLOW = {
),
}
OPTIONS_FLOW = {
"init": SchemaFlowMenuStep(["resource", "add_sensor", "remove_sensor"]),
"init": SchemaFlowMenuStep(
["resource", "add_sensor", "select_edit_sensor", "remove_sensor"]
),
"resource": SchemaFlowFormStep(
DATA_SCHEMA_RESOURCE,
validate_user_input=validate_rest_setup,
@ -207,6 +257,17 @@ OPTIONS_FLOW = {
suggested_values=None,
validate_user_input=validate_sensor_setup,
),
"select_edit_sensor": SchemaFlowFormStep(
get_select_sensor_schema,
suggested_values=None,
validate_user_input=validate_select_sensor,
next_step="edit_sensor",
),
"edit_sensor": SchemaFlowFormStep(
DATA_SCHEMA_EDIT_SENSOR,
suggested_values=get_edit_sensor_suggested_values,
validate_user_input=validate_sensor_edit,
),
"remove_sensor": SchemaFlowFormStep(
get_remove_sensor_schema,
suggested_values=None,

View File

@ -54,6 +54,7 @@
"init": {
"menu_options": {
"add_sensor": "Add sensor",
"select_edit_sensor": "Configure sensor",
"remove_sensor": "Remove sensor",
"resource": "Configure resource"
}
@ -79,6 +80,27 @@
"unit_of_measurement": "[%key:component::scrape::config::step::sensor::data_description::unit_of_measurement%]"
}
},
"edit_sensor": {
"data": {
"name": "[%key:component::scrape::config::step::sensor::data::name%]",
"attribute": "[%key:component::scrape::config::step::sensor::data::attribute%]",
"index": "[%key:component::scrape::config::step::sensor::data::index%]",
"select": "[%key:component::scrape::config::step::sensor::data::select%]",
"value_template": "[%key:component::scrape::config::step::sensor::data::value_template%]",
"device_class": "[%key:component::scrape::config::step::sensor::data::device_class%]",
"state_class": "[%key:component::scrape::config::step::sensor::data::state_class%]",
"unit_of_measurement": "[%key:component::scrape::config::step::sensor::data::unit_of_measurement%]"
},
"data_description": {
"select": "[%key:component::scrape::config::step::sensor::data_description::select%]",
"attribute": "[%key:component::scrape::config::step::sensor::data_description::attribute%]",
"index": "[%key:component::scrape::config::step::sensor::data_description::index%]",
"value_template": "[%key:component::scrape::config::step::sensor::data_description::value_template%]",
"device_class": "[%key:component::scrape::config::step::sensor::data_description::device_class%]",
"state_class": "[%key:component::scrape::config::step::sensor::data_description::state_class%]",
"unit_of_measurement": "[%key:component::scrape::config::step::sensor::data_description::unit_of_measurement%]"
}
},
"resource": {
"data": {
"resource": "[%key:component::scrape::config::step::user::data::resource%]",

View File

@ -78,26 +78,31 @@
"value_template": "Defines a template to get the state of the sensor"
}
},
"init": {
"edit_sensor": {
"data": {
"authentication": "Select authentication method",
"headers": "Headers",
"method": "Method",
"password": "Password",
"resource": "Resource",
"timeout": "Timeout",
"username": "Username",
"verify_ssl": "Verify SSL certificate"
"attribute": "Attribute",
"device_class": "Device Class",
"index": "Index",
"name": "Name",
"select": "Select",
"state_class": "State Class",
"unit_of_measurement": "Unit of Measurement",
"value_template": "Value Template"
},
"data_description": {
"authentication": "Type of the HTTP authentication. Either basic or digest",
"headers": "Headers to use for the web request",
"resource": "The URL to the website that contains the value",
"timeout": "Timeout for connection to website",
"verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed"
},
"attribute": "Get value of an attribute on the selected tag",
"device_class": "The type/class of the sensor to set the icon in the frontend",
"index": "Defines which of the elements returned by the CSS selector to use",
"select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details",
"state_class": "The state_class of the sensor",
"unit_of_measurement": "Choose temperature measurement or create your own",
"value_template": "Defines a template to get the state of the sensor"
}
},
"init": {
"menu_options": {
"add_sensor": "Add sensor",
"select_edit_sensor": "Configure sensor",
"remove_sensor": "Remove sensor",
"resource": "Configure resource"
}

View File

@ -356,3 +356,69 @@ async def test_options_add_remove_sensor_flow(
# Check the state of the new entity
state = hass.states.get("sensor.template")
assert state.state == "Trying to get"
async def test_options_edit_sensor_flow(
hass: HomeAssistant, loaded_entry: MockConfigEntry
) -> None:
"""Test options flow to edit a sensor."""
state = hass.states.get("sensor.current_version")
assert state.state == "Current Version: 2021.12.10"
result = await hass.config_entries.options.async_init(loaded_entry.entry_id)
assert result["type"] == FlowResultType.MENU
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
{"next_step_id": "select_edit_sensor"},
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "select_edit_sensor"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
{"index": "0"},
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "edit_sensor"
mocker = MockRestData("test_scrape_sensor2")
with patch("homeassistant.components.rest.RestData", return_value=mocker):
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
CONF_SELECT: "template",
CONF_INDEX: 0.0,
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == {
CONF_RESOURCE: "https://www.home-assistant.io",
CONF_METHOD: "GET",
CONF_VERIFY_SSL: True,
CONF_TIMEOUT: 10,
"sensor": [
{
CONF_NAME: "Current version",
CONF_SELECT: "template",
CONF_INDEX: 0,
CONF_UNIQUE_ID: "3699ef88-69e6-11ed-a1eb-0242ac120002",
},
],
}
await hass.async_block_till_done()
# Check the entity was updated
assert len(hass.states.async_all()) == 1
# Check the state of the entity has changed as expected
state = hass.states.get("sensor.current_version")
assert state.state == "Trying to get"