mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 22:57:17 +00:00
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:
parent
92fef0f2ba
commit
cbf8a41eed
@ -87,7 +87,6 @@ RESOURCE_SETUP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SENSOR_SETUP = {
|
SENSOR_SETUP = {
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(),
|
|
||||||
vol.Required(CONF_SELECT): TextSelector(),
|
vol.Required(CONF_SELECT): TextSelector(),
|
||||||
vol.Optional(CONF_INDEX, default=0): NumberSelector(
|
vol.Optional(CONF_INDEX, default=0): NumberSelector(
|
||||||
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
|
NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
|
||||||
@ -146,6 +145,49 @@ async def validate_sensor_setup(
|
|||||||
return {}
|
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:
|
async def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
|
||||||
"""Return schema for sensor removal."""
|
"""Return schema for sensor removal."""
|
||||||
return vol.Schema(
|
return vol.Schema(
|
||||||
@ -183,7 +225,13 @@ async def validate_remove_sensor(
|
|||||||
|
|
||||||
|
|
||||||
DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP)
|
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 = {
|
CONFIG_FLOW = {
|
||||||
"user": SchemaFlowFormStep(
|
"user": SchemaFlowFormStep(
|
||||||
@ -197,7 +245,9 @@ CONFIG_FLOW = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
OPTIONS_FLOW = {
|
OPTIONS_FLOW = {
|
||||||
"init": SchemaFlowMenuStep(["resource", "add_sensor", "remove_sensor"]),
|
"init": SchemaFlowMenuStep(
|
||||||
|
["resource", "add_sensor", "select_edit_sensor", "remove_sensor"]
|
||||||
|
),
|
||||||
"resource": SchemaFlowFormStep(
|
"resource": SchemaFlowFormStep(
|
||||||
DATA_SCHEMA_RESOURCE,
|
DATA_SCHEMA_RESOURCE,
|
||||||
validate_user_input=validate_rest_setup,
|
validate_user_input=validate_rest_setup,
|
||||||
@ -207,6 +257,17 @@ OPTIONS_FLOW = {
|
|||||||
suggested_values=None,
|
suggested_values=None,
|
||||||
validate_user_input=validate_sensor_setup,
|
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(
|
"remove_sensor": SchemaFlowFormStep(
|
||||||
get_remove_sensor_schema,
|
get_remove_sensor_schema,
|
||||||
suggested_values=None,
|
suggested_values=None,
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"init": {
|
"init": {
|
||||||
"menu_options": {
|
"menu_options": {
|
||||||
"add_sensor": "Add sensor",
|
"add_sensor": "Add sensor",
|
||||||
|
"select_edit_sensor": "Configure sensor",
|
||||||
"remove_sensor": "Remove sensor",
|
"remove_sensor": "Remove sensor",
|
||||||
"resource": "Configure resource"
|
"resource": "Configure resource"
|
||||||
}
|
}
|
||||||
@ -79,6 +80,27 @@
|
|||||||
"unit_of_measurement": "[%key:component::scrape::config::step::sensor::data_description::unit_of_measurement%]"
|
"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": {
|
"resource": {
|
||||||
"data": {
|
"data": {
|
||||||
"resource": "[%key:component::scrape::config::step::user::data::resource%]",
|
"resource": "[%key:component::scrape::config::step::user::data::resource%]",
|
||||||
|
@ -78,26 +78,31 @@
|
|||||||
"value_template": "Defines a template to get the state of the sensor"
|
"value_template": "Defines a template to get the state of the sensor"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"init": {
|
"edit_sensor": {
|
||||||
"data": {
|
"data": {
|
||||||
"authentication": "Select authentication method",
|
"attribute": "Attribute",
|
||||||
"headers": "Headers",
|
"device_class": "Device Class",
|
||||||
"method": "Method",
|
"index": "Index",
|
||||||
"password": "Password",
|
"name": "Name",
|
||||||
"resource": "Resource",
|
"select": "Select",
|
||||||
"timeout": "Timeout",
|
"state_class": "State Class",
|
||||||
"username": "Username",
|
"unit_of_measurement": "Unit of Measurement",
|
||||||
"verify_ssl": "Verify SSL certificate"
|
"value_template": "Value Template"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"authentication": "Type of the HTTP authentication. Either basic or digest",
|
"attribute": "Get value of an attribute on the selected tag",
|
||||||
"headers": "Headers to use for the web request",
|
"device_class": "The type/class of the sensor to set the icon in the frontend",
|
||||||
"resource": "The URL to the website that contains the value",
|
"index": "Defines which of the elements returned by the CSS selector to use",
|
||||||
"timeout": "Timeout for connection to website",
|
"select": "Defines what tag to search for. Check Beautifulsoup CSS selectors for details",
|
||||||
"verify_ssl": "Enables/disables verification of SSL/TLS certificate, for example if it is self-signed"
|
"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": {
|
"menu_options": {
|
||||||
"add_sensor": "Add sensor",
|
"add_sensor": "Add sensor",
|
||||||
|
"select_edit_sensor": "Configure sensor",
|
||||||
"remove_sensor": "Remove sensor",
|
"remove_sensor": "Remove sensor",
|
||||||
"resource": "Configure resource"
|
"resource": "Configure resource"
|
||||||
}
|
}
|
||||||
|
@ -356,3 +356,69 @@ async def test_options_add_remove_sensor_flow(
|
|||||||
# Check the state of the new entity
|
# Check the state of the new entity
|
||||||
state = hass.states.get("sensor.template")
|
state = hass.states.get("sensor.template")
|
||||||
assert state.state == "Trying to get"
|
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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user