eupy/eufy.py

169 lines
3.7 KiB
Python
Executable File

#!/usr/bin/env python3
import click
from datetime import datetime
class EufyRawIR:
""" Low level IR signal modulation """
_IR_PREAMBLE = (3000, 3000)
_IR_0 = (400, 600)
_IR_1 = (400, 1600)
_IR_TRAILER = (400, 20000)
def _modulate(self, message):
signal = list(self._IR_PREAMBLE)
for b in message:
for i in range(8):
if (b << i) & 0x80:
signal.extend(list(self._IR_1))
else:
signal.extend(list(self._IR_0))
signal.extend(list(self._IR_TRAILER))
return 3*signal
def sendRaw(self, message):
print(self._modulate(message))
class EufyIR(EufyRawIR):
""" High level Eufy commands """
_HEADER = 0x68
CLEAN_AUTO = 0x5d
CLEAN_SPOT = 0x8c
CLEAN_EDGE = 0x9c
CLEAN_ROOM = 0xad
STOP = 0x4f
POWER_MAX = 0x1c
POWER_BOOSTIQ = 0x1d
POWER_STANDARD = 0x1e
MOVE_FORWARD = 0x2f
MOVE_BACKWARD = 0x7f
MOVE_CCW = 0x3f
MOVE_CW = 0x6f
SET_TIME = 0xbf
SET_SCHEDULE = 0xcf
CANCEL_SCHEDULE = 0xdf
RETURN_BASE = 0xef
def __init__(self):
self._schedule = 0xff
def _message(self, code):
now = datetime.now()
msg = [self._HEADER, code, now.hour, now.minute, self._schedule]
checksum = sum(msg) & 0xff
msg.append(checksum)
return msg
def send(self, code):
self.sendRaw(self._message(code))
def cleanAuto(self):
self.send(self.CLEAN_AUTO)
def cleanSpot(self):
self.send(self.CLEAN_SPOT)
def cleanEdge(self):
self.send(self.CLEAN_EDGE)
def cleanRoom(self):
self.send(self.CLEAN_ROOM)
def start(self):
self.cleanAuto()
def stop(self):
self.send(self.STOP)
def powerMax(self):
self.send(self.POWER_MAX)
def powerBoostIQ(self):
self.send(self.POWER_BOOSTIQ)
def powerStandard(self):
self.send(self.POWER_STANDARD)
def moveForward(self):
self.send(self.MOVE_FORWARD)
def moveBackward(self):
self.send(self.MOVE_BACKWARD)
def moveCCW(self):
self.send(self.MOVE_CCW)
def moveLeft(self):
self.moveCCW()
def moveCW(self):
self.send(self.MOVE_CW)
def moveRight(self):
self.moveCW()
def setTime(self):
self.send(self.SET_TIME)
def setSchedule(self, t):
# FIXME: properly store schedule
self.send(self.SET_SCHEDULE, t)
def cancelSchedule(self):
# FIXME: properly store schedule
self.send(self.CANCEL_SCHEDULE)
def returnBase(self):
self.send(self.RETURN_BASE)
@click.group()
def cli():
pass
@cli.command()
@click.argument('mode', required=False,
type=click.Choice(['auto', 'spot', 'edge', 'room']))
def clean(mode):
eufy = EufyIR()
dict(
auto=eufy.cleanAuto,
spot=eufy.cleanSpot,
edge=eufy.cleanEdge,
room=eufy.cleanRoom
).get(mode, eufy.start)()
@cli.command()
@click.argument('mode', required=True,
type=click.Choice(['standard', 'boostiq', 'max']))
def power(mode):
eufy = EufyIR()
{
'standard': eufy.powerStandard,
'boostiq': eufy.powerBoostIQ,
'max': eufy.powerMax
}.get(mode)()
@cli.command()
@click.argument('mode', required=True,
type=click.Choice(['forward', 'backward', 'ccw', 'cw', 'left', 'right']))
def move(mode):
eufy = EufyIR()
dict(
forward=eufy.moveForward,
backward=eufy.moveBackward,
ccw=eufy.moveCCW,
cw=eufy.moveCW,
left=eufy.moveLeft,
right=eufy.moveRight
).get(mode)()
if __name__ == '__main__':
cli()