From 790523126effe84de725b469f3dd3946709719ca Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Fri, 18 Aug 2023 13:40:35 +0200 Subject: [PATCH] Name unnamed update entities by their device class (#98579) --- homeassistant/components/ezviz/strings.json | 5 - homeassistant/components/ezviz/update.py | 1 - .../components/litterrobot/strings.json | 5 - .../components/litterrobot/update.py | 1 - .../components/rainmachine/strings.json | 5 - .../components/rainmachine/update.py | 1 - homeassistant/components/update/__init__.py | 7 ++ tests/components/update/test_init.py | 114 +++++++++++++++++- 8 files changed, 120 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/ezviz/strings.json b/homeassistant/components/ezviz/strings.json index 373f9af22fc..11144f8ae71 100644 --- a/homeassistant/components/ezviz/strings.json +++ b/homeassistant/components/ezviz/strings.json @@ -223,11 +223,6 @@ "name": "Follow movement" } }, - "update": { - "firmware": { - "name": "[%key:component::update::entity_component::firmware::name%]" - } - }, "siren": { "siren": { "name": "[%key:component::siren::title%]" diff --git a/homeassistant/components/ezviz/update.py b/homeassistant/components/ezviz/update.py index 6a80a579080..003397d8dda 100644 --- a/homeassistant/components/ezviz/update.py +++ b/homeassistant/components/ezviz/update.py @@ -24,7 +24,6 @@ PARALLEL_UPDATES = 1 UPDATE_ENTITY_TYPES = UpdateEntityDescription( key="version", - translation_key="firmware", device_class=UpdateDeviceClass.FIRMWARE, ) diff --git a/homeassistant/components/litterrobot/strings.json b/homeassistant/components/litterrobot/strings.json index 8436d24902c..7acfad69735 100644 --- a/homeassistant/components/litterrobot/strings.json +++ b/homeassistant/components/litterrobot/strings.json @@ -131,11 +131,6 @@ "litter_box": { "name": "Litter box" } - }, - "update": { - "firmware": { - "name": "Firmware" - } } }, "services": { diff --git a/homeassistant/components/litterrobot/update.py b/homeassistant/components/litterrobot/update.py index 9b8391c5bae..584a6af77c2 100644 --- a/homeassistant/components/litterrobot/update.py +++ b/homeassistant/components/litterrobot/update.py @@ -24,7 +24,6 @@ SCAN_INTERVAL = timedelta(days=1) FIRMWARE_UPDATE_ENTITY = UpdateEntityDescription( key="firmware", - translation_key="firmware", device_class=UpdateDeviceClass.FIRMWARE, ) diff --git a/homeassistant/components/rainmachine/strings.json b/homeassistant/components/rainmachine/strings.json index fc48ebce4eb..ac2b86754e5 100644 --- a/homeassistant/components/rainmachine/strings.json +++ b/homeassistant/components/rainmachine/strings.json @@ -91,11 +91,6 @@ "hot_days_extra_watering": { "name": "Extra water on hot days" } - }, - "update": { - "firmware": { - "name": "Firmware" - } } }, "services": { diff --git a/homeassistant/components/rainmachine/update.py b/homeassistant/components/rainmachine/update.py index 372319ba9a0..8d5690b5320 100644 --- a/homeassistant/components/rainmachine/update.py +++ b/homeassistant/components/rainmachine/update.py @@ -44,7 +44,6 @@ UPDATE_STATE_MAP = { UPDATE_DESCRIPTION = RainMachineEntityDescription( key="update", - translation_key="firmware", api_category=DATA_MACHINE_FIRMWARE_UPDATE_STATUS, ) diff --git a/homeassistant/components/update/__init__.py b/homeassistant/components/update/__init__.py index b9d01629536..e23032e24fe 100644 --- a/homeassistant/components/update/__init__.py +++ b/homeassistant/components/update/__init__.py @@ -216,6 +216,13 @@ class UpdateEntity(RestoreEntity): """Version installed and in use.""" return self._attr_installed_version + def _default_to_device_class_name(self) -> bool: + """Return True if an unnamed entity should be named by its device class. + + For updates this is True if the entity has a device class. + """ + return self.device_class is not None + @property def device_class(self) -> UpdateDeviceClass | None: """Return the class of this entity.""" diff --git a/tests/components/update/test_init.py b/tests/components/update/test_init.py index a7780f54f70..73f98c9e2db 100644 --- a/tests/components/update/test_init.py +++ b/tests/components/update/test_init.py @@ -1,4 +1,5 @@ """The tests for the Update component.""" +from collections.abc import Generator from unittest.mock import MagicMock, patch import pytest @@ -24,6 +25,7 @@ from homeassistant.components.update.const import ( ATTR_TITLE, UpdateEntityFeature, ) +from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.const import ( ATTR_ENTITY_ID, CONF_PLATFORM, @@ -34,12 +36,24 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, State, callback from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_state_change_event from homeassistant.setup import async_setup_component -from tests.common import MockEntityPlatform, mock_restore_cache +from tests.common import ( + MockConfigEntry, + MockEntityPlatform, + MockModule, + MockPlatform, + mock_config_flow, + mock_integration, + mock_platform, + mock_restore_cache, +) from tests.typing import WebSocketGenerator +TEST_DOMAIN = "test" + class MockUpdateEntity(UpdateEntity): """Mock UpdateEntity to use in tests.""" @@ -752,3 +766,101 @@ async def test_release_notes_entity_does_not_support_release_notes( result = await client.receive_json() assert result["error"]["code"] == "not_supported" assert result["error"]["message"] == "Entity does not support release notes" + + +class MockFlow(ConfigFlow): + """Test flow.""" + + +@pytest.fixture(autouse=True) +def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]: + """Mock config flow.""" + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + + with mock_config_flow(TEST_DOMAIN, MockFlow): + yield + + +async def test_name(hass: HomeAssistant) -> None: + """Test update name.""" + + async def async_setup_entry_init( + hass: HomeAssistant, config_entry: ConfigEntry + ) -> bool: + """Set up test config entry.""" + await hass.config_entries.async_forward_entry_setup(config_entry, DOMAIN) + return True + + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + mock_integration( + hass, + MockModule( + TEST_DOMAIN, + async_setup_entry=async_setup_entry_init, + ), + ) + + # Unnamed update entity without device class -> no name + entity1 = UpdateEntity() + entity1.entity_id = "update.test1" + + # Unnamed update entity with device class but has_entity_name False -> no name + entity2 = UpdateEntity() + entity2.entity_id = "update.test2" + entity2._attr_device_class = UpdateDeviceClass.FIRMWARE + + # Unnamed update entity with device class and has_entity_name True -> named + entity3 = UpdateEntity() + entity3.entity_id = "update.test3" + entity3._attr_device_class = UpdateDeviceClass.FIRMWARE + entity3._attr_has_entity_name = True + + # Unnamed update entity with device class and has_entity_name True -> named + entity4 = UpdateEntity() + entity4.entity_id = "update.test4" + entity4.entity_description = UpdateEntityDescription( + "test", + UpdateDeviceClass.FIRMWARE, + has_entity_name=True, + ) + + async def async_setup_entry_platform( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, + ) -> None: + """Set up test update platform via config entry.""" + async_add_entities([entity1, entity2, entity3, entity4]) + + mock_platform( + hass, + f"{TEST_DOMAIN}.{DOMAIN}", + MockPlatform(async_setup_entry=async_setup_entry_platform), + ) + + config_entry = MockConfigEntry(domain=TEST_DOMAIN) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(entity1.entity_id) + assert state + assert "device_class" not in state.attributes + assert "friendly_name" not in state.attributes + + state = hass.states.get(entity2.entity_id) + assert state + assert state.attributes.get("device_class") == "firmware" + assert "friendly_name" not in state.attributes + + expected = { + "device_class": "firmware", + "friendly_name": "Firmware", + } + state = hass.states.get(entity3.entity_id) + assert state + assert expected.items() <= state.attributes.items() + + state = hass.states.get(entity4.entity_id) + assert state + assert expected.items() <= state.attributes.items()