diff --git a/.coveragerc b/.coveragerc index aae365530d9..550bf857e1b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -157,7 +157,10 @@ omit = homeassistant/components/denonavr/media_player.py homeassistant/components/deutsche_bahn/sensor.py homeassistant/components/devolo_home_control/__init__.py + homeassistant/components/devolo_home_control/binary_sensor.py homeassistant/components/devolo_home_control/const.py + homeassistant/components/devolo_home_control/devolo_device.py + homeassistant/components/devolo_home_control/subscriber.py homeassistant/components/devolo_home_control/switch.py homeassistant/components/dht/sensor.py homeassistant/components/digital_ocean/* diff --git a/homeassistant/components/devolo_home_control/binary_sensor.py b/homeassistant/components/devolo_home_control/binary_sensor.py new file mode 100644 index 00000000000..87af86f02af --- /dev/null +++ b/homeassistant/components/devolo_home_control/binary_sensor.py @@ -0,0 +1,72 @@ +"""Platform for binary sensor integration.""" +import logging + +from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.typing import HomeAssistantType + +from .const import DOMAIN +from .devolo_device import DevoloDeviceEntity + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistantType, entry: ConfigEntry, async_add_entities +) -> None: + """Get all binary sensor and multi level sensor devices and setup them via config entry.""" + entities = [] + + for device in hass.data[DOMAIN]["homecontrol"].binary_sensor_devices: + for binary_sensor in device.binary_sensor_property: + entities.append( + DevoloBinaryDeviceEntity( + homecontrol=hass.data[DOMAIN]["homecontrol"], + device_instance=device, + element_uid=binary_sensor, + ) + ) + + async_add_entities(entities, False) + + +class DevoloBinaryDeviceEntity(DevoloDeviceEntity, BinarySensorEntity): + """Representation of a binary sensor within devolo Home Control.""" + + def __init__(self, homecontrol, device_instance, element_uid): + """Initialize a devolo binary sensor.""" + if device_instance.binary_sensor_property.get(element_uid).sub_type != "": + name = f"{device_instance.itemName} {device_instance.binary_sensor_property.get(element_uid).sub_type}" + else: + name = f"{device_instance.itemName} {device_instance.binary_sensor_property.get(element_uid).sensor_type}" + + super().__init__( + homecontrol=homecontrol, + device_instance=device_instance, + element_uid=element_uid, + name=name, + sync=self._sync, + ) + + self._binary_sensor_property = self._device_instance.binary_sensor_property.get( + self._unique_id + ) + + self._state = self._binary_sensor_property.state + + self._subscriber = None + + @property + def is_on(self): + """Return the state.""" + return self._state + + def _sync(self, message=None): + """Update the binary sensor state.""" + if message[0].startswith("devolo.BinarySensor"): + self._state = self._device_instance.binary_sensor_property[message[0]].state + elif message[0].startswith("hdm"): + self._available = self._device_instance.is_online() + else: + _LOGGER.debug("No valid message received: %s", message) + self.schedule_update_ha_state() diff --git a/homeassistant/components/devolo_home_control/const.py b/homeassistant/components/devolo_home_control/const.py index 0d5bb9a3356..d5f3ca44ca4 100644 --- a/homeassistant/components/devolo_home_control/const.py +++ b/homeassistant/components/devolo_home_control/const.py @@ -3,6 +3,6 @@ DOMAIN = "devolo_home_control" DEFAULT_MYDEVOLO = "https://www.mydevolo.com" DEFAULT_MPRM = "https://homecontrol.mydevolo.com" -PLATFORMS = ["switch"] +PLATFORMS = ["binary_sensor", "switch"] CONF_MYDEVOLO = "mydevolo_url" CONF_HOMECONTROL = "home_control_url" diff --git a/homeassistant/components/devolo_home_control/devolo_device.py b/homeassistant/components/devolo_home_control/devolo_device.py new file mode 100644 index 00000000000..c7986981afc --- /dev/null +++ b/homeassistant/components/devolo_home_control/devolo_device.py @@ -0,0 +1,84 @@ +"""Base class for a device entity integrated in devolo Home Control.""" +import logging + +from homeassistant.helpers.entity import Entity + +from .const import DOMAIN +from .subscriber import Subscriber + +_LOGGER = logging.getLogger(__name__) + +ATTR_BATTERY_LEVEL = "battery_level" + + +class DevoloDeviceEntity(Entity): + """Representation of a sensor within devolo Home Control.""" + + def __init__(self, homecontrol, device_instance, element_uid, name, sync): + """Initialize a devolo device entity.""" + self._device_instance = device_instance + self._name = name + self._unique_id = element_uid + self._homecontrol = homecontrol + self._available = device_instance.is_online() + + # Get the brand and model information + self._brand = device_instance.brand + self._model = device_instance.name + + self._state_attrs = {} + if hasattr(self._device_instance, "batteryLevel"): + self._state_attrs = {ATTR_BATTERY_LEVEL: self._device_instance.batteryLevel} + + self.subscriber = None + self.sync_callback = sync + + async def async_added_to_hass(self) -> None: + """Call when entity is added to hass.""" + self.subscriber = Subscriber( + self._device_instance.itemName, callback=self.sync_callback + ) + self._homecontrol.publisher.register( + self._device_instance.uid, self.subscriber, self.sync_callback + ) + + async def async_will_remove_from_hass(self) -> None: + """Call when entity is removed or disabled.""" + self._homecontrol.publisher.unregister( + self._device_instance.uid, self.subscriber + ) + + @property + def unique_id(self): + """Return the unique ID of the entity.""" + return self._unique_id + + @property + def device_info(self): + """Return the device info.""" + return { + "identifiers": {(DOMAIN, self._device_instance.uid)}, + "name": self._device_instance.itemName, + "manufacturer": self._brand, + "model": self._model, + } + + @property + def device_state_attributes(self): + """Return the state attributes of the device.""" + return self._state_attrs + + @property + def should_poll(self): + """Return the polling state.""" + return False + + @property + def name(self): + """Return the display name of this entity.""" + return self._name + + @property + def available(self) -> bool: + """Return the online state.""" + return self._available diff --git a/homeassistant/components/devolo_home_control/subscriber.py b/homeassistant/components/devolo_home_control/subscriber.py new file mode 100644 index 00000000000..d291e4b174f --- /dev/null +++ b/homeassistant/components/devolo_home_control/subscriber.py @@ -0,0 +1,19 @@ +"""Subscriber for devolo home control API publisher.""" + +import logging + +_LOGGER = logging.getLogger(__name__) + + +class Subscriber: + """Subscriber class for the publisher in mprm websocket class.""" + + def __init__(self, name, callback): + """Initiate the subscriber.""" + self.name = name + self.callback = callback + + def update(self, message): + """Trigger hass to update the device.""" + _LOGGER.debug('%s got message "%s"', self.name, message) + self.callback(message)