diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index 592d234225c..bb5999108ce 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -2,7 +2,6 @@ import logging from homematicip.aio.group import AsyncSecurityZoneGroup -from homematicip.aio.home import AsyncHome from homematicip.base.enums import WindowState from homeassistant.components.alarm_control_panel import AlarmControlPanel @@ -16,6 +15,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -31,15 +31,15 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP alrm control panel from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] security_zones = [] - for group in home.groups: + for group in hap.home.groups: if isinstance(group, AsyncSecurityZoneGroup): security_zones.append(group) if security_zones: - devices.append(HomematicipAlarmControlPanel(home, security_zones)) + devices.append(HomematicipAlarmControlPanel(hap, security_zones)) if devices: async_add_entities(devices) @@ -48,9 +48,9 @@ async def async_setup_entry( class HomematicipAlarmControlPanel(AlarmControlPanel): """Representation of an alarm control panel.""" - def __init__(self, home: AsyncHome, security_zones) -> None: + def __init__(self, hap: HomematicipHAP, security_zones) -> None: """Initialize the alarm control panel.""" - self._home = home + self._home = hap.home self.alarm_state = STATE_ALARM_DISARMED for security_zone in security_zones: diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 1114f10b622..964ab4d8234 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -20,7 +20,6 @@ from homematicip.aio.device import ( AsyncWeatherSensorPro, ) from homematicip.aio.group import AsyncSecurityGroup, AsyncSecurityZoneGroup -from homematicip.aio.home import AsyncHome from homematicip.base.enums import SmokeDetectorAlarmType, WindowState from homeassistant.components.binary_sensor import ( @@ -41,6 +40,7 @@ from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice from .device import ATTR_GROUP_MEMBER_UNREACHABLE +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -85,18 +85,18 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP Cloud binary sensor from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] - for device in home.devices: + for device in hap.home.devices: if isinstance(device, AsyncAccelerationSensor): - devices.append(HomematicipAccelerationSensor(home, device)) + devices.append(HomematicipAccelerationSensor(hap, device)) if isinstance(device, (AsyncContactInterface, AsyncFullFlushContactInterface)): - devices.append(HomematicipContactInterface(home, device)) + devices.append(HomematicipContactInterface(hap, device)) if isinstance( device, (AsyncShutterContact, AsyncShutterContactMagnetic, AsyncRotaryHandleSensor), ): - devices.append(HomematicipShutterContact(home, device)) + devices.append(HomematicipShutterContact(hap, device)) if isinstance( device, ( @@ -105,28 +105,28 @@ async def async_setup_entry( AsyncMotionDetectorPushButton, ), ): - devices.append(HomematicipMotionDetector(home, device)) + devices.append(HomematicipMotionDetector(hap, device)) if isinstance(device, AsyncPresenceDetectorIndoor): - devices.append(HomematicipPresenceDetector(home, device)) + devices.append(HomematicipPresenceDetector(hap, device)) if isinstance(device, AsyncSmokeDetector): - devices.append(HomematicipSmokeDetector(home, device)) + devices.append(HomematicipSmokeDetector(hap, device)) if isinstance(device, AsyncWaterSensor): - devices.append(HomematicipWaterDetector(home, device)) + devices.append(HomematicipWaterDetector(hap, device)) if isinstance(device, (AsyncWeatherSensorPlus, AsyncWeatherSensorPro)): - devices.append(HomematicipRainSensor(home, device)) + devices.append(HomematicipRainSensor(hap, device)) if isinstance( device, (AsyncWeatherSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro) ): - devices.append(HomematicipStormSensor(home, device)) - devices.append(HomematicipSunshineSensor(home, device)) + devices.append(HomematicipStormSensor(hap, device)) + devices.append(HomematicipSunshineSensor(hap, device)) if isinstance(device, AsyncDevice) and device.lowBat is not None: - devices.append(HomematicipBatterySensor(home, device)) + devices.append(HomematicipBatterySensor(hap, device)) - for group in home.groups: + for group in hap.home.groups: if isinstance(group, AsyncSecurityGroup): - devices.append(HomematicipSecuritySensorGroup(home, group)) + devices.append(HomematicipSecuritySensorGroup(hap, group)) elif isinstance(group, AsyncSecurityZoneGroup): - devices.append(HomematicipSecurityZoneSensorGroup(home, group)) + devices.append(HomematicipSecurityZoneSensorGroup(hap, group)) if devices: async_add_entities(devices) @@ -249,9 +249,9 @@ class HomematicipWaterDetector(HomematicipGenericDevice, BinarySensorDevice): class HomematicipStormSensor(HomematicipGenericDevice, BinarySensorDevice): """Representation of a HomematicIP Cloud storm sensor.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize storm sensor.""" - super().__init__(home, device, "Storm") + super().__init__(hap, device, "Storm") @property def icon(self) -> str: @@ -267,9 +267,9 @@ class HomematicipStormSensor(HomematicipGenericDevice, BinarySensorDevice): class HomematicipRainSensor(HomematicipGenericDevice, BinarySensorDevice): """Representation of a HomematicIP Cloud rain sensor.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize rain sensor.""" - super().__init__(home, device, "Raining") + super().__init__(hap, device, "Raining") @property def device_class(self) -> str: @@ -285,9 +285,9 @@ class HomematicipRainSensor(HomematicipGenericDevice, BinarySensorDevice): class HomematicipSunshineSensor(HomematicipGenericDevice, BinarySensorDevice): """Representation of a HomematicIP Cloud sunshine sensor.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize sunshine sensor.""" - super().__init__(home, device, "Sunshine") + super().__init__(hap, device, "Sunshine") @property def device_class(self) -> str: @@ -314,9 +314,9 @@ class HomematicipSunshineSensor(HomematicipGenericDevice, BinarySensorDevice): class HomematicipBatterySensor(HomematicipGenericDevice, BinarySensorDevice): """Representation of a HomematicIP Cloud low battery sensor.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize battery sensor.""" - super().__init__(home, device, "Battery") + super().__init__(hap, device, "Battery") @property def device_class(self) -> str: @@ -332,10 +332,10 @@ class HomematicipBatterySensor(HomematicipGenericDevice, BinarySensorDevice): class HomematicipSecurityZoneSensorGroup(HomematicipGenericDevice, BinarySensorDevice): """Representation of a HomematicIP Cloud security zone group.""" - def __init__(self, home: AsyncHome, device, post: str = "SecurityZone") -> None: + def __init__(self, hap: HomematicipHAP, device, post: str = "SecurityZone") -> None: """Initialize security zone group.""" device.modelType = f"HmIP-{post}" - super().__init__(home, device, post) + super().__init__(hap, device, post) @property def device_class(self) -> str: @@ -389,9 +389,9 @@ class HomematicipSecuritySensorGroup( ): """Representation of a HomematicIP security group.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize security group.""" - super().__init__(home, device, "Sensors") + super().__init__(hap, device, "Sensors") @property def device_state_attributes(self): diff --git a/homeassistant/components/homematicip_cloud/climate.py b/homeassistant/components/homematicip_cloud/climate.py index 794a8b44cbc..cf1c1baabe0 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -4,7 +4,6 @@ from typing import Awaitable from homematicip.aio.device import AsyncHeatingThermostat, AsyncHeatingThermostatCompact from homematicip.aio.group import AsyncHeatingGroup -from homematicip.aio.home import AsyncHome from homematicip.base.enums import AbsenceType from homematicip.functionalHomes import IndoorClimateHome @@ -24,6 +23,7 @@ from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -41,11 +41,11 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP climate from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] - for device in home.groups: + for device in hap.home.groups: if isinstance(device, AsyncHeatingGroup): - devices.append(HomematicipHeatingGroup(home, device)) + devices.append(HomematicipHeatingGroup(hap, device)) if devices: async_add_entities(devices) @@ -54,13 +54,13 @@ async def async_setup_entry( class HomematicipHeatingGroup(HomematicipGenericDevice, ClimateDevice): """Representation of a HomematicIP heating group.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize heating group.""" device.modelType = "Group-Heating" self._simple_heating = None if device.actualTemperature is None: self._simple_heating = _get_first_heating_thermostat(device) - super().__init__(home, device) + super().__init__(hap, device) @property def temperature_unit(self) -> str: diff --git a/homeassistant/components/homematicip_cloud/cover.py b/homeassistant/components/homematicip_cloud/cover.py index 9252c4322d9..c5821c4f75e 100644 --- a/homeassistant/components/homematicip_cloud/cover.py +++ b/homeassistant/components/homematicip_cloud/cover.py @@ -31,13 +31,13 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP cover from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] - for device in home.devices: + for device in hap.home.devices: if isinstance(device, AsyncFullFlushBlind): - devices.append(HomematicipCoverSlats(home, device)) + devices.append(HomematicipCoverSlats(hap, device)) elif isinstance(device, AsyncFullFlushShutter): - devices.append(HomematicipCoverShutter(home, device)) + devices.append(HomematicipCoverShutter(hap, device)) if devices: async_add_entities(devices) diff --git a/homeassistant/components/homematicip_cloud/device.py b/homeassistant/components/homematicip_cloud/device.py index 0389e0b9935..3d64014883d 100644 --- a/homeassistant/components/homematicip_cloud/device.py +++ b/homeassistant/components/homematicip_cloud/device.py @@ -4,17 +4,17 @@ from typing import Optional from homematicip.aio.device import AsyncDevice from homematicip.aio.group import AsyncGroup -from homematicip.aio.home import AsyncHome from homeassistant.components import homematicip_cloud from homeassistant.core import callback from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.entity import Entity +from .hap import HomematicipHAP + _LOGGER = logging.getLogger(__name__) ATTR_MODEL_TYPE = "model_type" -ATTR_GROUP_ID = "group_id" ATTR_ID = "id" ATTR_IS_GROUP = "is_group" # RSSI HAP -> Device @@ -46,15 +46,16 @@ DEVICE_ATTRIBUTES = { "id": ATTR_ID, } -GROUP_ATTRIBUTES = {"modelType": ATTR_MODEL_TYPE, "id": ATTR_GROUP_ID} +GROUP_ATTRIBUTES = {"modelType": ATTR_MODEL_TYPE} class HomematicipGenericDevice(Entity): """Representation of an HomematicIP generic device.""" - def __init__(self, home: AsyncHome, device, post: Optional[str] = None) -> None: + def __init__(self, hap: HomematicipHAP, device, post: Optional[str] = None) -> None: """Initialize the generic device.""" - self._home = home + self._hap = hap + self._home = hap.home self._device = device self.post = post # Marker showing that the HmIP device hase been removed. @@ -81,6 +82,7 @@ class HomematicipGenericDevice(Entity): async def async_added_to_hass(self): """Register callbacks.""" + self._hap.hmip_device_by_entity_id[self.entity_id] = self._device self._device.on_update(self._async_device_changed) self._device.on_remove(self._async_device_removed) @@ -104,6 +106,7 @@ class HomematicipGenericDevice(Entity): # Only go further if the device/entity should be removed from registries # due to a removal of the HmIP device. if self.hmip_device_removed: + del self._hap.hmip_device_by_entity_id[self.entity_id] await self.async_remove_from_registries() async def async_remove_from_registries(self) -> None: diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index abba183d339..22ab1fd617c 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -79,6 +79,7 @@ class HomematicipHAP: self._retry_task = None self._tries = 0 self._accesspoint_connected = True + self.hmip_device_by_entity_id = {} async def async_setup(self, tries: int = 0): """Initialize connection.""" diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index 42ff6d30478..80ee4cc5743 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -9,7 +9,6 @@ from homematicip.aio.device import ( AsyncFullFlushDimmer, AsyncPluggableDimmer, ) -from homematicip.aio.home import AsyncHome from homematicip.base.enums import RGBColorState from homematicip.base.functionalChannels import NotificationLightChannel @@ -25,6 +24,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -41,26 +41,26 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP Cloud lights from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] - for device in home.devices: + for device in hap.home.devices: if isinstance(device, AsyncBrandSwitchMeasuring): - devices.append(HomematicipLightMeasuring(home, device)) + devices.append(HomematicipLightMeasuring(hap, device)) elif isinstance(device, AsyncBrandSwitchNotificationLight): - devices.append(HomematicipLight(home, device)) + devices.append(HomematicipLight(hap, device)) devices.append( - HomematicipNotificationLight(home, device, device.topLightChannelIndex) + HomematicipNotificationLight(hap, device, device.topLightChannelIndex) ) devices.append( HomematicipNotificationLight( - home, device, device.bottomLightChannelIndex + hap, device, device.bottomLightChannelIndex ) ) elif isinstance( device, (AsyncDimmer, AsyncPluggableDimmer, AsyncBrandDimmer, AsyncFullFlushDimmer), ): - devices.append(HomematicipDimmer(home, device)) + devices.append(HomematicipDimmer(hap, device)) if devices: async_add_entities(devices) @@ -69,9 +69,9 @@ async def async_setup_entry( class HomematicipLight(HomematicipGenericDevice, Light): """Representation of a HomematicIP Cloud light device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the light device.""" - super().__init__(home, device) + super().__init__(hap, device) @property def is_on(self) -> bool: @@ -107,9 +107,9 @@ class HomematicipLightMeasuring(HomematicipLight): class HomematicipDimmer(HomematicipGenericDevice, Light): """Representation of HomematicIP Cloud dimmer light device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the dimmer light device.""" - super().__init__(home, device) + super().__init__(hap, device) @property def is_on(self) -> bool: @@ -143,13 +143,13 @@ class HomematicipDimmer(HomematicipGenericDevice, Light): class HomematicipNotificationLight(HomematicipGenericDevice, Light): """Representation of HomematicIP Cloud dimmer light device.""" - def __init__(self, home: AsyncHome, device, channel: int) -> None: + def __init__(self, hap: HomematicipHAP, device, channel: int) -> None: """Initialize the dimmer light device.""" self.channel = channel if self.channel == 2: - super().__init__(home, device, "Top") + super().__init__(hap, device, "Top") else: - super().__init__(home, device, "Bottom") + super().__init__(hap, device, "Bottom") self._color_switcher = { RGBColorState.WHITE: [0.0, 0.0], diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index ceb7fc39fd7..18d483f6adf 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -20,7 +20,6 @@ from homematicip.aio.device import ( AsyncWeatherSensorPlus, AsyncWeatherSensorPro, ) -from homematicip.aio.home import AsyncHome from homematicip.base.enums import ValveState from homeassistant.config_entries import ConfigEntry @@ -36,6 +35,7 @@ from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice from .device import ATTR_IS_GROUP, ATTR_MODEL_TYPE +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -55,12 +55,12 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP Cloud sensors from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home - devices = [HomematicipAccesspointStatus(home)] - for device in home.devices: + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + devices = [HomematicipAccesspointStatus(hap)] + for device in hap.home.devices: if isinstance(device, (AsyncHeatingThermostat, AsyncHeatingThermostatCompact)): - devices.append(HomematicipHeatingThermostat(home, device)) - devices.append(HomematicipTemperatureSensor(home, device)) + devices.append(HomematicipHeatingThermostat(hap, device)) + devices.append(HomematicipTemperatureSensor(hap, device)) if isinstance( device, ( @@ -72,8 +72,8 @@ async def async_setup_entry( AsyncWeatherSensorPro, ), ): - devices.append(HomematicipTemperatureSensor(home, device)) - devices.append(HomematicipHumiditySensor(home, device)) + devices.append(HomematicipTemperatureSensor(hap, device)) + devices.append(HomematicipHumiditySensor(hap, device)) if isinstance( device, ( @@ -87,7 +87,7 @@ async def async_setup_entry( AsyncWeatherSensorPro, ), ): - devices.append(HomematicipIlluminanceSensor(home, device)) + devices.append(HomematicipIlluminanceSensor(hap, device)) if isinstance( device, ( @@ -96,15 +96,15 @@ async def async_setup_entry( AsyncFullFlushSwitchMeasuring, ), ): - devices.append(HomematicipPowerSensor(home, device)) + devices.append(HomematicipPowerSensor(hap, device)) if isinstance( device, (AsyncWeatherSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro) ): - devices.append(HomematicipWindspeedSensor(home, device)) + devices.append(HomematicipWindspeedSensor(hap, device)) if isinstance(device, (AsyncWeatherSensorPlus, AsyncWeatherSensorPro)): - devices.append(HomematicipTodayRainSensor(home, device)) + devices.append(HomematicipTodayRainSensor(hap, device)) if isinstance(device, AsyncPassageDetector): - devices.append(HomematicipPassageDetectorDeltaCounter(home, device)) + devices.append(HomematicipPassageDetectorDeltaCounter(hap, device)) if devices: async_add_entities(devices) @@ -113,9 +113,9 @@ async def async_setup_entry( class HomematicipAccesspointStatus(HomematicipGenericDevice): """Representation of an HomeMaticIP Cloud access point.""" - def __init__(self, home: AsyncHome) -> None: + def __init__(self, hap: HomematicipHAP) -> None: """Initialize access point device.""" - super().__init__(home, home) + super().__init__(hap, hap.home) @property def device_info(self): @@ -162,9 +162,9 @@ class HomematicipAccesspointStatus(HomematicipGenericDevice): class HomematicipHeatingThermostat(HomematicipGenericDevice): """Representation of a HomematicIP heating thermostat device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize heating thermostat device.""" - super().__init__(home, device, "Heating") + super().__init__(hap, device, "Heating") @property def icon(self) -> str: @@ -191,9 +191,9 @@ class HomematicipHeatingThermostat(HomematicipGenericDevice): class HomematicipHumiditySensor(HomematicipGenericDevice): """Representation of a HomematicIP Cloud humidity device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the thermometer device.""" - super().__init__(home, device, "Humidity") + super().__init__(hap, device, "Humidity") @property def device_class(self) -> str: @@ -214,9 +214,9 @@ class HomematicipHumiditySensor(HomematicipGenericDevice): class HomematicipTemperatureSensor(HomematicipGenericDevice): """Representation of a HomematicIP Cloud thermometer device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the thermometer device.""" - super().__init__(home, device, "Temperature") + super().__init__(hap, device, "Temperature") @property def device_class(self) -> str: @@ -251,9 +251,9 @@ class HomematicipTemperatureSensor(HomematicipGenericDevice): class HomematicipIlluminanceSensor(HomematicipGenericDevice): """Representation of a HomematicIP Illuminance device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" - super().__init__(home, device, "Illuminance") + super().__init__(hap, device, "Illuminance") @property def device_class(self) -> str: @@ -277,9 +277,9 @@ class HomematicipIlluminanceSensor(HomematicipGenericDevice): class HomematicipPowerSensor(HomematicipGenericDevice): """Representation of a HomematicIP power measuring device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" - super().__init__(home, device, "Power") + super().__init__(hap, device, "Power") @property def device_class(self) -> str: @@ -300,9 +300,9 @@ class HomematicipPowerSensor(HomematicipGenericDevice): class HomematicipWindspeedSensor(HomematicipGenericDevice): """Representation of a HomematicIP wind speed sensor.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" - super().__init__(home, device, "Windspeed") + super().__init__(hap, device, "Windspeed") @property def state(self) -> float: @@ -333,9 +333,9 @@ class HomematicipWindspeedSensor(HomematicipGenericDevice): class HomematicipTodayRainSensor(HomematicipGenericDevice): """Representation of a HomematicIP rain counter of a day sensor.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" - super().__init__(home, device, "Today Rain") + super().__init__(hap, device, "Today Rain") @property def state(self) -> float: @@ -351,10 +351,6 @@ class HomematicipTodayRainSensor(HomematicipGenericDevice): class HomematicipPassageDetectorDeltaCounter(HomematicipGenericDevice): """Representation of a HomematicIP passage detector delta counter.""" - def __init__(self, home: AsyncHome, device) -> None: - """Initialize the device.""" - super().__init__(home, device) - @property def state(self) -> int: """Representation of the HomematicIP passage detector delta counter value.""" diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index 7994aa446b8..3b54d3fc279 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -12,7 +12,6 @@ from homematicip.aio.device import ( AsyncPrintedCircuitBoardSwitchBattery, ) from homematicip.aio.group import AsyncSwitchingGroup -from homematicip.aio.home import AsyncHome from homeassistant.components.switch import SwitchDevice from homeassistant.config_entries import ConfigEntry @@ -20,6 +19,7 @@ from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice from .device import ATTR_GROUP_MEMBER_UNREACHABLE +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -33,9 +33,9 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP switch from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] - for device in home.devices: + for device in hap.home.devices: if isinstance(device, AsyncBrandSwitchMeasuring): # BrandSwitchMeasuring inherits PlugableSwitchMeasuring # This device is implemented in the light platform and will @@ -44,24 +44,24 @@ async def async_setup_entry( elif isinstance( device, (AsyncPlugableSwitchMeasuring, AsyncFullFlushSwitchMeasuring) ): - devices.append(HomematicipSwitchMeasuring(home, device)) + devices.append(HomematicipSwitchMeasuring(hap, device)) elif isinstance( device, (AsyncPlugableSwitch, AsyncPrintedCircuitBoardSwitchBattery) ): - devices.append(HomematicipSwitch(home, device)) + devices.append(HomematicipSwitch(hap, device)) elif isinstance(device, AsyncOpenCollector8Module): for channel in range(1, 9): - devices.append(HomematicipMultiSwitch(home, device, channel)) + devices.append(HomematicipMultiSwitch(hap, device, channel)) elif isinstance(device, AsyncMultiIOBox): for channel in range(1, 3): - devices.append(HomematicipMultiSwitch(home, device, channel)) + devices.append(HomematicipMultiSwitch(hap, device, channel)) elif isinstance(device, AsyncPrintedCircuitBoardSwitch2): for channel in range(1, 3): - devices.append(HomematicipMultiSwitch(home, device, channel)) + devices.append(HomematicipMultiSwitch(hap, device, channel)) - for group in home.groups: + for group in hap.home.groups: if isinstance(group, AsyncSwitchingGroup): - devices.append(HomematicipGroupSwitch(home, group)) + devices.append(HomematicipGroupSwitch(hap, group)) if devices: async_add_entities(devices) @@ -70,9 +70,9 @@ async def async_setup_entry( class HomematicipSwitch(HomematicipGenericDevice, SwitchDevice): """representation of a HomematicIP Cloud switch device.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the switch device.""" - super().__init__(home, device) + super().__init__(hap, device) @property def is_on(self) -> bool: @@ -91,10 +91,10 @@ class HomematicipSwitch(HomematicipGenericDevice, SwitchDevice): class HomematicipGroupSwitch(HomematicipGenericDevice, SwitchDevice): """representation of a HomematicIP switching group.""" - def __init__(self, home: AsyncHome, device, post: str = "Group") -> None: + def __init__(self, hap: HomematicipHAP, device, post: str = "Group") -> None: """Initialize switching group.""" device.modelType = f"HmIP-{post}" - super().__init__(home, device, post) + super().__init__(hap, device, post) @property def is_on(self) -> bool: @@ -148,10 +148,10 @@ class HomematicipSwitchMeasuring(HomematicipSwitch): class HomematicipMultiSwitch(HomematicipGenericDevice, SwitchDevice): """Representation of a HomematicIP Cloud multi switch device.""" - def __init__(self, home: AsyncHome, device, channel: int): + def __init__(self, hap: HomematicipHAP, device, channel: int): """Initialize the multi switch device.""" self.channel = channel - super().__init__(home, device, f"Channel{channel}") + super().__init__(hap, device, f"Channel{channel}") @property def unique_id(self) -> str: diff --git a/homeassistant/components/homematicip_cloud/weather.py b/homeassistant/components/homematicip_cloud/weather.py index 0d020312fe9..6b92b639c7a 100644 --- a/homeassistant/components/homematicip_cloud/weather.py +++ b/homeassistant/components/homematicip_cloud/weather.py @@ -6,7 +6,6 @@ from homematicip.aio.device import ( AsyncWeatherSensorPlus, AsyncWeatherSensorPro, ) -from homematicip.aio.home import AsyncHome from homematicip.base.enums import WeatherCondition from homeassistant.components.weather import WeatherEntity @@ -15,6 +14,7 @@ from homeassistant.const import TEMP_CELSIUS from homeassistant.core import HomeAssistant from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -46,15 +46,15 @@ async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP weather sensor from a config entry.""" - home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home + hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] devices = [] - for device in home.devices: + for device in hap.home.devices: if isinstance(device, AsyncWeatherSensorPro): - devices.append(HomematicipWeatherSensorPro(home, device)) + devices.append(HomematicipWeatherSensorPro(hap, device)) elif isinstance(device, (AsyncWeatherSensor, AsyncWeatherSensorPlus)): - devices.append(HomematicipWeatherSensor(home, device)) + devices.append(HomematicipWeatherSensor(hap, device)) - devices.append(HomematicipHomeWeather(home)) + devices.append(HomematicipHomeWeather(hap)) if devices: async_add_entities(devices) @@ -63,9 +63,9 @@ async def async_setup_entry( class HomematicipWeatherSensor(HomematicipGenericDevice, WeatherEntity): """representation of a HomematicIP Cloud weather sensor plus & basic.""" - def __init__(self, home: AsyncHome, device) -> None: + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the weather sensor.""" - super().__init__(home, device) + super().__init__(hap, device) @property def name(self) -> str: @@ -121,10 +121,10 @@ class HomematicipWeatherSensorPro(HomematicipWeatherSensor): class HomematicipHomeWeather(HomematicipGenericDevice, WeatherEntity): """representation of a HomematicIP Cloud home weather.""" - def __init__(self, home: AsyncHome) -> None: + def __init__(self, hap: HomematicipHAP) -> None: """Initialize the home weather.""" - home.modelType = "HmIP-Home-Weather" - super().__init__(home, home) + hap.home.modelType = "HmIP-Home-Weather" + super().__init__(hap, hap.home) @property def available(self) -> bool: diff --git a/tests/components/homematicip_cloud/conftest.py b/tests/components/homematicip_cloud/conftest.py index c301c73b4d0..2c2b020f3a0 100644 --- a/tests/components/homematicip_cloud/conftest.py +++ b/tests/components/homematicip_cloud/conftest.py @@ -6,10 +6,14 @@ import pytest from homeassistant import config_entries from homeassistant.components.homematicip_cloud import ( + CONF_ACCESSPOINT, + CONF_AUTHTOKEN, DOMAIN as HMIPC_DOMAIN, + async_setup as hmip_async_setup, const as hmipc, hap as hmip_hap, ) +from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from .helper import AUTH_TOKEN, HAPID, HomeTemplate @@ -19,7 +23,7 @@ from tests.common import MockConfigEntry, mock_coro @pytest.fixture(name="mock_connection") def mock_connection_fixture(): - """Return a mockked connection.""" + """Return a mocked connection.""" connection = MagicMock(spec=AsyncConnection) def _rest_call_side_effect(path, body=None): @@ -39,7 +43,7 @@ def default_mock_home_fixture(mock_connection): @pytest.fixture(name="hmip_config_entry") def hmip_config_entry_fixture(): - """Create a fake config entriy for homematic ip cloud.""" + """Create a mock config entriy for homematic ip cloud.""" entry_data = { hmipc.HMIPC_HAPID: HAPID, hmipc.HMIPC_AUTHTOKEN: AUTH_TOKEN, @@ -67,9 +71,32 @@ async def default_mock_hap_fixture( hap = hmip_hap.HomematicipHAP(hass, hmip_config_entry) with patch.object(hap, "get_hap", return_value=mock_coro(default_mock_home)): assert await hap.async_setup() is True + default_mock_home.on_update(hap.async_update) + default_mock_home.on_create(hap.async_create_entity) hass.data[HMIPC_DOMAIN] = {HAPID: hap} await hass.async_block_till_done() return hap + + +@pytest.fixture(name="hmip_config") +def hmip_config_fixture(): + """Create a config for homematic ip cloud.""" + + entry_data = {CONF_ACCESSPOINT: HAPID, CONF_AUTHTOKEN: AUTH_TOKEN, CONF_NAME: ""} + + return {hmipc.DOMAIN: [entry_data]} + + +@pytest.fixture(name="mock_hap_with_service") +async def mock_hap_with_service_fixture( + hass: HomeAssistant, default_mock_hap, hmip_config +): + """Create a fake homematic access point with hass services.""" + + await hmip_async_setup(hass, hmip_config) + await hass.async_block_till_done() + hass.data[HMIPC_DOMAIN] = {HAPID: default_mock_hap} + return default_mock_hap diff --git a/tests/components/homematicip_cloud/helper.py b/tests/components/homematicip_cloud/helper.py index 79a5bc0b201..b5e41a6ae86 100644 --- a/tests/components/homematicip_cloud/helper.py +++ b/tests/components/homematicip_cloud/helper.py @@ -1,18 +1,25 @@ """Helper for HomematicIP Cloud Tests.""" import json -from unittest.mock import Mock +from asynctest import Mock from homematicip.aio.class_maps import ( TYPE_CLASS_MAP, TYPE_GROUP_MAP, TYPE_SECURITY_EVENT_MAP, ) +from homematicip.aio.device import AsyncDevice +from homematicip.aio.group import AsyncGroup from homematicip.aio.home import AsyncHome from homematicip.home import Home +from homeassistant.components.homematicip_cloud.device import ( + ATTR_IS_GROUP, + ATTR_MODEL_TYPE, +) + from tests.common import load_fixture -HAPID = "Mock_HAP" +HAPID = "3014F7110000000000000001" AUTH_TOKEN = "1234" HOME_JSON = "homematicip_cloud.json" @@ -21,28 +28,38 @@ def get_and_check_entity_basics( hass, default_mock_hap, entity_id, entity_name, device_model ): """Get and test basic device.""" - ha_entity = hass.states.get(entity_id) - assert ha_entity is not None - assert ha_entity.attributes["model_type"] == device_model - assert ha_entity.name == entity_name + ha_state = hass.states.get(entity_id) + assert ha_state is not None + if device_model: + assert ha_state.attributes[ATTR_MODEL_TYPE] == device_model + assert ha_state.name == entity_name - hmip_device = default_mock_hap.home.template.search_mock_device_by_id( - ha_entity.attributes["id"] - ) - assert hmip_device is not None - return ha_entity, hmip_device + hmip_device = default_mock_hap.hmip_device_by_entity_id.get(entity_id) + if hmip_device: + if isinstance(hmip_device, AsyncDevice): + assert ha_state.attributes[ATTR_IS_GROUP] is False + elif isinstance(hmip_device, AsyncGroup): + assert ha_state.attributes[ATTR_IS_GROUP] is True + return ha_state, hmip_device async def async_manipulate_test_data( - hass, hmip_device, attribute, new_value, channel=1 + hass, hmip_device, attribute, new_value, channel=1, fire_device=None ): """Set new value on hmip device.""" if channel == 1: setattr(hmip_device, attribute, new_value) - functional_channel = hmip_device.functionalChannels[channel] - setattr(functional_channel, attribute, new_value) + if hasattr(hmip_device, "functionalChannels"): + functional_channel = hmip_device.functionalChannels[channel] + setattr(functional_channel, attribute, new_value) + + fire_target = hmip_device if fire_device is None else fire_device + + if isinstance(fire_target, AsyncHome): + fire_target.fire_update_event(fire_target._rawJSONData) # pylint: disable=W0212 + else: + fire_target.fire_update_event() - hmip_device.fire_update_event() await hass.async_block_till_done() @@ -66,8 +83,8 @@ class HomeTemplate(Home): def __init__(self, connection=None): """Init template with connection.""" super().__init__(connection=connection) - self.mock_devices = [] - self.mock_groups = [] + self.label = "Access Point" + self.model_type = "HmIP-HAP" def init_home(self, json_path=HOME_JSON): """Init template with json.""" @@ -78,24 +95,15 @@ class HomeTemplate(Home): def _generate_mocks(self): """Generate mocks for groups and devices.""" + mock_devices = [] for device in self.devices: - self.mock_devices.append(_get_mock(device)) + mock_devices.append(_get_mock(device)) + self.devices = mock_devices + + mock_groups = [] for group in self.groups: - self.mock_groups.append(_get_mock(group)) - - def search_mock_device_by_id(self, device_id): - """Search a device by given id.""" - for device in self.mock_devices: - if device.id == device_id: - return device - return None - - def search_mock_group_by_id(self, group_id): - """Search a group by given id.""" - for group in self.mock_groups: - if group.id == group_id: - return group - return None + mock_groups.append(_get_mock(group)) + self.groups = mock_groups def get_async_home_mock(self): """ @@ -105,19 +113,11 @@ class HomeTemplate(Home): and sets reuired attributes. """ mock_home = Mock( - check_connection=self._connection, - id=HAPID, - connected=True, - dutyCycle=self.dutyCycle, - devices=self.mock_devices, - groups=self.mock_groups, - weather=self.weather, - location=self.location, - label="home label", - template=self, - spec=AsyncHome, + spec=AsyncHome, wraps=self, label="Access Point", modelType="HmIP-HAP" ) + mock_home.__dict__.update(self.__dict__) mock_home.name = "" + return mock_home diff --git a/tests/components/homematicip_cloud/test_binary_sensor.py b/tests/components/homematicip_cloud/test_binary_sensor.py new file mode 100644 index 00000000000..0de2101d287 --- /dev/null +++ b/tests/components/homematicip_cloud/test_binary_sensor.py @@ -0,0 +1,289 @@ +"""Tests for HomematicIP Cloud binary sensor.""" +from homematicip.base.enums import SmokeDetectorAlarmType, WindowState + +from homeassistant.components.homematicip_cloud.binary_sensor import ( + ATTR_ACCELERATION_SENSOR_MODE, + ATTR_ACCELERATION_SENSOR_NEUTRAL_POSITION, + ATTR_ACCELERATION_SENSOR_SENSITIVITY, + ATTR_ACCELERATION_SENSOR_TRIGGER_ANGLE, + ATTR_LOW_BATTERY, + ATTR_MOTION_DETECTED, +) +from homeassistant.const import STATE_OFF, STATE_ON + +from .helper import async_manipulate_test_data, get_and_check_entity_basics + + +async def test_hmip_acceleration_sensor(hass, default_mock_hap): + """Test HomematicipAccelerationSensor.""" + entity_id = "binary_sensor.garagentor" + entity_name = "Garagentor" + device_model = "HmIP-SAM" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_ACCELERATION_SENSOR_MODE] == "FLAT_DECT" + assert ha_state.attributes[ATTR_ACCELERATION_SENSOR_NEUTRAL_POSITION] == "VERTICAL" + assert ( + ha_state.attributes[ATTR_ACCELERATION_SENSOR_SENSITIVITY] == "SENSOR_RANGE_4G" + ) + assert ha_state.attributes[ATTR_ACCELERATION_SENSOR_TRIGGER_ANGLE] == 45 + service_call_counter = len(hmip_device.mock_calls) + + await async_manipulate_test_data( + hass, hmip_device, "accelerationSensorTriggered", False + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + assert len(hmip_device.mock_calls) == service_call_counter + 1 + + await async_manipulate_test_data( + hass, hmip_device, "accelerationSensorTriggered", True + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert len(hmip_device.mock_calls) == service_call_counter + 2 + + +async def test_hmip_contact_interface(hass, default_mock_hap): + """Test HomematicipContactInterface.""" + entity_id = "binary_sensor.kontakt_schnittstelle_unterputz_1_fach" + entity_name = "Kontakt-Schnittstelle Unterputz – 1-fach" + device_model = "HmIP-FCI1" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "windowState", WindowState.OPEN) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + await async_manipulate_test_data(hass, hmip_device, "windowState", None) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + +async def test_hmip_shutter_contact(hass, default_mock_hap): + """Test HomematicipShutterContact.""" + entity_id = "binary_sensor.fenstergriffsensor" + entity_name = "Fenstergriffsensor" + device_model = "HmIP-SRH" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + await async_manipulate_test_data( + hass, hmip_device, "windowState", WindowState.CLOSED + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + await async_manipulate_test_data(hass, hmip_device, "windowState", None) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + +async def test_hmip_motion_detector(hass, default_mock_hap): + """Test HomematicipMotionDetector.""" + entity_id = "binary_sensor.bewegungsmelder_fur_55er_rahmen_innen" + entity_name = "Bewegungsmelder für 55er Rahmen – innen" + device_model = "HmIP-SMI55" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "motionDetected", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_presence_detector(hass, default_mock_hap): + """Test HomematicipPresenceDetector.""" + entity_id = "binary_sensor.spi_1" + entity_name = "SPI_1" + device_model = "HmIP-SPI" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "presenceDetected", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_smoke_detector(hass, default_mock_hap): + """Test HomematicipSmokeDetector.""" + entity_id = "binary_sensor.rauchwarnmelder" + entity_name = "Rauchwarnmelder" + device_model = "HmIP-SWSD" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data( + hass, + hmip_device, + "smokeDetectorAlarmType", + SmokeDetectorAlarmType.PRIMARY_ALARM, + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_water_detector(hass, default_mock_hap): + """Test HomematicipWaterDetector.""" + entity_id = "binary_sensor.wassersensor" + entity_name = "Wassersensor" + device_model = "HmIP-SWD" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "waterlevelDetected", True) + await async_manipulate_test_data(hass, hmip_device, "moistureDetected", False) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + await async_manipulate_test_data(hass, hmip_device, "waterlevelDetected", True) + await async_manipulate_test_data(hass, hmip_device, "moistureDetected", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + await async_manipulate_test_data(hass, hmip_device, "waterlevelDetected", False) + await async_manipulate_test_data(hass, hmip_device, "moistureDetected", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + await async_manipulate_test_data(hass, hmip_device, "waterlevelDetected", False) + await async_manipulate_test_data(hass, hmip_device, "moistureDetected", False) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + +async def test_hmip_storm_sensor(hass, default_mock_hap): + """Test HomematicipStormSensor.""" + entity_id = "binary_sensor.weather_sensor_plus_storm" + entity_name = "Weather Sensor – plus Storm" + device_model = "HmIP-SWO-PL" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "storm", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_rain_sensor(hass, default_mock_hap): + """Test HomematicipRainSensor.""" + entity_id = "binary_sensor.wettersensor_pro_raining" + entity_name = "Wettersensor - pro Raining" + device_model = "HmIP-SWO-PR" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "raining", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_sunshine_sensor(hass, default_mock_hap): + """Test HomematicipSunshineSensor.""" + entity_id = "binary_sensor.wettersensor_pro_sunshine" + entity_name = "Wettersensor - pro Sunshine" + device_model = "HmIP-SWO-PR" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + assert ha_state.attributes["today_sunshine_duration_in_minutes"] == 100 + await async_manipulate_test_data(hass, hmip_device, "sunshine", False) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + +async def test_hmip_battery_sensor(hass, default_mock_hap): + """Test HomematicipSunshineSensor.""" + entity_id = "binary_sensor.wohnungsture_battery" + entity_name = "Wohnungstüre Battery" + device_model = "HMIP-SWDO" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "lowBat", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_security_zone_sensor_group(hass, default_mock_hap): + """Test HomematicipSecurityZoneSensorGroup.""" + entity_id = "binary_sensor.internal_securityzone" + entity_name = "INTERNAL SecurityZone" + device_model = "HmIP-SecurityZone" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + await async_manipulate_test_data(hass, hmip_device, "motionDetected", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_MOTION_DETECTED] is True + + +async def test_hmip_security_sensor_group(hass, default_mock_hap): + """Test HomematicipSecuritySensorGroup.""" + entity_id = "binary_sensor.buro_sensors" + entity_name = "Büro Sensors" + device_model = None + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + assert not ha_state.attributes.get("low_bat") + await async_manipulate_test_data(hass, hmip_device, "lowBat", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_LOW_BATTERY] is True + + await async_manipulate_test_data(hass, hmip_device, "lowBat", False) + await async_manipulate_test_data( + hass, + hmip_device, + "smokeDetectorAlarmType", + SmokeDetectorAlarmType.PRIMARY_ALARM, + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ( + ha_state.attributes["smoke_detector_alarm"] + == SmokeDetectorAlarmType.PRIMARY_ALARM + ) diff --git a/tests/components/homematicip_cloud/test_binary_sensors.py b/tests/components/homematicip_cloud/test_binary_sensors.py deleted file mode 100644 index 4471c5dd7f3..00000000000 --- a/tests/components/homematicip_cloud/test_binary_sensors.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Tests for HomematicIP Cloud lights.""" -import logging - -from tests.components.homematicip_cloud.helper import ( - async_manipulate_test_data, - get_and_check_entity_basics, -) - -_LOGGER = logging.getLogger(__name__) - - -async def test_hmip_sam(hass, default_mock_hap): - """Test HomematicipLight.""" - entity_id = "binary_sensor.garagentor" - entity_name = "Garagentor" - device_model = "HmIP-SAM" - - ha_entity, hmip_device = get_and_check_entity_basics( - hass, default_mock_hap, entity_id, entity_name, device_model - ) - - assert ha_entity.state == "on" - assert ha_entity.attributes["acceleration_sensor_mode"] == "FLAT_DECT" - assert ha_entity.attributes["acceleration_sensor_neutral_position"] == "VERTICAL" - assert ha_entity.attributes["acceleration_sensor_sensitivity"] == "SENSOR_RANGE_4G" - assert ha_entity.attributes["acceleration_sensor_trigger_angle"] == 45 - service_call_counter = len(hmip_device.mock_calls) - - await async_manipulate_test_data( - hass, hmip_device, "accelerationSensorTriggered", False - ) - ha_entity = hass.states.get(entity_id) - assert ha_entity.state == "off" - assert len(hmip_device.mock_calls) == service_call_counter + 1 - - await async_manipulate_test_data( - hass, hmip_device, "accelerationSensorTriggered", True - ) - ha_entity = hass.states.get(entity_id) - assert ha_entity.state == "on" - assert len(hmip_device.mock_calls) == service_call_counter + 2 diff --git a/tests/components/homematicip_cloud/test_light.py b/tests/components/homematicip_cloud/test_light.py new file mode 100644 index 00000000000..a8d4984520c --- /dev/null +++ b/tests/components/homematicip_cloud/test_light.py @@ -0,0 +1,196 @@ +"""Tests for HomematicIP Cloud light.""" +from homematicip.base.enums import RGBColorState + +from homeassistant.components.homematicip_cloud.light import ( + ATTR_ENERGY_COUNTER, + ATTR_POWER_CONSUMPTION, +) +from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_COLOR_NAME +from homeassistant.const import STATE_OFF, STATE_ON + +from .helper import async_manipulate_test_data, get_and_check_entity_basics + + +async def test_hmip_light(hass, default_mock_hap): + """Test HomematicipLight.""" + entity_id = "light.treppe" + entity_name = "Treppe" + device_model = "HmIP-BSL" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + + service_call_counter = len(hmip_device.mock_calls) + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 1 + assert hmip_device.mock_calls[-1][0] == "turn_off" + assert hmip_device.mock_calls[-1][1] == () + + await async_manipulate_test_data(hass, hmip_device, "on", False) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 3 + assert hmip_device.mock_calls[-1][0] == "turn_on" + assert hmip_device.mock_calls[-1][1] == () + + await async_manipulate_test_data(hass, hmip_device, "on", True) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + + +async def test_hmip_notification_light(hass, default_mock_hap): + """Test HomematicipNotificationLight.""" + entity_id = "light.treppe_top_notification" + entity_name = "Treppe Top Notification" + device_model = "HmIP-BSL" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + service_call_counter = len(hmip_device.mock_calls) + + # Send all color via service call. + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level" + assert hmip_device.mock_calls[-1][1] == (2, RGBColorState.RED, 1.0) + + color_list = { + RGBColorState.WHITE: [0.0, 0.0], + RGBColorState.RED: [0.0, 100.0], + RGBColorState.YELLOW: [60.0, 100.0], + RGBColorState.GREEN: [120.0, 100.0], + RGBColorState.TURQUOISE: [180.0, 100.0], + RGBColorState.BLUE: [240.0, 100.0], + RGBColorState.PURPLE: [300.0, 100.0], + } + + for color, hs_color in color_list.items(): + await hass.services.async_call( + "light", + "turn_on", + {"entity_id": entity_id, "hs_color": hs_color}, + blocking=True, + ) + assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level" + assert hmip_device.mock_calls[-1][1] == (2, color, 0.0392156862745098) + + assert len(hmip_device.mock_calls) == service_call_counter + 8 + + assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level" + assert hmip_device.mock_calls[-1][1] == ( + 2, + RGBColorState.PURPLE, + 0.0392156862745098, + ) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1, 2) + await async_manipulate_test_data( + hass, hmip_device, "simpleRGBColorState", RGBColorState.PURPLE, 2 + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_COLOR_NAME] == RGBColorState.PURPLE + assert ha_state.attributes[ATTR_BRIGHTNESS] == 255 + + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 11 + assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level" + assert hmip_device.mock_calls[-1][1] == (2, RGBColorState.PURPLE, 0.0) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, 2) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + +async def test_hmip_dimmer(hass, default_mock_hap): + """Test HomematicipDimmer.""" + entity_id = "light.schlafzimmerlicht" + entity_name = "Schlafzimmerlicht" + device_model = "HmIP-BDT" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + service_call_counter = len(hmip_device.mock_calls) + + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (1,) + + await hass.services.async_call( + "light", + "turn_on", + {"entity_id": entity_id, "brightness_pct": "100"}, + blocking=True, + ) + assert len(hmip_device.mock_calls) == service_call_counter + 2 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (1.0,) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_BRIGHTNESS] == 255 + + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 4 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0,) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + +async def test_hmip_light_measuring(hass, default_mock_hap): + """Test HomematicipLightMeasuring.""" + entity_id = "light.flur_oben" + entity_name = "Flur oben" + device_model = "HmIP-BSM" + + ha_state, hmip_device = get_and_check_entity_basics( + hass, default_mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_OFF + service_call_counter = len(hmip_device.mock_calls) + + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 1 + assert hmip_device.mock_calls[-1][0] == "turn_on" + assert hmip_device.mock_calls[-1][1] == () + await async_manipulate_test_data(hass, hmip_device, "on", True) + await async_manipulate_test_data(hass, hmip_device, "currentPowerConsumption", 50) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_POWER_CONSUMPTION] == 50 + assert ha_state.attributes[ATTR_ENERGY_COUNTER] == 6.33 + + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 4 + assert hmip_device.mock_calls[-1][0] == "turn_off" + assert hmip_device.mock_calls[-1][1] == () + await async_manipulate_test_data(hass, hmip_device, "on", False) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF diff --git a/tests/components/homematicip_cloud/test_lights.py b/tests/components/homematicip_cloud/test_lights.py deleted file mode 100644 index dcf5f76d0a0..00000000000 --- a/tests/components/homematicip_cloud/test_lights.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Tests for HomematicIP Cloud lights.""" -import logging - -from tests.components.homematicip_cloud.helper import ( - async_manipulate_test_data, - get_and_check_entity_basics, -) - -_LOGGER = logging.getLogger(__name__) - - -async def test_hmip_light(hass, default_mock_hap): - """Test HomematicipLight.""" - entity_id = "light.treppe" - entity_name = "Treppe" - device_model = "HmIP-BSL" - - ha_entity, hmip_device = get_and_check_entity_basics( - hass, default_mock_hap, entity_id, entity_name, device_model - ) - - assert ha_entity.state == "on" - - service_call_counter = len(hmip_device.mock_calls) - await hass.services.async_call( - "light", "turn_off", {"entity_id": entity_id}, blocking=True - ) - assert len(hmip_device.mock_calls) == service_call_counter + 1 - assert hmip_device.mock_calls[-1][0] == "turn_off" - await async_manipulate_test_data(hass, hmip_device, "on", False) - ha_entity = hass.states.get(entity_id) - assert ha_entity.state == "off" - - await hass.services.async_call( - "light", "turn_on", {"entity_id": entity_id}, blocking=True - ) - assert len(hmip_device.mock_calls) == service_call_counter + 3 - assert hmip_device.mock_calls[-1][0] == "turn_on" - await async_manipulate_test_data(hass, hmip_device, "on", True) - ha_entity = hass.states.get(entity_id) - assert ha_entity.state == "on" - - -# HomematicipLightMeasuring -# HomematicipDimmer - - -async def test_hmip_notification_light(hass, default_mock_hap): - """Test HomematicipNotificationLight.""" - entity_id = "light.treppe_top_notification" - entity_name = "Treppe Top Notification" - device_model = "HmIP-BSL" - - ha_entity, hmip_device = get_and_check_entity_basics( - hass, default_mock_hap, entity_id, entity_name, device_model - ) - - assert ha_entity.state == "off" - service_call_counter = len(hmip_device.mock_calls) - - await hass.services.async_call( - "light", "turn_on", {"entity_id": entity_id}, blocking=True - ) - assert len(hmip_device.mock_calls) == service_call_counter + 1 - assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level" - await async_manipulate_test_data(hass, hmip_device, "dimLevel", 100, 2) - ha_entity = hass.states.get(entity_id) - assert ha_entity.state == "on" - - await hass.services.async_call( - "light", "turn_off", {"entity_id": entity_id}, blocking=True - ) - assert len(hmip_device.mock_calls) == service_call_counter + 3 - assert hmip_device.mock_calls[-1][0] == "set_rgb_dim_level" - await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, 2) - ha_entity = hass.states.get(entity_id) - assert ha_entity.state == "off" diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index b96bff8fac9..1d3d5bfd8f4 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -2077,6 +2077,144 @@ "type": "SHUTTER_CONTACT", "updateState": "UP_TO_DATE" }, + "3014F7110000000000000108": { + "availableFirmwareVersion": "1.12.6", + "firmwareVersion": "1.12.6", + "firmwareVersionInteger": 68614, + "functionalChannels": { + "0": { + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000000108", + "deviceOverheated": false, + "deviceOverloaded": false, + "deviceUndervoltage": false, + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": [ + "00000000-0000-0000-0000-000000000009" + ], + "index": 0, + "label": "", + "lowBat": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -68, + "rssiPeerValue": -63, + "supportedOptionalFeatures": { + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceOverheated": true, + "IFeatureDeviceOverloaded": false, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false + }, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "currentPowerConsumption": 0.0, + "deviceId": "3014F7110000000000000108", + "energyCounter": 6.333200000000001, + "functionalChannelType": "SWITCH_MEASURING_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000023" + ], + "index": 1, + "label": "", + "on": false, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000000108", + "label": "Flur oben", + "lastStatusUpdate": 1570365990392, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 288, + "modelType": "HmIP-BSM", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000000108", + "type": "BRAND_SWITCH_MEASURING", + "updateState": "UP_TO_DATE" + }, + "3014F7110000000000000109": { + "availableFirmwareVersion": "1.6.2", + "firmwareVersion": "1.6.2", + "firmwareVersionInteger": 67074, + "functionalChannels": { + "0": { + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F7110000000000000109", + "deviceOverheated": null, + "deviceOverloaded": false, + "deviceUndervoltage": false, + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": [ + "00000000-0000-0000-0000-000000000029" + ], + "index": 0, + "label": "", + "lowBat": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -80, + "rssiPeerValue": -73, + "supportedOptionalFeatures": { + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceOverheated": true, + "IFeatureDeviceOverloaded": false, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false + }, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "currentPowerConsumption": 0.0, + "deviceId": "3014F7110000000000000109", + "energyCounter": 0.0011, + "functionalChannelType": "SWITCH_MEASURING_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000030" + ], + "index": 1, + "label": "", + "on": false, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000000011", + "label": "Ausschalter Terrasse Bewegungsmelder", + "lastStatusUpdate": 1570366291250, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 289, + "modelType": "HmIP-FSM", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000000109", + "type": "FULL_FLUSH_SWITCH_MEASURING", + "updateState": "UP_TO_DATE" + }, "3014F7110000000000000008": { "availableFirmwareVersion": "0.0.0", "firmwareVersion": "2.6.2", @@ -2236,6 +2374,57 @@ "type": "PLUGABLE_SWITCH_MEASURING", "updateState": "UP_TO_DATE" }, + "3014F7110000000000000110": { + "availableFirmwareVersion": "0.0.0", + "firmwareVersion": "2.6.2", + "firmwareVersionInteger": 132610, + "functionalChannels": { + "0": { + "configPending": false, + "deviceId": "3014F7110000000000000110", + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": [ + "00000000-0000-0000-0000-000000000017" + ], + "index": 0, + "label": "", + "lowBat": null, + "routerModuleEnabled": true, + "routerModuleSupported": true, + "rssiDeviceValue": -47, + "rssiPeerValue": -49, + "unreach": false + }, + "1": { + "deviceId": "3014F7110000000000000110", + "functionalChannelType": "SWITCH_CHANNEL", + "groupIndex": 1, + "groups": [ + "00000000-0000-0000-0000-000000000018" + ], + "index": 1, + "label": "", + "on": true, + "profileMode": "AUTOMATIC", + "userDesiredProfileMode": "AUTOMATIC" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F7110000000000000110", + "label": "Schrank", + "lastStatusUpdate": 1524513613922, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 262, + "modelType": "HMIP-PS", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F7110000000000000110", + "type": "PLUGABLE_SWITCH", + "updateState": "UP_TO_DATE" + }, "3014F7110000000000000011": { "automaticValveAdaptionNeeded": false, "availableFirmwareVersion": "2.0.2",