From 5a555102b9958f6a0be0aa3b349b254b0f1a1bd7 Mon Sep 17 00:00:00 2001 From: Markus Jankowski Date: Thu, 7 Mar 2019 11:12:03 +0100 Subject: [PATCH] Fix group-switch availability for Homematic IP (#21640) * Add available=True to groups * Added unreach to stateattributes * Fixed comments * added missing sabotage check * added missing lowBat check * fix typo * apply suggestion Co-Authored-By: SukramJ * apply suggestion Co-Authored-By: SukramJ * applied suggiestions * readded lost str() * fix comment --- .../homematicip_cloud/binary_sensor.py | 45 ++++++++++++------- .../components/homematicip_cloud/device.py | 1 + .../components/homematicip_cloud/switch.py | 23 +++++++++- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 4b82a500bde..d6ce4152001 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -4,6 +4,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.homematicip_cloud import ( DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud.device import ( + ATTR_GROUP_MEMBER_UNREACHABLE) DEPENDENCIES = ['homematicip_cloud'] @@ -31,8 +33,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): AsyncWaterSensor, AsyncRotaryHandleSensor, AsyncMotionDetectorPushButton) - from homematicip.group import ( - SecurityGroup, SecurityZoneGroup) + from homematicip.aio.group import ( + AsyncSecurityGroup, AsyncSecurityZoneGroup) home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home devices = [] @@ -48,9 +50,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): devices.append(HomematicipWaterDetector(home, device)) for group in home.groups: - if isinstance(group, SecurityGroup): + if isinstance(group, AsyncSecurityGroup): devices.append(HomematicipSecuritySensorGroup(home, group)) - elif isinstance(group, SecurityZoneGroup): + elif isinstance(group, AsyncSecurityZoneGroup): devices.append(HomematicipSecurityZoneSensorGroup(home, group)) if devices: @@ -137,27 +139,37 @@ class HomematicipSecurityZoneSensorGroup(HomematicipGenericDevice, """Return the class of this sensor.""" return 'safety' + @property + def available(self): + """Security-Group available.""" + # A security-group must be available, and should not be affected by + # the individual availability of group members. + return True + @property def device_state_attributes(self): """Return the state attributes of the security zone group.""" attr = super().device_state_attributes if self._device.motionDetected: - attr.update({ATTR_MOTIONDETECTED: True}) + attr[ATTR_MOTIONDETECTED] = True if self._device.presenceDetected: - attr.update({ATTR_PRESENCEDETECTED: True}) + attr[ATTR_PRESENCEDETECTED] = True from homematicip.base.enums import WindowState if self._device.windowState is not None and \ self._device.windowState != WindowState.CLOSED: - attr.update({ATTR_WINDOWSTATE: str(self._device.windowState)}) - + attr[ATTR_WINDOWSTATE] = str(self._device.windowState) + if self._device.unreach: + attr[ATTR_GROUP_MEMBER_UNREACHABLE] = True return attr @property def is_on(self): """Return true if security issue detected.""" if self._device.motionDetected or \ - self._device.presenceDetected: + self._device.presenceDetected or \ + self._device.unreach or \ + self._device.sabotage: return True from homematicip.base.enums import WindowState if self._device.windowState is not None and \ @@ -180,29 +192,30 @@ class HomematicipSecuritySensorGroup(HomematicipSecurityZoneSensorGroup, attr = super().device_state_attributes if self._device.powerMainsFailure: - attr.update({ATTR_POWERMAINSFAILURE: True}) + attr[ATTR_POWERMAINSFAILURE] = True if self._device.moistureDetected: - attr.update({ATTR_MOISTUREDETECTED: True}) + attr[ATTR_MOISTUREDETECTED] = True if self._device.waterlevelDetected: - attr.update({ATTR_WATERLEVELDETECTED: True}) + attr[ATTR_WATERLEVELDETECTED] = True from homematicip.base.enums import SmokeDetectorAlarmType if self._device.smokeDetectorAlarmType is not None and \ self._device.smokeDetectorAlarmType != \ SmokeDetectorAlarmType.IDLE_OFF: - attr.update({ATTR_SMOKEDETECTORALARM: str( - self._device.smokeDetectorAlarmType)}) + attr[ATTR_SMOKEDETECTORALARM] = \ + str(self._device.smokeDetectorAlarmType) return attr @property def is_on(self): - """Return true if security issue detected.""" + """Return true if safety issue detected.""" parent_is_on = super().is_on from homematicip.base.enums import SmokeDetectorAlarmType if parent_is_on or \ self._device.powerMainsFailure or \ self._device.moistureDetected or \ - self._device.waterlevelDetected: + self._device.waterlevelDetected or \ + self._device.lowBat: return True if self._device.smokeDetectorAlarmType is not None and \ self._device.smokeDetectorAlarmType != \ diff --git a/homeassistant/components/homematicip_cloud/device.py b/homeassistant/components/homematicip_cloud/device.py index 85cc3c0c77a..9940e6960db 100644 --- a/homeassistant/components/homematicip_cloud/device.py +++ b/homeassistant/components/homematicip_cloud/device.py @@ -21,6 +21,7 @@ ATTR_OPERATION_LOCK = 'operation_lock' ATTR_SABOTAGE = 'sabotage' ATTR_STATUS_UPDATE = 'status_update' ATTR_UNREACHABLE = 'unreachable' +ATTR_GROUP_MEMBER_UNREACHABLE = 'group_member_unreachable' class HomematicipGenericDevice(Entity): diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index f129febb5e7..74f50f87b25 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -3,6 +3,8 @@ import logging from homeassistant.components.homematicip_cloud import ( DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice) +from homeassistant.components.homematicip_cloud.device import ( + ATTR_GROUP_MEMBER_UNREACHABLE) from homeassistant.components.switch import SwitchDevice DEPENDENCIES = ['homematicip_cloud'] @@ -30,7 +32,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): AsyncOpenCollector8Module, ) - from homematicip.group import SwitchingGroup + from homematicip.aio.group import AsyncSwitchingGroup home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home devices = [] @@ -50,7 +52,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): devices.append(HomematicipMultiSwitch(home, device, channel)) for group in home.groups: - if isinstance(group, SwitchingGroup): + if isinstance(group, AsyncSwitchingGroup): devices.append( HomematicipGroupSwitch(home, group)) @@ -92,6 +94,23 @@ class HomematicipGroupSwitch(HomematicipGenericDevice, SwitchDevice): """Return true if group is on.""" return self._device.on + @property + def available(self): + """Switch-Group available.""" + # A switch-group must be available, and should not be affected by the + # individual availability of group members. + # This allows switching even when individual group members + # are not available. + return True + + @property + def device_state_attributes(self): + """Return the state attributes of the switch-group.""" + attr = {} + if self._device.unreach: + attr[ATTR_GROUP_MEMBER_UNREACHABLE] = True + return attr + async def async_turn_on(self, **kwargs): """Turn the group on.""" await self._device.turn_on()