"""Support for Nest Thermostat binary sensors.""" from itertools import chain import logging from homeassistant.components.binary_sensor import ( DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_MOTION, DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_SOUND, BinarySensorEntity, ) from homeassistant.const import CONF_MONITORED_CONDITIONS from . import CONF_BINARY_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice _LOGGER = logging.getLogger(__name__) BINARY_TYPES = {"online": DEVICE_CLASS_CONNECTIVITY} CLIMATE_BINARY_TYPES = { "fan": None, "is_using_emergency_heat": "heat", "is_locked": None, "has_leaf": None, } CAMERA_BINARY_TYPES = { "motion_detected": DEVICE_CLASS_MOTION, "sound_detected": DEVICE_CLASS_SOUND, "person_detected": DEVICE_CLASS_OCCUPANCY, } STRUCTURE_BINARY_TYPES = {"away": None} STRUCTURE_BINARY_STATE_MAP = {"away": {"away": True, "home": False}} _BINARY_TYPES_DEPRECATED = [ "hvac_ac_state", "hvac_aux_heater_state", "hvac_heater_state", "hvac_heat_x2_state", "hvac_heat_x3_state", "hvac_alt_heat_state", "hvac_alt_heat_x2_state", "hvac_emer_heat_state", ] _VALID_BINARY_SENSOR_TYPES = { **BINARY_TYPES, **CLIMATE_BINARY_TYPES, **CAMERA_BINARY_TYPES, **STRUCTURE_BINARY_TYPES, } def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Nest binary sensors. No longer used. """ async def async_setup_entry(hass, entry, async_add_entities): """Set up a Nest binary sensor based on a config entry.""" nest = hass.data[DATA_NEST] discovery_info = hass.data.get(DATA_NEST_CONFIG, {}).get(CONF_BINARY_SENSORS, {}) # Add all available binary sensors if no Nest binary sensor config is set if discovery_info == {}: conditions = _VALID_BINARY_SENSOR_TYPES else: conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {}) for variable in conditions: if variable in _BINARY_TYPES_DEPRECATED: wstr = ( f"{variable} is no a longer supported " "monitored_conditions. See " "https://www.home-assistant.io/integrations/binary_sensor.nest/ " "for valid options." ) _LOGGER.error(wstr) def get_binary_sensors(): """Get the Nest binary sensors.""" sensors = [] for structure in nest.structures(): sensors += [ NestBinarySensor(structure, None, variable) for variable in conditions if variable in STRUCTURE_BINARY_TYPES ] device_chain = chain(nest.thermostats(), nest.smoke_co_alarms(), nest.cameras()) for structure, device in device_chain: sensors += [ NestBinarySensor(structure, device, variable) for variable in conditions if variable in BINARY_TYPES ] sensors += [ NestBinarySensor(structure, device, variable) for variable in conditions if variable in CLIMATE_BINARY_TYPES and device.is_thermostat ] if device.is_camera: sensors += [ NestBinarySensor(structure, device, variable) for variable in conditions if variable in CAMERA_BINARY_TYPES ] for activity_zone in device.activity_zones: sensors += [ NestActivityZoneSensor(structure, device, activity_zone) ] return sensors async_add_entities(await hass.async_add_executor_job(get_binary_sensors), True) class NestBinarySensor(NestSensorDevice, BinarySensorEntity): """Represents a Nest binary sensor.""" @property def is_on(self): """Return true if the binary sensor is on.""" return self._state @property def device_class(self): """Return the device class of the binary sensor.""" return _VALID_BINARY_SENSOR_TYPES.get(self.variable) def update(self): """Retrieve latest state.""" value = getattr(self.device, self.variable) if self.variable in STRUCTURE_BINARY_TYPES: self._state = bool(STRUCTURE_BINARY_STATE_MAP[self.variable].get(value)) else: self._state = bool(value) class NestActivityZoneSensor(NestBinarySensor): """Represents a Nest binary sensor for activity in a zone.""" def __init__(self, structure, device, zone): """Initialize the sensor.""" super().__init__(structure, device, "") self.zone = zone self._name = f"{self._name} {self.zone.name} activity" @property def unique_id(self): """Return unique id based on camera serial and zone id.""" return f"{self.device.serial}-{self.zone.zone_id}" @property def device_class(self): """Return the device class of the binary sensor.""" return DEVICE_CLASS_MOTION def update(self): """Retrieve latest state.""" self._state = self.device.has_ongoing_motion_in_zone(self.zone.zone_id)