Merge branch 'cmd' of guru/eupy into master

This commit is contained in:
Maurizio Porrato 2019-02-11 22:20:16 +00:00 committed by Gitea
commit 49dc2c9d41
2 changed files with 132 additions and 50 deletions

169
eufy.py
View File

@ -1,7 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import click from cmd import Cmd
from datetime import datetime from datetime import datetime, time
from inspect import signature, Signature
import os
from time import sleep
def drive_ir(pulses):
# Debugging placeholder
print(','.join([str(x) for x in pulses]))
class EufyRawIR: class EufyRawIR:
@ -24,7 +32,7 @@ class EufyRawIR:
return 3*signal return 3*signal
def sendRaw(self, message): def sendRaw(self, message):
print(self._modulate(message)) drive_ir(self._modulate(message))
class EufyIR(EufyRawIR): class EufyIR(EufyRawIR):
@ -50,7 +58,12 @@ class EufyIR(EufyRawIR):
RETURN_BASE = 0xef RETURN_BASE = 0xef
def __init__(self): def __init__(self):
self._schedule = 0xff self._schedule_file = os.path.expanduser('~/.eufy-schedule')
try:
with open(os.path.expanduser(self._SCHEDULE_FILE), 'r') as f:
self._schedule = int(f.read(5))
except:
self._schedule = 0xff
def _message(self, code): def _message(self, code):
now = datetime.now() now = datetime.now()
@ -111,58 +124,124 @@ class EufyIR(EufyRawIR):
self.send(self.SET_TIME) self.send(self.SET_TIME)
def setSchedule(self, t): def setSchedule(self, t):
# FIXME: properly store schedule self._schedule = t.hour * 4 + t.minute // 15
self.send(self.SET_SCHEDULE, t) with open(self._schedule_file, 'w') as f:
f.write(str(self._schedule))
self.send(self.SET_SCHEDULE)
def cancelSchedule(self): def cancelSchedule(self):
# FIXME: properly store schedule self._schedule = 0xff
try:
os.remove(self._schedule_file)
except:
pass
self.send(self.CANCEL_SCHEDULE) self.send(self.CANCEL_SCHEDULE)
def returnBase(self): def returnBase(self):
self.send(self.RETURN_BASE) self.send(self.RETURN_BASE)
@click.group() def lexer(f):
def cli(): def g(self, args):
pass argv = tuple(x.lower() for x in args.split())
s = signature(f)
maxargs = len(s.parameters) - 1
minargs = len([x for _,x in s.parameters.items() if x.default is Signature.empty]) - 1
if minargs <= len(argv) <= maxargs:
return f(self, *argv)
else:
self.stdout.write("*** Invalid arguments for {}\n".format(f.__name__[3:]))
return g
@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() class Eufy(Cmd):
@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() prompt = 'eufy> '
@click.argument('mode', required=True,
type=click.Choice(['forward', 'backward', 'ccw', 'cw', 'left', 'right'])) def __init__(self, *args, **kwargs):
def move(mode): super(Eufy, self).__init__(*args, **kwargs)
eufy = EufyIR() self.ir = EufyIR()
dict(
forward=eufy.moveForward, @lexer
backward=eufy.moveBackward, def do_clean(self, mode='auto'):
ccw=eufy.moveCCW, {
cw=eufy.moveCW, 'auto': self.ir.cleanAuto,
left=eufy.moveLeft, 'spot': self.ir.cleanSpot,
right=eufy.moveRight 'edge': self.ir.cleanEdge,
).get(mode)() 'room': self.ir.cleanRoom
}.get(mode)()
@lexer
def do_start(self):
self.ir.cleanAuto()
@lexer
def do_stop(self):
self.ir.stop()
@lexer
def do_power(self, mode):
{
'standard': self.ir.powerStandard,
'boostiq': self.ir.powerBoostIQ,
'max': self.ir.powerMax
}.get(mode)()
@lexer
def do_move(self, direction):
{
'forward': self.ir.moveForward,
'backward': self.ir.moveBackward,
'ccw': self.ir.moveCCW,
'cw': self.ir.moveCW,
'left': self.ir.moveCCW,
'right': self.ir.moveCW
}.get(direction)()
@lexer
def do_time(self):
self.ir.setTime()
@lexer
def do_schedule(self, t):
newtime = datetime.now()
newtime.strptime(t, "%H:%M")
self.ir.setSchedule(newtime.time())
@lexer
def do_base(self):
self.ir.returnBase()
@lexer
def do_pause(self, delay=1):
sleep(int(delay))
@lexer
def do_quit(self):
return True
@lexer
def do_EOF(self):
return True
if __name__ == '__main__': if __name__ == '__main__':
cli() from argparse import ArgumentParser, FileType
parser = ArgumentParser(description='Eufy RoboVac 11s CLI tool')
parser.add_argument('-f', '--file', type=FileType('r'), help='read commands from file')
parser.add_argument('command', nargs='*')
args = parser.parse_args()
eufy = Eufy()
if args.file:
eufy.cmdqueue.extend(args.file.readlines())
if args.command:
eufy.cmdqueue.extend(' '.join(args.command).split(','))
if eufy.cmdqueue:
eufy.cmdqueue.append('quit')
eufy.cmdloop()

View File

@ -1,3 +1,6 @@
Eufy RoboVac 11s IR protocol
============================
Preamble: H:3ms L:3ms Preamble: H:3ms L:3ms
Logical 0: H:400µs L:600µs Logical 0: H:400µs L:600µs
Logical 1: H:400µs L:1.6ms Logical 1: H:400µs L:1.6ms
@ -9,11 +12,11 @@ Frame: (Preamble+48bits+Trailer)*3
Format: 68CCHHMMSSKK (Hex, MSB first) Format: 68CCHHMMSSKK (Hex, MSB first)
CC: command CC: command
HH: hour HH: hour
MM: minute MM: minute
SS: schedule (in quarters of an hour from midnight, 0xff=off) SS: schedule (in quarters of an hour from midnight, 0xff=off)
KK: checksum (sum of all bytes in the frame modulo 256) KK: checksum (sum of all bytes in the frame modulo 256)
CC | Command CC | Command
------+--------- ------+---------