""" This module describes the connection between Home Assistant and Wyze for the Sensors """ import logging import time from typing import Callable, List, Any from homeassistant.components.binary_sensor import ( BinarySensorEntity, BinarySensorDeviceClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import HomeAssistant from wyzeapy import Wyzeapy, CameraService, SensorService from wyzeapy.services.camera_service import Camera from wyzeapy.services.sensor_service import Sensor from wyzeapy.types import DeviceTypes from .token_manager import token_exception_handler from .const import DOMAIN, CONF_CLIENT _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by Wyze" @token_exception_handler async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable[[List[Any], bool], None], ): """ This function sets up the config entry for use in Home Assistant :param hass: Home Assistant instance :param config_entry: The current config_entry :param async_add_entities: This function adds entities to the config_entry :return: """ _LOGGER.debug("""Creating new WyzeApi binary sensor component""") client: Wyzeapy = hass.data[DOMAIN][config_entry.entry_id][CONF_CLIENT] sensor_service = await client.sensor_service camera_service = await client.camera_service cameras = [ WyzeCameraMotion(camera_service, camera) for camera in await camera_service.get_cameras() ] sensors = [ WyzeSensor(sensor_service, sensor) for sensor in await sensor_service.get_sensors() ] async_add_entities(cameras, True) async_add_entities(sensors, True) class WyzeSensor(BinarySensorEntity): """ A representation of the WyzeSensor for use in Home Assistant """ def __init__(self, sensor_service: SensorService, sensor: Sensor): """Initializes the class""" self._sensor_service = sensor_service self._sensor = sensor self._last_event = int(str(int(time.time())) + "000") async def async_added_to_hass(self) -> None: """Registers for updates when the entity is added to Home Assistant""" await self._sensor_service.register_for_updates( self._sensor, self.process_update ) async def async_will_remove_from_hass(self) -> None: await self._sensor_service.deregister_for_updates(self._sensor) def process_update(self, sensor: Sensor): """ This function processes an update for the Wyze Sensor :param sensor: The sensor with the updated values """ self._sensor = sensor self.schedule_update_ha_state() @property def device_info(self): return { "identifiers": {(DOMAIN, self._sensor.mac)}, "name": self.name, "manufacturer": "WyzeLabs", "model": self._sensor.product_model, } @property def available(self) -> bool: return True @property def name(self): """Return the display name of this switch.""" return self._sensor.nickname @property def should_poll(self) -> bool: return False @property def is_on(self): """Return true if sensor detects motion""" return self._sensor.detected @property def unique_id(self): return "{}-motion".format(self._sensor.mac) @property def extra_state_attributes(self): """Return device attributes of the entity.""" return { ATTR_ATTRIBUTION: ATTRIBUTION, "device model": self._sensor.product_model, "mac": self.unique_id, } @property def device_class(self): # pylint: disable=R1705 if self._sensor.type is DeviceTypes.MOTION_SENSOR: return BinarySensorDeviceClass.MOTION elif self._sensor.type is DeviceTypes.CONTACT_SENSOR: return BinarySensorDeviceClass.DOOR else: raise RuntimeError( f"The device type {self._sensor.type} is not supported by this class" ) class WyzeCameraMotion(BinarySensorEntity): """ A representation of the Wyze Camera for use as a binary sensor in Home Assistant """ _is_on = False _last_event = time.time() * 1000 def __init__(self, camera_service: CameraService, camera: Camera): self._camera_service = camera_service self._camera = camera @property def device_info(self): return { "identifiers": {(DOMAIN, self._camera.mac)}, "name": self.name, "manufacturer": "WyzeLabs", "model": self._camera.product_model, } @property def available(self) -> bool: return self._camera.available @property def name(self): """Return the display name of this switch.""" return self._camera.nickname @property def should_poll(self) -> bool: return False @property def is_on(self): """Return true if the binary sensor is on""" return self._is_on @property def unique_id(self): return "{}-motion".format(self._camera.mac) @property def extra_state_attributes(self): """Return device attributes of the entity.""" return { ATTR_ATTRIBUTION: ATTRIBUTION, "device model": self._camera.product_model, "mac": self.unique_id, } @property def device_class(self): return BinarySensorDeviceClass.MOTION async def async_added_to_hass(self) -> None: await self._camera_service.register_for_updates( self._camera, self.process_update ) async def async_will_remove_from_hass(self) -> None: await self._camera_service.deregister_for_updates(self._camera) @token_exception_handler def process_update(self, camera: Camera) -> None: """ Is called by the update worker for events to update the values in this sensor :param camera: An updated version of the current camera """ self._camera = camera if camera.last_event_ts > self._last_event: self._is_on = True self._last_event = camera.last_event_ts else: self._is_on = False self._last_event = camera.last_event_ts self.schedule_update_ha_state()