Migrate devolo Home Control to new entity naming (#74969)

This commit is contained in:
Guido Schmitz 2022-11-22 09:54:05 +01:00 committed by GitHub
parent e4c73259f7
commit 1e64d830ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 32 deletions

View File

@ -81,13 +81,14 @@ class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity):
or self._binary_sensor_property.sensor_type or self._binary_sensor_property.sensor_type
) )
if self._attr_device_class is None: if device_instance.binary_sensor_property[element_uid].sub_type != "":
if device_instance.binary_sensor_property[element_uid].sub_type != "": self._attr_name = device_instance.binary_sensor_property[
self._attr_name += ( element_uid
f" {device_instance.binary_sensor_property[element_uid].sub_type}" ].sub_type.capitalize()
) else:
else: self._attr_name = device_instance.binary_sensor_property[
self._attr_name += f" {device_instance.binary_sensor_property[element_uid].sensor_type}" element_uid
].sensor_type.capitalize()
self._value = self._binary_sensor_property.state self._value = self._binary_sensor_property.state
@ -128,6 +129,7 @@ class DevoloRemoteControl(DevoloDeviceEntity, BinarySensorEntity):
self._key = key self._key = key
self._attr_is_on = False self._attr_is_on = False
self._attr_name = f"Button {key}"
def _sync(self, message: tuple) -> None: def _sync(self, message: tuple) -> None:
"""Update the binary sensor state.""" """Update the binary sensor state."""

View File

