Merge branch 'cmd' of guru/eupy into master
This commit is contained in:
commit
49dc2c9d41
169
eufy.py
169
eufy.py
|
@ -1,7 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import click
|
||||
from datetime import datetime
|
||||
from cmd import Cmd
|
||||
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:
|
||||
|
@ -24,7 +32,7 @@ class EufyRawIR:
|
|||
return 3*signal
|
||||
|
||||
def sendRaw(self, message):
|
||||
print(self._modulate(message))
|
||||
drive_ir(self._modulate(message))
|
||||
|
||||
|
||||
class EufyIR(EufyRawIR):
|
||||
|
@ -50,7 +58,12 @@ class EufyIR(EufyRawIR):
|
|||
RETURN_BASE = 0xef
|
||||
|
||||
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):
|
||||
now = datetime.now()
|
||||
|
@ -111,58 +124,124 @@ class EufyIR(EufyRawIR):
|
|||
self.send(self.SET_TIME)
|
||||
|
||||
def setSchedule(self, t):
|
||||
# FIXME: properly store schedule
|
||||
self.send(self.SET_SCHEDULE, t)
|
||||
self._schedule = t.hour * 4 + t.minute // 15
|
||||
with open(self._schedule_file, 'w') as f:
|
||||
f.write(str(self._schedule))
|
||||
self.send(self.SET_SCHEDULE)
|
||||
|
||||
def cancelSchedule(self):
|
||||
# FIXME: properly store schedule
|
||||
self._schedule = 0xff
|
||||
try:
|
||||
os.remove(self._schedule_file)
|
||||
except:
|
||||
pass
|
||||
self.send(self.CANCEL_SCHEDULE)
|
||||
|
||||
def returnBase(self):
|
||||
self.send(self.RETURN_BASE)
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
def lexer(f):
|
||||
def g(self, args):
|
||||
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()
|
||||
@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)()
|
||||
class Eufy(Cmd):
|
||||
|
||||
@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)()
|
||||
prompt = 'eufy> '
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Eufy, self).__init__(*args, **kwargs)
|
||||
self.ir = EufyIR()
|
||||
|
||||
@lexer
|
||||
def do_clean(self, mode='auto'):
|
||||
{
|
||||
'auto': self.ir.cleanAuto,
|
||||
'spot': self.ir.cleanSpot,
|
||||
'edge': self.ir.cleanEdge,
|
||||
'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__':
|
||||
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()
|
||||
|
|
13
protocol.txt
13
protocol.txt
|
@ -1,3 +1,6 @@
|
|||
Eufy RoboVac 11s IR protocol
|
||||
============================
|
||||
|
||||
Preamble: H:3ms L:3ms
|
||||
Logical 0: H:400µs L:600µs
|
||||
Logical 1: H:400µs L:1.6ms
|
||||
|
@ -9,11 +12,11 @@ Frame: (Preamble+48bits+Trailer)*3
|
|||
|
||||
Format: 68CCHHMMSSKK (Hex, MSB first)
|
||||
|
||||
CC: command
|
||||
HH: hour
|
||||
MM: minute
|
||||
SS: schedule (in quarters of an hour from midnight, 0xff=off)
|
||||
KK: checksum (sum of all bytes in the frame modulo 256)
|
||||
CC: command
|
||||
HH: hour
|
||||
MM: minute
|
||||
SS: schedule (in quarters of an hour from midnight, 0xff=off)
|
||||
KK: checksum (sum of all bytes in the frame modulo 256)
|
||||
|
||||
CC | Command
|
||||
------+---------
|
||||
|
|
Loading…
Reference in New Issue