From d7f0137bc6bbe8da07e17912e4e9084cea187396 Mon Sep 17 00:00:00 2001 From: Richard Mitchell Date: Thu, 18 Apr 2019 12:40:52 +0100 Subject: [PATCH] More detail and sync-friendly options --- eufy_robovac/robovac.py | 63 +++++++++++++++++++++++++++-------------- eufy_robovac/tuya.py | 29 +++++++++++++++---- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/eufy_robovac/robovac.py b/eufy_robovac/robovac.py index ca58031..7a16306 100644 --- a/eufy_robovac/robovac.py +++ b/eufy_robovac/robovac.py @@ -16,43 +16,53 @@ import logging +from .property import DeviceProperty, StringEnum from .tuya import TuyaDevice _LOGGER = logging.getLogger(__name__) -class WorkMode: +class WorkMode(StringEnum): AUTO = 'auto' NO_SWEEP = 'Nosweep' SMALL_ROOM = 'SmallRoom' EDGE = 'Edge' - Spot = 'Spot' + SPOT = 'Spot' -class Direction: +class Direction(StringEnum): LEFT = 'left' RIGHT = 'right' FORWARD = 'forward' BACKWARD = 'backward' -class WorkStatus: +class WorkStatus(StringEnum): + RUNNING = 'Running' CHARGING = 'Charging' STAND_BY = 'standby' SLEEPING = 'Sleeping' RECHARGING = 'Recharge' -class CleanSpeed: +class CleanSpeed(StringEnum): NO_SUCTION = 'No_suction' STANDARD = 'Standard' BOOST_IQ = 'Boost_IQ' MAX = 'Max' -class ErrorCode: - pass +class ErrorCode(StringEnum): + NO_ERROR = 'no_error' + WHEEL_STUCK = 'Wheel_stuck' + R_BRUSH_STUCK = 'R_brush_stuck' + CRASH_BAR_STUCK = 'Crash_bar_stuck' + SENSOR_DIRTY = 'sensor_dirty' + NOT_ENOUGH_POWER = 'N_enough_pow' + STUCK_5_MIN = 'Stuck_5_min' + FAN_STUCK = 'Fan_stuck' + S_BRUSH_STUCK = 'S_brush_stuck' class Robovac(TuyaDevice): @@ -67,23 +77,34 @@ class Robovac(TuyaDevice): FIND_ROBOT = '103' BATTERY_LEVEL = '104' ERROR_CODE = '106' - KEY_NAMES = { - PLAY_PAUSE: 'play/pause', - DIRECTION: 'direction', - WORK_MODE: 'work mode', - WORK_STATUS: 'work status', - GO_HOME: 'go home', - CLEAN_SPEED: 'clean speed', - FIND_ROBOT: 'find robot', - BATTERY_LEVEL: 'battery level', - ERROR_CODE: 'error code' - } - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + play_pause = DeviceProperty(PLAY_PAUSE) + direction = DeviceProperty(DIRECTION) + work_mode = DeviceProperty(WORK_MODE, WorkMode) + work_status = DeviceProperty(WORK_STATUS, WorkStatus, True) + go_home = DeviceProperty(GO_HOME) + clean_speed = DeviceProperty(CLEAN_SPEED, CleanSpeed) + find_robot = DeviceProperty(FIND_ROBOT) + battery_level = DeviceProperty(BATTERY_LEVEL, read_only=True) + error_code = DeviceProperty(ERROR_CODE, ErrorCode, True) + + async def async_play(self, callback=None): + await self.async_set({self.PLAY_PAUSE: True}, callback) + + async def async_pause(self, callback=None): + await self.async_set({self.PLAY_PAUSE: False}, callback) async def async_start_cleaning(self, callback=None): - await self.async_set({self.WORK_MODE: WorkMode.AUTO}, callback) + await self.async_set({self.WORK_MODE: str(WorkMode.AUTO)}, callback) async def async_go_home(self, callback=None): await self.async_set({self.GO_HOME: True}, callback) + + async def async_set_work_mode(self, work_mode, callback=None): + await self.async_set({self.WORK_MODE: work_mode}, callback) + + async def async_find_robot(self, callback=None): + await self.async_set({self.FIND_ROBOT: True}, callback) + + async def async_set_clean_speed(self, clean_speed, callback=None): + await self.async_set({self.CLEAN_SPEED: str(clean_speed)}, callback) diff --git a/eufy_robovac/tuya.py b/eufy_robovac/tuya.py index 4147cd5..0eeb587 100644 --- a/eufy_robovac/tuya.py +++ b/eufy_robovac/tuya.py @@ -45,6 +45,7 @@ from json.decoder import JSONDecodeError import logging import socket import struct +import sys import time from cryptography.hazmat.backends.openssl import backend as openssl_backend @@ -156,7 +157,7 @@ class RequestResponseCommandMismatch(TuyaException): class TuyaCipher: - """The Tuya encryption cipher""" + """Tuya cryptographic helpers.""" def __init__(self, key, version): """Initialize the cipher.""" @@ -218,6 +219,7 @@ class TuyaCipher: intermediate = digest.finalize().hex() return intermediate[8:24] + def crc(data): """Calculate the Tuya-flavored CRC of some data.""" c = 0xFFFFFFFF @@ -395,6 +397,22 @@ class Message: return cls(command, payload, sequence) +def _call_async(fn, *args): + loop = None + if sys.version_info >= (3, 7): + try: + loop = asyncio.get_running_loop() + except RuntimeError: + pass + + loop = asyncio.get_event_loop() + + def wrapper(fn, *args): + asyncio.ensure_future(fn(*args)) + + loop.call_soon(wrapper, fn, *args) + + class TuyaDevice: """Represents a generic Tuya device.""" @@ -438,10 +456,6 @@ class TuyaDevice: def __str__(self): return "{} ({}:{})".format(self.device_id, self.host, self.port) - @property - def loop(self): - return asyncio.get_running_loop() - async def async_connect(self, callback=None): sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sock.settimeout(self.timeout) @@ -482,7 +496,10 @@ class TuyaDevice: 'dps': dps } message = Message(Message.SET_COMMAND, payload, encrypt_for=self) - return await message.async_send(self, callback) + await message.async_send(self, callback) + + def set(self, dps): + _call_async(self.async_set, dps) async def _async_ping(self): self.last_ping = time.time()