From b54fc0229d9f0d2b8be58032182e387516703ed6 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Wed, 13 Oct 2021 23:33:03 +0200 Subject: [PATCH] Use entity_registry_enabled_default for Nut sensors (#56854) --- homeassistant/components/nut/__init__.py | 12 ++-- homeassistant/components/nut/config_flow.py | 32 +++-------- homeassistant/components/nut/sensor.py | 56 ++++++++----------- homeassistant/components/nut/strings.json | 6 -- .../components/nut/translations/en.json | 8 +-- tests/components/nut/test_config_flow.py | 21 ++----- tests/components/nut/test_sensor.py | 13 +++++ tests/components/nut/util.py | 3 +- 8 files changed, 56 insertions(+), 95 deletions(-) diff --git a/homeassistant/components/nut/__init__.py b/homeassistant/components/nut/__init__.py index 33cbc9fb47c..ea57c5994c6 100644 --- a/homeassistant/components/nut/__init__.py +++ b/homeassistant/components/nut/__init__.py @@ -39,6 +39,11 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Network UPS Tools (NUT) from a config entry.""" + # strip out the stale options CONF_RESOURCES + if CONF_RESOURCES in entry.options: + new_options = {k: v for k, v in entry.options.items() if k != CONF_RESOURCES} + hass.config_entries.async_update_entry(entry, options=new_options) + config = entry.data host = config[CONF_HOST] port = config[CONF_PORT] @@ -156,13 +161,6 @@ def _unique_id_from_status(status): return "_".join(unique_id_group) -def find_resources_in_config_entry(config_entry): - """Find the configured resources in the config entry.""" - if CONF_RESOURCES in config_entry.options: - return config_entry.options[CONF_RESOURCES] - return config_entry.data[CONF_RESOURCES] - - async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/nut/config_flow.py b/homeassistant/components/nut/config_flow.py index 70c097bd6f1..9b45e270448 100644 --- a/homeassistant/components/nut/config_flow.py +++ b/homeassistant/components/nut/config_flow.py @@ -17,7 +17,7 @@ from homeassistant.const import ( from homeassistant.core import callback import homeassistant.helpers.config_validation as cv -from . import PyNUTData, find_resources_in_config_entry +from . import PyNUTData from .const import ( DEFAULT_HOST, DEFAULT_PORT, @@ -229,35 +229,17 @@ class OptionsFlowHandler(config_entries.OptionsFlow): if user_input is not None: return self.async_create_entry(title="", data=user_input) - resources = find_resources_in_config_entry(self.config_entry) scan_interval = self.config_entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL ) - errors = {} - try: - info = await validate_input(self.hass, self.config_entry.data) - except CannotConnect: - errors[CONF_BASE] = "cannot_connect" - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Unexpected exception") - errors[CONF_BASE] = "unknown" + base_schema = { + vol.Optional(CONF_SCAN_INTERVAL, default=scan_interval): vol.All( + vol.Coerce(int), vol.Clamp(min=10, max=300) + ) + } - if errors: - return self.async_show_form(step_id="abort", errors=errors) - - base_schema = _resource_schema_base(info["available_resources"], resources) - base_schema[ - vol.Optional(CONF_SCAN_INTERVAL, default=scan_interval) - ] = cv.positive_int - - return self.async_show_form( - step_id="init", data_schema=vol.Schema(base_schema), errors=errors - ) - - async def async_step_abort(self, user_input=None): - """Abort options flow.""" - return self.async_create_entry(title="", data=self.config_entry.options) + return self.async_show_form(step_id="init", data_schema=vol.Schema(base_schema)) class CannotConnect(exceptions.HomeAssistantError): diff --git a/homeassistant/components/nut/sensor.py b/homeassistant/components/nut/sensor.py index 5c965274eae..703e9ddd4ec 100644 --- a/homeassistant/components/nut/sensor.py +++ b/homeassistant/components/nut/sensor.py @@ -42,39 +42,29 @@ async def async_setup_entry(hass, config_entry, async_add_entities): data = pynut_data[PYNUT_DATA] status = data.status - entities = [] + enabled_resources = [ + resource.lower() for resource in config_entry.data[CONF_RESOURCES] + ] + resources = [sensor_id for sensor_id in SENSOR_TYPES if sensor_id in status] + # Display status is a special case that falls back to the status value + # of the UPS instead. + if KEY_STATUS in resources: + resources.append(KEY_STATUS_DISPLAY) - if CONF_RESOURCES in config_entry.options: - resources = config_entry.options[CONF_RESOURCES] - else: - resources = config_entry.data[CONF_RESOURCES] - - for resource in resources: - sensor_type = resource.lower() - - # Display status is a special case that falls back to the status value - # of the UPS instead. - if sensor_type in status or ( - sensor_type == KEY_STATUS_DISPLAY and KEY_STATUS in status - ): - entities.append( - NUTSensor( - coordinator, - data, - name.title(), - SENSOR_TYPES[sensor_type], - unique_id, - manufacturer, - model, - firmware, - ) - ) - else: - _LOGGER.info( - "Sensor type: %s does not appear in the NUT status " - "output, cannot add", - sensor_type, - ) + entities = [ + NUTSensor( + coordinator, + data, + name.title(), + SENSOR_TYPES[sensor_type], + unique_id, + manufacturer, + model, + firmware, + sensor_type in enabled_resources, + ) + for sensor_type in resources + ] async_add_entities(entities, True) @@ -92,6 +82,7 @@ class NUTSensor(CoordinatorEntity, SensorEntity): manufacturer: str | None, model: str | None, firmware: str | None, + enabled_default: bool, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -102,6 +93,7 @@ class NUTSensor(CoordinatorEntity, SensorEntity): self._device_name = name self._data = data self._unique_id = unique_id + self._attr_entity_registry_enabled_default = enabled_default self._attr_name = f"{name} {sensor_description.name}" if unique_id is not None: diff --git a/homeassistant/components/nut/strings.json b/homeassistant/components/nut/strings.json index 97e637fdcb3..179f974b870 100644 --- a/homeassistant/components/nut/strings.json +++ b/homeassistant/components/nut/strings.json @@ -35,16 +35,10 @@ "options": { "step": { "init": { - "description": "Choose Sensor Resources.", "data": { - "resources": "Resources", "scan_interval": "Scan Interval (seconds)" } } - }, - "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "unknown": "[%key:common::config_flow::error::unknown%]" } } } diff --git a/homeassistant/components/nut/translations/en.json b/homeassistant/components/nut/translations/en.json index 3d57189f7a5..5975d734df4 100644 --- a/homeassistant/components/nut/translations/en.json +++ b/homeassistant/components/nut/translations/en.json @@ -33,17 +33,11 @@ } }, "options": { - "error": { - "cannot_connect": "Failed to connect", - "unknown": "Unexpected error" - }, "step": { "init": { "data": { - "resources": "Resources", "scan_interval": "Scan Interval (seconds)" - }, - "description": "Choose Sensor Resources." + } } } } diff --git a/tests/components/nut/test_config_flow.py b/tests/components/nut/test_config_flow.py index 135d0ef9efc..0799248384b 100644 --- a/tests/components/nut/test_config_flow.py +++ b/tests/components/nut/test_config_flow.py @@ -294,37 +294,25 @@ async def test_options_flow(hass): domain=DOMAIN, unique_id="abcde12345", data=VALID_CONFIG, - options={CONF_RESOURCES: ["battery.charge"]}, ) config_entry.add_to_hass(hass) - mock_pynut = _get_mock_pynutclient( - list_vars={"battery.voltage": "voltage"}, list_ups=["ups1"] - ) - - with patch( - "homeassistant.components.nut.PyNUTClient", - return_value=mock_pynut, - ), patch("homeassistant.components.nut.async_setup_entry", return_value=True): + with patch("homeassistant.components.nut.async_setup_entry", return_value=True): result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_RESOURCES: ["battery.voltage"]} + result["flow_id"], user_input={} ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert config_entry.options == { - CONF_RESOURCES: ["battery.voltage"], CONF_SCAN_INTERVAL: 60, } - with patch( - "homeassistant.components.nut.PyNUTClient", - return_value=mock_pynut, - ), patch("homeassistant.components.nut.async_setup_entry", return_value=True): + with patch("homeassistant.components.nut.async_setup_entry", return_value=True): result2 = await hass.config_entries.options.async_init(config_entry.entry_id) assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM @@ -332,11 +320,10 @@ async def test_options_flow(hass): result2 = await hass.config_entries.options.async_configure( result2["flow_id"], - user_input={CONF_RESOURCES: ["battery.voltage"], CONF_SCAN_INTERVAL: 12}, + user_input={CONF_SCAN_INTERVAL: 12}, ) assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert config_entry.options == { - CONF_RESOURCES: ["battery.voltage"], CONF_SCAN_INTERVAL: 12, } diff --git a/tests/components/nut/test_sensor.py b/tests/components/nut/test_sensor.py index a8c0945c6c0..a5715ff9c8e 100644 --- a/tests/components/nut/test_sensor.py +++ b/tests/components/nut/test_sensor.py @@ -202,3 +202,16 @@ async def test_blazer_usb(hass): assert all( state.attributes[key] == expected_attributes[key] for key in expected_attributes ) + + +async def test_stale_options(hass): + """Test creation of sensors with stale options to remove.""" + + config_entry = await async_init_integration( + hass, "blazer_usb", ["battery.charge"], True + ) + registry = er.async_get(hass) + entry = registry.async_get("sensor.ups1_battery_charge") + assert entry + assert entry.unique_id == f"{config_entry.entry_id}_battery.charge" + assert config_entry.options == {} diff --git a/tests/components/nut/util.py b/tests/components/nut/util.py index 4e7506a9db1..8ac1d110512 100644 --- a/tests/components/nut/util.py +++ b/tests/components/nut/util.py @@ -18,7 +18,7 @@ def _get_mock_pynutclient(list_vars=None, list_ups=None): async def async_init_integration( - hass: HomeAssistant, ups_fixture: str, resources: list + hass: HomeAssistant, ups_fixture: str, resources: list, add_options: bool = False ) -> MockConfigEntry: """Set up the nexia integration in Home Assistant.""" @@ -34,6 +34,7 @@ async def async_init_integration( entry = MockConfigEntry( domain=DOMAIN, data={CONF_HOST: "mock", CONF_PORT: "mock", CONF_RESOURCES: resources}, + options={CONF_RESOURCES: resources} if add_options else {}, ) entry.add_to_hass(hass)