wyzeapy.services.sensor_service
1# Copyright (c) 2021. Mulliken, LLC - All Rights Reserved 2# You may use, distribute and modify this code under the terms 3# of the attached license. You should have received a copy of 4# the license with this file. If not, please write to: 5# katie@mulliken.net to receive a copy 6import asyncio 7import logging 8from threading import Thread 9from typing import List, Callable, Tuple, Optional 10 11from aiohttp import ClientOSError, ContentTypeError 12 13from ..exceptions import UnknownApiError 14from .base_service import BaseService 15from ..types import Device, PropertyIDs, DeviceTypes 16 17_LOGGER = logging.getLogger(__name__) 18 19 20class Sensor(Device): 21 detected: bool = False 22 23 24class SensorService(BaseService): 25 _updater_thread: Optional[Thread] = None 26 _subscribers: List[Tuple[Sensor, Callable[[Sensor], None]]] = [] 27 28 async def update(self, sensor: Sensor) -> Sensor: 29 # Get updated device_params 30 async with BaseService._update_lock: 31 sensor.device_params = await self.get_updated_params(sensor.mac) 32 properties = await self._get_device_info(sensor) 33 34 for property in properties['data']['property_list']: 35 pid = property['pid'] 36 value = property['value'] 37 38 try: 39 if PropertyIDs(pid) == PropertyIDs.CONTACT_STATE: 40 sensor.detected = value == "1" 41 if PropertyIDs(pid) == PropertyIDs.MOTION_STATE: 42 sensor.detected = value == "1" 43 except ValueError: 44 pass 45 46 return sensor 47 48 async def register_for_updates(self, sensor: Sensor, callback: Callable[[Sensor], None]): 49 _LOGGER.debug(f"Registering sensor: {sensor.nickname} for updates") 50 loop = asyncio.get_event_loop() 51 if not self._updater_thread: 52 self._updater_thread = Thread(target=self.update_worker, args=[loop, ], daemon=True) 53 self._updater_thread.start() 54 55 self._subscribers.append((sensor, callback)) 56 57 async def deregister_for_updates(self, sensor: Sensor): 58 self._subscribers = [(sense, callback) for sense, callback in self._subscribers if sense.mac != sensor.mac] 59 60 def update_worker(self, loop): 61 while True: 62 for sensor, callback in self._subscribers: 63 _LOGGER.debug(f"Providing update for {sensor.nickname}") 64 try: 65 callback(asyncio.run_coroutine_threadsafe(self.update(sensor), loop).result()) 66 except UnknownApiError as e: 67 _LOGGER.warning(f"The update method detected an UnknownApiError: {e}") 68 except ClientOSError as e: 69 _LOGGER.error(f"A network error was detected: {e}") 70 except ContentTypeError as e: 71 _LOGGER.error(f"Server returned unexpected ContentType: {e}") 72 73 async def get_sensors(self) -> List[Sensor]: 74 if self._devices is None: 75 self._devices = await self.get_object_list() 76 77 sensors = [Sensor(device.raw_dict) for device in self._devices if 78 device.type is DeviceTypes.MOTION_SENSOR or 79 device.type is DeviceTypes.CONTACT_SENSOR] 80 return [Sensor(sensor.raw_dict) for sensor in sensors]
25class SensorService(BaseService): 26 _updater_thread: Optional[Thread] = None 27 _subscribers: List[Tuple[Sensor, Callable[[Sensor], None]]] = [] 28 29 async def update(self, sensor: Sensor) -> Sensor: 30 # Get updated device_params 31 async with BaseService._update_lock: 32 sensor.device_params = await self.get_updated_params(sensor.mac) 33 properties = await self._get_device_info(sensor) 34 35 for property in properties['data']['property_list']: 36 pid = property['pid'] 37 value = property['value'] 38 39 try: 40 if PropertyIDs(pid) == PropertyIDs.CONTACT_STATE: 41 sensor.detected = value == "1" 42 if PropertyIDs(pid) == PropertyIDs.MOTION_STATE: 43 sensor.detected = value == "1" 44 except ValueError: 45 pass 46 47 return sensor 48 49 async def register_for_updates(self, sensor: Sensor, callback: Callable[[Sensor], None]): 50 _LOGGER.debug(f"Registering sensor: {sensor.nickname} for updates") 51 loop = asyncio.get_event_loop() 52 if not self._updater_thread: 53 self._updater_thread = Thread(target=self.update_worker, args=[loop, ], daemon=True) 54 self._updater_thread.start() 55 56 self._subscribers.append((sensor, callback)) 57 58 async def deregister_for_updates(self, sensor: Sensor): 59 self._subscribers = [(sense, callback) for sense, callback in self._subscribers if sense.mac != sensor.mac] 60 61 def update_worker(self, loop): 62 while True: 63 for sensor, callback in self._subscribers: 64 _LOGGER.debug(f"Providing update for {sensor.nickname}") 65 try: 66 callback(asyncio.run_coroutine_threadsafe(self.update(sensor), loop).result()) 67 except UnknownApiError as e: 68 _LOGGER.warning(f"The update method detected an UnknownApiError: {e}") 69 except ClientOSError as e: 70 _LOGGER.error(f"A network error was detected: {e}") 71 except ContentTypeError as e: 72 _LOGGER.error(f"Server returned unexpected ContentType: {e}") 73 74 async def get_sensors(self) -> List[Sensor]: 75 if self._devices is None: 76 self._devices = await self.get_object_list() 77 78 sensors = [Sensor(device.raw_dict) for device in self._devices if 79 device.type is DeviceTypes.MOTION_SENSOR or 80 device.type is DeviceTypes.CONTACT_SENSOR] 81 return [Sensor(sensor.raw_dict) for sensor in sensors]
Base service class for interacting with Wyze devices.
29 async def update(self, sensor: Sensor) -> Sensor: 30 # Get updated device_params 31 async with BaseService._update_lock: 32 sensor.device_params = await self.get_updated_params(sensor.mac) 33 properties = await self._get_device_info(sensor) 34 35 for property in properties['data']['property_list']: 36 pid = property['pid'] 37 value = property['value'] 38 39 try: 40 if PropertyIDs(pid) == PropertyIDs.CONTACT_STATE: 41 sensor.detected = value == "1" 42 if PropertyIDs(pid) == PropertyIDs.MOTION_STATE: 43 sensor.detected = value == "1" 44 except ValueError: 45 pass 46 47 return sensor
49 async def register_for_updates(self, sensor: Sensor, callback: Callable[[Sensor], None]): 50 _LOGGER.debug(f"Registering sensor: {sensor.nickname} for updates") 51 loop = asyncio.get_event_loop() 52 if not self._updater_thread: 53 self._updater_thread = Thread(target=self.update_worker, args=[loop, ], daemon=True) 54 self._updater_thread.start() 55 56 self._subscribers.append((sensor, callback))
def
update_worker(self, loop):
61 def update_worker(self, loop): 62 while True: 63 for sensor, callback in self._subscribers: 64 _LOGGER.debug(f"Providing update for {sensor.nickname}") 65 try: 66 callback(asyncio.run_coroutine_threadsafe(self.update(sensor), loop).result()) 67 except UnknownApiError as e: 68 _LOGGER.warning(f"The update method detected an UnknownApiError: {e}") 69 except ClientOSError as e: 70 _LOGGER.error(f"A network error was detected: {e}") 71 except ContentTypeError as e: 72 _LOGGER.error(f"Server returned unexpected ContentType: {e}")
74 async def get_sensors(self) -> List[Sensor]: 75 if self._devices is None: 76 self._devices = await self.get_object_list() 77 78 sensors = [Sensor(device.raw_dict) for device in self._devices if 79 device.type is DeviceTypes.MOTION_SENSOR or 80 device.type is DeviceTypes.CONTACT_SENSOR] 81 return [Sensor(sensor.raw_dict) for sensor in sensors]