@ -19,6 +19,8 @@ _LOGGER = logging.getLogger(__name__)
class DevoloDeviceEntity(Entity): class DevoloDeviceEntity(Entity):
"""Abstract representation of a device within devolo Home Control.""" """Abstract representation of a device within devolo Home Control."""
_attr_has_entity_name = True
def __init__( def __init__(
self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str self, homecontrol: HomeControl, device_instance: Zwave, element_uid: str
) -> None: ) -> None:
@ -29,9 +31,6 @@ class DevoloDeviceEntity(Entity):
self._attr_available = ( self._attr_available = (
device_instance.is_online() device_instance.is_online()
) # This is not doing I/O. It fetches an internal state of the API ) # This is not doing I/O. It fetches an internal state of the API
self._attr_name: str = device_instance.settings_property[
"general_device_settings"
].name
self._attr_should_poll = False self._attr_should_poll = False
self._attr_unique_id = element_uid self._attr_unique_id = element_uid
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
@ -39,7 +38,7 @@ class DevoloDeviceEntity(Entity):
identifiers={(DOMAIN, self._device_instance.uid)}, identifiers={(DOMAIN, self._device_instance.uid)},
manufacturer=device_instance.brand, manufacturer=device_instance.brand,
model=device_instance.name, model=device_instance.name,
name=self._attr_name, name=device_instance.settings_property["general_device_settings"].name,
suggested_area=device_instance.settings_property[ suggested_area=device_instance.settings_property[
"general_device_settings" "general_device_settings"
].zone, ].zone,
@ -47,11 +46,16 @@ class DevoloDeviceEntity(Entity):
self.subscriber: Subscriber | None = None self.subscriber: Subscriber | None = None
self.sync_callback = self._sync self.sync_callback = self._sync
self._value: float self._value: float
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Call when entity is added to hass.""" """Call when entity is added to hass."""
self.subscriber = Subscriber(self._attr_name, callback=self.sync_callback) assert self.device_info
assert self.device_info["name"] # The name was set on entity creation
self.subscriber = Subscriber(
self.device_info["name"], callback=self.sync_callback
)
self._homecontrol.publisher.register( self._homecontrol.publisher.register(
self._device_instance.uid, self.subscriber, self.sync_callback self._device_instance.uid, self.subscriber, self.sync_callback
) )

View File

@ -115,12 +115,9 @@ class DevoloGenericMultiLevelDeviceEntity(DevoloMultiLevelDeviceEntity):
self._multi_level_sensor_property.sensor_type self._multi_level_sensor_property.sensor_type
) )
self._attr_native_unit_of_measurement = self._multi_level_sensor_property.unit 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 self._value = self._multi_level_sensor_property.value
if self._attr_device_class is None:
self._attr_name += f" {self._multi_level_sensor_property.sensor_type}"
if element_uid.startswith("devolo.VoltageMultiLevelSensor:"): if element_uid.startswith("devolo.VoltageMultiLevelSensor:"):
self._attr_entity_registry_enabled_default = False self._attr_entity_registry_enabled_default = False
@ -143,7 +140,7 @@ class DevoloBatteryEntity(DevoloMultiLevelDeviceEntity):
self._attr_state_class = STATE_CLASS_MAPPING.get("battery") self._attr_state_class = STATE_CLASS_MAPPING.get("battery")
self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_entity_category = EntityCategory.DIAGNOSTIC
self._attr_native_unit_of_measurement = PERCENTAGE self._attr_native_unit_of_measurement = PERCENTAGE
self._attr_name = "Battery level"
self._value = device_instance.battery_level self._value = device_instance.battery_level
@ -179,7 +176,7 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
device_instance.consumption_property[element_uid], consumption device_instance.consumption_property[element_uid], consumption
) )
self._attr_name += f" {consumption}" self._attr_name = f"{consumption.capitalize()} consumption"
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:

View File

@ -4,7 +4,12 @@ from unittest.mock import patch
import pytest import pytest
from homeassistant.components.binary_sensor import DOMAIN from homeassistant.components.binary_sensor import DOMAIN
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from homeassistant.const import (
ATTR_FRIENDLY_NAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry from homeassistant.helpers import entity_registry
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
@ -31,25 +36,30 @@ async def test_binary_sensor(hass: HomeAssistant):
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get(f"{DOMAIN}.test") state = hass.states.get(f"{DOMAIN}.test_door")
assert state is not None assert state is not None
assert state.state == STATE_OFF assert state.state == STATE_OFF
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Door"
state = hass.states.get(f"{DOMAIN}.test_2") state = hass.states.get(f"{DOMAIN}.test_overload")
assert state is not None assert state is not None
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Overload"
er = entity_registry.async_get(hass) er = entity_registry.async_get(hass)
assert er.async_get(f"{DOMAIN}.test_2").entity_category == EntityCategory.DIAGNOSTIC assert (
er.async_get(f"{DOMAIN}.test_overload").entity_category
== EntityCategory.DIAGNOSTIC
)
# Emulate websocket message: sensor turned on # Emulate websocket message: sensor turned on
test_gateway.publisher.dispatch("Test", ("Test", True)) test_gateway.publisher.dispatch("Test", ("Test", True))
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(f"{DOMAIN}.test").state == STATE_ON assert hass.states.get(f"{DOMAIN}.test_door").state == STATE_ON
# Emulate websocket message: device went offline # Emulate websocket message: device went offline
test_gateway.devices["Test"].status = 1 test_gateway.devices["Test"].status = 1
test_gateway.publisher.dispatch("Test", ("Status", False, "status")) test_gateway.publisher.dispatch("Test", ("Status", False, "status"))
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE assert hass.states.get(f"{DOMAIN}.test_door").state == STATE_UNAVAILABLE
@pytest.mark.usefixtures("mock_zeroconf") @pytest.mark.usefixtures("mock_zeroconf")
@ -65,25 +75,26 @@ async def test_remote_control(hass: HomeAssistant):
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get(f"{DOMAIN}.test") state = hass.states.get(f"{DOMAIN}.test_button_1")
assert state is not None assert state is not None
assert state.state == STATE_OFF assert state.state == STATE_OFF
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Button 1"
# Emulate websocket message: button pressed # Emulate websocket message: button pressed
test_gateway.publisher.dispatch("Test", ("Test", 1)) test_gateway.publisher.dispatch("Test", ("Test", 1))
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(f"{DOMAIN}.test").state == STATE_ON assert hass.states.get(f"{DOMAIN}.test_button_1").state == STATE_ON
# Emulate websocket message: button released # Emulate websocket message: button released
test_gateway.publisher.dispatch("Test", ("Test", 0)) test_gateway.publisher.dispatch("Test", ("Test", 0))
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(f"{DOMAIN}.test").state == STATE_OFF assert hass.states.get(f"{DOMAIN}.test_button_1").state == STATE_OFF
# Emulate websocket message: device went offline # Emulate websocket message: device went offline
test_gateway.devices["Test"].status = 1 test_gateway.devices["Test"].status = 1
test_gateway.publisher.dispatch("Test", ("Status", False, "status")) test_gateway.publisher.dispatch("Test", ("Status", False, "status"))
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE assert hass.states.get(f"{DOMAIN}.test_button_1").state == STATE_UNAVAILABLE
@pytest.mark.usefixtures("mock_zeroconf") @pytest.mark.usefixtures("mock_zeroconf")
@ -97,7 +108,7 @@ async def test_disabled(hass: HomeAssistant):
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(f"{DOMAIN}.devolo.WarningBinaryFI:Test") is None assert hass.states.get(f"{DOMAIN}.test_door") is None
@pytest.mark.usefixtures("mock_zeroconf") @pytest.mark.usefixtures("mock_zeroconf")
@ -112,7 +123,7 @@ async def test_remove_from_hass(hass: HomeAssistant):
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get(f"{DOMAIN}.test") state = hass.states.get(f"{DOMAIN}.test_door")
assert state is not None assert state is not None
await hass.config_entries.async_remove(entry.entry_id) await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -7,7 +7,12 @@ from homeassistant.components.climate import (
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
HVACMode, HVACMode,
) )
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNAVAILABLE from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
ATTR_TEMPERATURE,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import configure_integration from . import configure_integration
@ -30,6 +35,7 @@ async def test_climate(hass: HomeAssistant):
assert state is not None assert state is not None
assert state.state == HVACMode.HEAT assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_TEMPERATURE] == test_gateway.devices["Test"].value assert state.attributes[ATTR_TEMPERATURE] == test_gateway.devices["Test"].value
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test"
# Emulate websocket message: temperature changed # Emulate websocket message: temperature changed
test_gateway.publisher.dispatch("Test", ("Test", 21.0)) test_gateway.publisher.dispatch("Test", ("Test", 21.0))

View File

@ -4,6 +4,7 @@ from unittest.mock import patch
from homeassistant.components.cover import ATTR_CURRENT_POSITION, ATTR_POSITION, DOMAIN from homeassistant.components.cover import ATTR_CURRENT_POSITION, ATTR_POSITION, DOMAIN
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER,
SERVICE_OPEN_COVER, SERVICE_OPEN_COVER,
SERVICE_SET_COVER_POSITION, SERVICE_SET_COVER_POSITION,
@ -32,6 +33,7 @@ async def test_cover(hass: HomeAssistant):
state = hass.states.get(f"{DOMAIN}.test") state = hass.states.get(f"{DOMAIN}.test")
assert state is not None assert state is not None
assert state.state == STATE_OPEN assert state.state == STATE_OPEN
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test"
assert ( assert (
state.attributes[ATTR_CURRENT_POSITION] state.attributes[ATTR_CURRENT_POSITION]
== test_gateway.devices["Test"] == test_gateway.devices["Test"]

View File

@ -10,6 +10,7 @@ from homeassistant.components.light import (
) )
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_FRIENDLY_NAME,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
SERVICE_TURN_ON, SERVICE_TURN_ON,
STATE_OFF, STATE_OFF,
@ -36,6 +37,7 @@ async def test_light_without_binary_sensor(hass: HomeAssistant):
state = hass.states.get(f"{DOMAIN}.test") state = hass.states.get(f"{DOMAIN}.test")
assert state is not None assert state is not None
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test"
assert state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS assert state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS]
assert state.attributes[ATTR_BRIGHTNESS] == round( assert state.attributes[ATTR_BRIGHTNESS] == round(

View File

@ -4,7 +4,12 @@ from unittest.mock import patch
import pytest import pytest
from homeassistant.components.siren import DOMAIN from homeassistant.components.siren import DOMAIN
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from homeassistant.const import (
ATTR_FRIENDLY_NAME,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import configure_integration from . import configure_integration
@ -27,6 +32,7 @@ async def test_siren(hass: HomeAssistant):
state = hass.states.get(f"{DOMAIN}.test") state = hass.states.get(f"{DOMAIN}.test")
assert state is not None assert state is not None
assert state.state == STATE_OFF assert state.state == STATE_OFF
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test"
# Emulate websocket message: sensor turned on # Emulate websocket message: sensor turned on
test_gateway.publisher.dispatch("Test", ("devolo.SirenMultiLevelSwitch:Test", 1)) test_gateway.publisher.dispatch("Test", ("devolo.SirenMultiLevelSwitch:Test", 1))