1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00
ha-core/homeassistant/components/homematicip_cloud/binary_sensor.py
epenet 8c49fff699
Add setup type hints [h-i] (#63603)
Co-authored-by: epenet <epenet@users.noreply.github.com>
2022-01-07 16:26:57 +01:00

558 lines
19 KiB
Python

"""Support for HomematicIP Cloud binary sensor."""
from __future__ import annotations
from typing import Any
from homematicip.aio.device import (
AsyncAccelerationSensor,
AsyncContactInterface,
AsyncDevice,
AsyncFullFlushContactInterface,
AsyncFullFlushContactInterface6,
AsyncMotionDetectorIndoor,
AsyncMotionDetectorOutdoor,
AsyncMotionDetectorPushButton,
AsyncPluggableMainsFailureSurveillance,
AsyncPresenceDetectorIndoor,
AsyncRainSensor,
AsyncRotaryHandleSensor,
AsyncShutterContact,
AsyncShutterContactMagnetic,
AsyncSmokeDetector,
AsyncTiltVibrationSensor,
AsyncWaterSensor,
AsyncWeatherSensor,
AsyncWeatherSensorPlus,
AsyncWeatherSensorPro,
AsyncWiredInput32,
)
from homematicip.aio.group import AsyncSecurityGroup, AsyncSecurityZoneGroup
from homematicip.base.enums import SmokeDetectorAlarmType, WindowState
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericEntity
from .hap import HomematicipHAP
ATTR_ACCELERATION_SENSOR_MODE = "acceleration_sensor_mode"
ATTR_ACCELERATION_SENSOR_NEUTRAL_POSITION = "acceleration_sensor_neutral_position"
ATTR_ACCELERATION_SENSOR_SENSITIVITY = "acceleration_sensor_sensitivity"
ATTR_ACCELERATION_SENSOR_TRIGGER_ANGLE = "acceleration_sensor_trigger_angle"
ATTR_INTRUSION_ALARM = "intrusion_alarm"
ATTR_MOISTURE_DETECTED = "moisture_detected"
ATTR_MOTION_DETECTED = "motion_detected"
ATTR_POWER_MAINS_FAILURE = "power_mains_failure"
ATTR_PRESENCE_DETECTED = "presence_detected"
ATTR_SMOKE_DETECTOR_ALARM = "smoke_detector_alarm"
ATTR_TODAY_SUNSHINE_DURATION = "today_sunshine_duration_in_minutes"
ATTR_WATER_LEVEL_DETECTED = "water_level_detected"
ATTR_WINDOW_STATE = "window_state"
GROUP_ATTRIBUTES = {
"moistureDetected": ATTR_MOISTURE_DETECTED,
"motionDetected": ATTR_MOTION_DETECTED,
"powerMainsFailure": ATTR_POWER_MAINS_FAILURE,
"presenceDetected": ATTR_PRESENCE_DETECTED,
"waterlevelDetected": ATTR_WATER_LEVEL_DETECTED,
}
SAM_DEVICE_ATTRIBUTES = {
"accelerationSensorNeutralPosition": ATTR_ACCELERATION_SENSOR_NEUTRAL_POSITION,
"accelerationSensorMode": ATTR_ACCELERATION_SENSOR_MODE,
"accelerationSensorSensitivity": ATTR_ACCELERATION_SENSOR_SENSITIVITY,
"accelerationSensorTriggerAngle": ATTR_ACCELERATION_SENSOR_TRIGGER_ANGLE,
}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the HomematicIP Cloud binary sensor from a config entry."""
hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id]
entities: list[HomematicipGenericEntity] = [HomematicipCloudConnectionSensor(hap)]
for device in hap.home.devices:
if isinstance(device, AsyncAccelerationSensor):
entities.append(HomematicipAccelerationSensor(hap, device))
if isinstance(device, AsyncTiltVibrationSensor):
entities.append(HomematicipTiltVibrationSensor(hap, device))
if isinstance(device, AsyncWiredInput32):
for channel in range(1, 33):
entities.append(
HomematicipMultiContactInterface(hap, device, channel=channel)
)
elif isinstance(device, AsyncFullFlushContactInterface6):
for channel in range(1, 7):
entities.append(
HomematicipMultiContactInterface(hap, device, channel=channel)
)
elif isinstance(
device, (AsyncContactInterface, AsyncFullFlushContactInterface)
):
entities.append(HomematicipContactInterface(hap, device))
if isinstance(
device,
(AsyncShutterContact, AsyncShutterContactMagnetic),
):
entities.append(HomematicipShutterContact(hap, device))
if isinstance(device, AsyncRotaryHandleSensor):
entities.append(HomematicipShutterContact(hap, device, True))
if isinstance(
device,
(
AsyncMotionDetectorIndoor,
AsyncMotionDetectorOutdoor,
AsyncMotionDetectorPushButton,
),
):
entities.append(HomematicipMotionDetector(hap, device))
if isinstance(device, AsyncPluggableMainsFailureSurveillance):
entities.append(
HomematicipPluggableMainsFailureSurveillanceSensor(hap, device)
)
if isinstance(device, AsyncPresenceDetectorIndoor):
entities.append(HomematicipPresenceDetector(hap, device))
if isinstance(device, AsyncSmokeDetector):
entities.append(HomematicipSmokeDetector(hap, device))
if isinstance(device, AsyncWaterSensor):
entities.append(HomematicipWaterDetector(hap, device))
if isinstance(
device, (AsyncRainSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro)
):
entities.append(HomematicipRainSensor(hap, device))
if isinstance(
device, (AsyncWeatherSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro)
):
entities.append(HomematicipStormSensor(hap, device))
entities.append(HomematicipSunshineSensor(hap, device))
if isinstance(device, AsyncDevice) and device.lowBat is not None:
entities.append(HomematicipBatterySensor(hap, device))
for group in hap.home.groups:
if isinstance(group, AsyncSecurityGroup):
entities.append(HomematicipSecuritySensorGroup(hap, device=group))
elif isinstance(group, AsyncSecurityZoneGroup):
entities.append(HomematicipSecurityZoneSensorGroup(hap, device=group))
if entities:
async_add_entities(entities)
class HomematicipCloudConnectionSensor(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP cloud connection sensor."""
def __init__(self, hap: HomematicipHAP) -> None:
"""Initialize the cloud connection sensor."""
super().__init__(hap, hap.home)
@property
def name(self) -> str:
"""Return the name cloud connection entity."""
name = "Cloud Connection"
# Add a prefix to the name if the homematic ip home has a name.
return name if not self._home.name else f"{self._home.name} {name}"
@property
def device_info(self) -> DeviceInfo:
"""Return device specific attributes."""
# Adds a sensor to the existing HAP device
return DeviceInfo(
identifiers={
# Serial numbers of Homematic IP device
(HMIPC_DOMAIN, self._home.id)
}
)
@property
def icon(self) -> str:
"""Return the icon of the access point entity."""
return (
"mdi:access-point-network"
if self._home.connected
else "mdi:access-point-network-off"
)
@property
def is_on(self) -> bool:
"""Return true if hap is connected to cloud."""
return self._home.connected
@property
def available(self) -> bool:
"""Sensor is always available."""
return True
class HomematicipBaseActionSensor(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP base action sensor."""
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.MOVING
@property
def is_on(self) -> bool:
"""Return true if acceleration is detected."""
return self._device.accelerationSensorTriggered
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the acceleration sensor."""
state_attr = super().extra_state_attributes
for attr, attr_key in SAM_DEVICE_ATTRIBUTES.items():
if attr_value := getattr(self._device, attr, None):
state_attr[attr_key] = attr_value
return state_attr
class HomematicipAccelerationSensor(HomematicipBaseActionSensor):
"""Representation of the HomematicIP acceleration sensor."""
class HomematicipTiltVibrationSensor(HomematicipBaseActionSensor):
"""Representation of the HomematicIP tilt vibration sensor."""
class HomematicipMultiContactInterface(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP multi room/area contact interface."""
def __init__(
self,
hap: HomematicipHAP,
device,
channel=1,
is_multi_channel=True,
) -> None:
"""Initialize the multi contact entity."""
super().__init__(
hap, device, channel=channel, is_multi_channel=is_multi_channel
)
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.OPENING
@property
def is_on(self) -> bool | None:
"""Return true if the contact interface is on/open."""
if self._device.functionalChannels[self._channel].windowState is None:
return None
return (
self._device.functionalChannels[self._channel].windowState
!= WindowState.CLOSED
)
class HomematicipContactInterface(HomematicipMultiContactInterface, BinarySensorEntity):
"""Representation of the HomematicIP contact interface."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the multi contact entity."""
super().__init__(hap, device, is_multi_channel=False)
class HomematicipShutterContact(HomematicipMultiContactInterface, BinarySensorEntity):
"""Representation of the HomematicIP shutter contact."""
def __init__(
self, hap: HomematicipHAP, device, has_additional_state: bool = False
) -> None:
"""Initialize the shutter contact."""
super().__init__(hap, device, is_multi_channel=False)
self.has_additional_state = has_additional_state
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.DOOR
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the Shutter Contact."""
state_attr = super().extra_state_attributes
if self.has_additional_state:
window_state = getattr(self._device, "windowState", None)
if window_state and window_state != WindowState.CLOSED:
state_attr[ATTR_WINDOW_STATE] = window_state
return state_attr
class HomematicipMotionDetector(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP motion detector."""
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.MOTION
@property
def is_on(self) -> bool:
"""Return true if motion is detected."""
return self._device.motionDetected
class HomematicipPresenceDetector(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP presence detector."""
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.PRESENCE
@property
def is_on(self) -> bool:
"""Return true if presence is detected."""
return self._device.presenceDetected
class HomematicipSmokeDetector(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP smoke detector."""
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.SMOKE
@property
def is_on(self) -> bool:
"""Return true if smoke is detected."""
if self._device.smokeDetectorAlarmType:
return (
self._device.smokeDetectorAlarmType
== SmokeDetectorAlarmType.PRIMARY_ALARM
)
return False
class HomematicipWaterDetector(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP water detector."""
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.MOISTURE
@property
def is_on(self) -> bool:
"""Return true, if moisture or waterlevel is detected."""
return self._device.moistureDetected or self._device.waterlevelDetected
class HomematicipStormSensor(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP storm sensor."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize storm sensor."""
super().__init__(hap, device, "Storm")
@property
def icon(self) -> str:
"""Return the icon."""
return "mdi:weather-windy" if self.is_on else "mdi:pinwheel-outline"
@property
def is_on(self) -> bool:
"""Return true, if storm is detected."""
return self._device.storm
class HomematicipRainSensor(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP rain sensor."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize rain sensor."""
super().__init__(hap, device, "Raining")
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.MOISTURE
@property
def is_on(self) -> bool:
"""Return true, if it is raining."""
return self._device.raining
class HomematicipSunshineSensor(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP sunshine sensor."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize sunshine sensor."""
super().__init__(hap, device, post="Sunshine")
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.LIGHT
@property
def is_on(self) -> bool:
"""Return true if sun is shining."""
return self._device.sunshine
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the illuminance sensor."""
state_attr = super().extra_state_attributes
today_sunshine_duration = getattr(self._device, "todaySunshineDuration", None)
if today_sunshine_duration:
state_attr[ATTR_TODAY_SUNSHINE_DURATION] = today_sunshine_duration
return state_attr
class HomematicipBatterySensor(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP low battery sensor."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize battery sensor."""
super().__init__(hap, device, post="Battery")
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.BATTERY
@property
def is_on(self) -> bool:
"""Return true if battery is low."""
return self._device.lowBat
class HomematicipPluggableMainsFailureSurveillanceSensor(
HomematicipGenericEntity, BinarySensorEntity
):
"""Representation of the HomematicIP pluggable mains failure surveillance sensor."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize pluggable mains failure surveillance sensor."""
super().__init__(hap, device)
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.POWER
@property
def is_on(self) -> bool:
"""Return true if power mains fails."""
return not self._device.powerMainsFailure
class HomematicipSecurityZoneSensorGroup(HomematicipGenericEntity, BinarySensorEntity):
"""Representation of the HomematicIP security zone sensor group."""
def __init__(self, hap: HomematicipHAP, device, post: str = "SecurityZone") -> None:
"""Initialize security zone group."""
device.modelType = f"HmIP-{post}"
super().__init__(hap, device, post=post)
@property
def device_class(self) -> str:
"""Return the class of this sensor."""
return BinarySensorDeviceClass.SAFETY
@property
def available(self) -> bool:
"""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 extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the security zone group."""
state_attr = super().extra_state_attributes
for attr, attr_key in GROUP_ATTRIBUTES.items():
if attr_value := getattr(self._device, attr, None):
state_attr[attr_key] = attr_value
window_state = getattr(self._device, "windowState", None)
if window_state and window_state != WindowState.CLOSED:
state_attr[ATTR_WINDOW_STATE] = str(window_state)
return state_attr
@property
def is_on(self) -> bool:
"""Return true if security issue detected."""
if (
self._device.motionDetected
or self._device.presenceDetected
or self._device.unreach
or self._device.sabotage
):
return True
if (
self._device.windowState is not None
and self._device.windowState != WindowState.CLOSED
):
return True
return False
class HomematicipSecuritySensorGroup(
HomematicipSecurityZoneSensorGroup, BinarySensorEntity
):
"""Representation of the HomematicIP security group."""
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize security group."""
super().__init__(hap, device, post="Sensors")
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the security group."""
state_attr = super().extra_state_attributes
smoke_detector_at = getattr(self._device, "smokeDetectorAlarmType", None)
if smoke_detector_at:
if smoke_detector_at == SmokeDetectorAlarmType.PRIMARY_ALARM:
state_attr[ATTR_SMOKE_DETECTOR_ALARM] = str(smoke_detector_at)
if smoke_detector_at == SmokeDetectorAlarmType.INTRUSION_ALARM:
state_attr[ATTR_INTRUSION_ALARM] = str(smoke_detector_at)
return state_attr
@property
def is_on(self) -> bool:
"""Return true if safety issue detected."""
if super().is_on:
# parent is on
return True
if (
self._device.powerMainsFailure
or self._device.moistureDetected
or self._device.waterlevelDetected
or self._device.lowBat
or self._device.dutyCycle
):
return True
if (
self._device.smokeDetectorAlarmType is not None
and self._device.smokeDetectorAlarmType != SmokeDetectorAlarmType.IDLE_OFF
):
return True
return False