From 971618399723472f17541130f260b5148f77f9af Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Wed, 18 Dec 2024 14:38:29 +0100 Subject: [PATCH] Add entity translations to devolo Home Control (#132927) --- .../devolo_home_control/binary_sensor.py | 13 +--- .../components/devolo_home_control/sensor.py | 7 +- .../devolo_home_control/strings.json | 15 ++++ tests/components/devolo_home_control/mocks.py | 38 +++++++++- .../snapshots/test_binary_sensor.ambr | 4 +- .../snapshots/test_sensor.ambr | 74 ++++++++++++++++--- .../devolo_home_control/test_sensor.py | 56 ++++++++------ 7 files changed, 157 insertions(+), 50 deletions(-) diff --git a/homeassistant/components/devolo_home_control/binary_sensor.py b/homeassistant/components/devolo_home_control/binary_sensor.py index 449b1c7659f..d24033a80b9 100644 --- a/homeassistant/components/devolo_home_control/binary_sensor.py +++ b/homeassistant/components/devolo_home_control/binary_sensor.py @@ -81,14 +81,8 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity): or self._binary_sensor_property.sensor_type ) - if device_instance.binary_sensor_property[element_uid].sub_type != "": - self._attr_name = device_instance.binary_sensor_property[ - element_uid - ].sub_type.capitalize() - else: - self._attr_name = device_instance.binary_sensor_property[ - element_uid - ].sensor_type.capitalize() + if device_instance.binary_sensor_property[element_uid].sub_type == "overload": + self._attr_translation_key = "overload" self._value = self._binary_sensor_property.state @@ -129,7 +123,8 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity): self._key = key self._attr_is_on = False - self._attr_name = f"Button {key}" + self._attr_translation_key = "button" + self._attr_translation_placeholders = {"key": str(key)} def _sync(self, message: tuple) -> None: """Update the binary sensor state.""" diff --git a/homeassistant/components/devolo_home_control/sensor.py b/homeassistant/components/devolo_home_control/sensor.py index 61a63419732..8d0a7f0313c 100644 --- a/homeassistant/components/devolo_home_control/sensor.py +++ b/homeassistant/components/devolo_home_control/sensor.py @@ -116,9 +116,11 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity): self._multi_level_sensor_property.sensor_type ) self._attr_native_unit_of_measurement = self._multi_level_sensor_property.unit - self._attr_name = self._multi_level_sensor_property.sensor_type.capitalize() self._value = self._multi_level_sensor_property.value + if self._multi_level_sensor_property.sensor_type == "light": + self._attr_translation_key = "brightness" + if element_uid.startswith("devolo.VoltageMultiLevelSensor:"): self._attr_entity_registry_enabled_default = False @@ -128,7 +130,6 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_native_unit_of_measurement = PERCENTAGE - _attr_name = "Battery level" _attr_device_class = SensorDeviceClass.BATTERY _attr_state_class = SensorStateClass.MEASUREMENT @@ -175,8 +176,6 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): device_instance.consumption_property[element_uid], consumption ) - self._attr_name = f"{consumption.capitalize()} consumption" - @property def unique_id(self) -> str: """Return the unique ID of the entity. diff --git a/homeassistant/components/devolo_home_control/strings.json b/homeassistant/components/devolo_home_control/strings.json index 1eaf64564c2..be853e2d89d 100644 --- a/homeassistant/components/devolo_home_control/strings.json +++ b/homeassistant/components/devolo_home_control/strings.json @@ -30,5 +30,20 @@ } } } + }, + "entity": { + "binary_sensor": { + "button": { + "name": "Button {key}" + }, + "overload": { + "name": "Overload" + } + }, + "sensor": { + "brightness": { + "name": "Brightness" + } + } } } diff --git a/tests/components/devolo_home_control/mocks.py b/tests/components/devolo_home_control/mocks.py index 33c0a230e90..d611c73cf2c 100644 --- a/tests/components/devolo_home_control/mocks.py +++ b/tests/components/devolo_home_control/mocks.py @@ -70,6 +70,18 @@ class MultiLevelSensorPropertyMock(MultiLevelSensorProperty): self._logger = MagicMock() +class BrightnessSensorPropertyMock(MultiLevelSensorProperty): + """devolo Home Control brightness sensor mock.""" + + def __init__(self, **kwargs: Any) -> None: # pylint: disable=super-init-not-called + """Initialize the mock.""" + self.element_uid = "Test" + self.sensor_type = "light" + self._unit = "%" + self._value = 20 + self._logger = MagicMock() + + class MultiLevelSwitchPropertyMock(MultiLevelSwitchProperty): """devolo Home Control multi level switch mock.""" @@ -138,7 +150,18 @@ class BinarySensorMockOverload(DeviceMock): """Initialize the mock.""" super().__init__() self.binary_sensor_property = {"Overload": BinarySensorPropertyMock()} - self.binary_sensor_property["Overload"].sensor_type = "overload" + self.binary_sensor_property["Overload"].sub_type = "overload" + + +class BrightnessSensorMock(DeviceMock): + """devolo Home Control brightness sensor device mock.""" + + def __init__(self) -> None: + """Initialize the mock.""" + super().__init__() + self.multi_level_sensor_property = { + "devolo.MultiLevelSensor:Test": BrightnessSensorPropertyMock() + } class ClimateMock(DeviceMock): @@ -275,6 +298,19 @@ class HomeControlMockBinarySensor(HomeControlMock): self.publisher.unregister = MagicMock() +class HomeControlMockBrightness(HomeControlMock): + """devolo Home Control gateway mock with brightness devices.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + super().__init__() + self.devices = { + "Test": BrightnessSensorMock(), + } + self.publisher = Publisher(self.devices.keys()) + self.publisher.unregister = MagicMock() + + class HomeControlMockClimate(HomeControlMock): """devolo Home Control gateway mock with climate devices.""" diff --git a/tests/components/devolo_home_control/snapshots/test_binary_sensor.ambr b/tests/components/devolo_home_control/snapshots/test_binary_sensor.ambr index 0980a550c7b..c5daed73b33 100644 --- a/tests/components/devolo_home_control/snapshots/test_binary_sensor.ambr +++ b/tests/components/devolo_home_control/snapshots/test_binary_sensor.ambr @@ -88,7 +88,7 @@ 'platform': 'devolo_home_control', 'previous_unique_id': None, 'supported_features': 0, - 'translation_key': None, + 'translation_key': 'overload', 'unique_id': 'Overload', 'unit_of_measurement': None, }) @@ -134,7 +134,7 @@ 'platform': 'devolo_home_control', 'previous_unique_id': None, 'supported_features': 0, - 'translation_key': None, + 'translation_key': 'button', 'unique_id': 'Test_1', 'unit_of_measurement': None, }) diff --git a/tests/components/devolo_home_control/snapshots/test_sensor.ambr b/tests/components/devolo_home_control/snapshots/test_sensor.ambr index 7f67c70f6ac..3c23385594a 100644 --- a/tests/components/devolo_home_control/snapshots/test_sensor.ambr +++ b/tests/components/devolo_home_control/snapshots/test_sensor.ambr @@ -3,12 +3,12 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'battery', - 'friendly_name': 'Test Battery level', + 'friendly_name': 'Test Battery', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.test_battery_level', + 'entity_id': 'sensor.test_battery', 'last_changed': , 'last_reported': , 'last_updated': , @@ -29,7 +29,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.test_battery_level', + 'entity_id': 'sensor.test_battery', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -41,7 +41,7 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Battery level', + 'original_name': 'Battery', 'platform': 'devolo_home_control', 'previous_unique_id': None, 'supported_features': 0, @@ -50,16 +50,66 @@ 'unit_of_measurement': '%', }) # --- +# name: test_brightness_sensor + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Brightness', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.test_brightness', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '20', + }) +# --- +# name: test_brightness_sensor.1 + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.test_brightness', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Brightness', + 'platform': 'devolo_home_control', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'brightness', + 'unique_id': 'devolo.MultiLevelSensor:Test', + 'unit_of_measurement': '%', + }) +# --- # name: test_consumption_sensor StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'power', - 'friendly_name': 'Test Current consumption', + 'friendly_name': 'Test Power', 'state_class': , 'unit_of_measurement': 'W', }), 'context': , - 'entity_id': 'sensor.test_current_consumption', + 'entity_id': 'sensor.test_power', 'last_changed': , 'last_reported': , 'last_updated': , @@ -80,7 +130,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.test_current_consumption', + 'entity_id': 'sensor.test_power', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -92,7 +142,7 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Current consumption', + 'original_name': 'Power', 'platform': 'devolo_home_control', 'previous_unique_id': None, 'supported_features': 0, @@ -105,12 +155,12 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'energy', - 'friendly_name': 'Test Total consumption', + 'friendly_name': 'Test Energy', 'state_class': , 'unit_of_measurement': 'kWh', }), 'context': , - 'entity_id': 'sensor.test_total_consumption', + 'entity_id': 'sensor.test_energy', 'last_changed': , 'last_reported': , 'last_updated': , @@ -131,7 +181,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.test_total_consumption', + 'entity_id': 'sensor.test_energy', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -143,7 +193,7 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Total consumption', + 'original_name': 'Energy', 'platform': 'devolo_home_control', 'previous_unique_id': None, 'supported_features': 0, diff --git a/tests/components/devolo_home_control/test_sensor.py b/tests/components/devolo_home_control/test_sensor.py index 08b53dae865..ba4c493c366 100644 --- a/tests/components/devolo_home_control/test_sensor.py +++ b/tests/components/devolo_home_control/test_sensor.py @@ -10,7 +10,30 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import configure_integration -from .mocks import HomeControlMock, HomeControlMockConsumption, HomeControlMockSensor +from .mocks import ( + HomeControlMock, + HomeControlMockBrightness, + HomeControlMockConsumption, + HomeControlMockSensor, +) + + +async def test_brightness_sensor( + hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion +) -> None: + """Test setup of a brightness sensor device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockBrightness() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{SENSOR_DOMAIN}.test_brightness") + assert state == snapshot + assert entity_registry.async_get(f"{SENSOR_DOMAIN}.test_brightness") == snapshot async def test_temperature_sensor( @@ -45,14 +68,14 @@ async def test_battery_sensor( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - state = hass.states.get(f"{SENSOR_DOMAIN}.test_battery_level") + state = hass.states.get(f"{SENSOR_DOMAIN}.test_battery") assert state == snapshot - assert entity_registry.async_get(f"{SENSOR_DOMAIN}.test_battery_level") == snapshot + assert entity_registry.async_get(f"{SENSOR_DOMAIN}.test_battery") == snapshot # Emulate websocket message: value changed test_gateway.publisher.dispatch("Test", ("Test", 10, "battery_level")) await hass.async_block_till_done() - assert hass.states.get(f"{SENSOR_DOMAIN}.test_battery_level").state == "10" + assert hass.states.get(f"{SENSOR_DOMAIN}.test_battery").state == "10" async def test_consumption_sensor( @@ -68,37 +91,26 @@ async def test_consumption_sensor( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - state = hass.states.get(f"{SENSOR_DOMAIN}.test_current_consumption") + state = hass.states.get(f"{SENSOR_DOMAIN}.test_power") assert state == snapshot - assert ( - entity_registry.async_get(f"{SENSOR_DOMAIN}.test_current_consumption") - == snapshot - ) + assert entity_registry.async_get(f"{SENSOR_DOMAIN}.test_power") == snapshot - state = hass.states.get(f"{SENSOR_DOMAIN}.test_total_consumption") + state = hass.states.get(f"{SENSOR_DOMAIN}.test_energy") assert state == snapshot - assert ( - entity_registry.async_get(f"{SENSOR_DOMAIN}.test_total_consumption") == snapshot - ) + assert entity_registry.async_get(f"{SENSOR_DOMAIN}.test_energy") == snapshot # Emulate websocket message: value changed test_gateway.devices["Test"].consumption_property["devolo.Meter:Test"].total = 50.0 test_gateway.publisher.dispatch("Test", ("devolo.Meter:Test", 50.0)) await hass.async_block_till_done() - assert hass.states.get(f"{SENSOR_DOMAIN}.test_total_consumption").state == "50.0" + assert hass.states.get(f"{SENSOR_DOMAIN}.test_energy").state == "50.0" # Emulate websocket message: device went offline test_gateway.devices["Test"].status = 1 test_gateway.publisher.dispatch("Test", ("Status", False, "status")) await hass.async_block_till_done() - assert ( - hass.states.get(f"{SENSOR_DOMAIN}.test_current_consumption").state - == STATE_UNAVAILABLE - ) - assert ( - hass.states.get(f"{SENSOR_DOMAIN}.test_total_consumption").state - == STATE_UNAVAILABLE - ) + assert hass.states.get(f"{SENSOR_DOMAIN}.test_power").state == STATE_UNAVAILABLE + assert hass.states.get(f"{SENSOR_DOMAIN}.test_energy").state == STATE_UNAVAILABLE async def test_voltage_sensor(hass: HomeAssistant) -> None: