Skip to content

Commit

Permalink
Vacuum: Implement TUI for the manual mode
Browse files Browse the repository at this point in the history
  • Loading branch information
rnovatorov committed Oct 28, 2020
1 parent 6c7de4b commit a5f4851
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
1 change: 1 addition & 0 deletions miio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from miio.pwzn_relay import PwznRelay
from miio.toiletlid import Toiletlid
from miio.vacuum import Vacuum, VacuumException
from miio.vacuum_tui import VacuumTUI
from miio.vacuumcontainers import (
CleaningDetails,
CleaningSummary,
Expand Down
7 changes: 7 additions & 0 deletions miio/vacuum_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ def manual(vac: miio.Vacuum):
# if not vac.manual_mode and command :


@manual.command()
@pass_dev
def tui(vac: miio.Vacuum):
"""TUI for the manual mode."""
miio.VacuumTUI(vac).run()


@manual.command()
@pass_dev
def start(vac: miio.Vacuum): # noqa: F811 # redef of start
Expand Down
94 changes: 94 additions & 0 deletions miio/vacuum_tui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
try:
import curses
except ImportError: # curses unavailable
pass

import enum
from typing import Tuple

from .vacuum import Vacuum


class Control(enum.Enum):

Quit = "q"
Forward = "l"
ForwardFast = "L"
Backward = "k"
BackwardFast = "K"
Left = "j"
LeftFast = "J"
Right = ";"
RightFast = ":"


class VacuumTUI:
def __init__(self, vac: Vacuum):
self.vac = vac
self.rot = 0
self.rot_delta = 30
self.rot_min = Vacuum.MANUAL_ROTATION_MIN
self.rot_max = Vacuum.MANUAL_ROTATION_MAX
self.vel = 0.0
self.vel_delta = 0.1
self.vel_min = Vacuum.MANUAL_VELOCITY_MIN
self.vel_max = Vacuum.MANUAL_VELOCITY_MAX
self.dur = 10 * 1000

def run(self) -> None:
self.vac.manual_start()
try:
curses.wrapper(self.main_loop)
finally:
self.vac.manual_stop()

def main_loop(self, screen) -> None:
done = False
while not done:
key = screen.getkey()
text, done = self.handle_key(key)
screen.clear()
screen.addstr(text)
screen.refresh()

def handle_key(self, key: str) -> Tuple[str, bool]:
try:
ctl = Control(key)
except ValueError as e:
return "ignoring %s: %s\n" % (key, e), False

done = self.dispatch_control(ctl)
return self.info(), done

def dispatch_control(self, ctl: Control) -> bool:
if ctl == Control.Quit:
return True

if ctl == Control.Forward:
self.vel = min(self.vel + self.vel_delta, self.vel_max)
elif ctl == Control.ForwardFast:
self.vel = 0 if self.vel < 0 else self.vel_max

elif ctl == Control.Backward:
self.vel = max(self.vel - self.vel_delta, self.vel_min)
elif ctl == Control.BackwardFast:
self.vel = 0 if self.vel > 0 else self.vel_min

elif ctl == Control.Left:
self.rot = min(self.rot + self.rot_delta, self.rot_max)
elif ctl == Control.LeftFast:
self.rot = 0 if self.rot < 0 else self.rot_max

elif ctl == Control.Right:
self.rot = max(self.rot - self.rot_delta, self.rot_min)
elif ctl == Control.RightFast:
self.rot = 0 if self.rot > 0 else self.rot_min

else:
raise RuntimeError("unreachable")

self.vac.manual_control(rotation=self.rot, velocity=self.vel, duration=self.dur)
return False

def info(self) -> str:
return f"rotation={self.rot}\nvelocity={self.vel}\n"

0 comments on commit a5f4851

Please sign in to comment.