Skip to content

Commit

Permalink
Add control for persistent maps, no-go zones and barriers (#438)
Browse files Browse the repository at this point in the history
* Add some map editing commands

Completely untested for now, works only on gen2 and requires a fairly
recent firmware version supporting no-go zones and barriers.

Based on information from marcelrv/XiaomiRobotVacuumProtocol#15

* Add parameters to no-go zone, fix type handling
  • Loading branch information
rytilahti authored Feb 2, 2019
1 parent 9692296 commit bce9a29
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions miio/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,92 @@ def map(self):
# returns ['retry'] without internet
return self.send("get_map_v1")

@command(
click.argument("start", type=bool)
)
def edit_map(self, start):
"""Start map editing?"""
if start:
return self.send("start_edit_map")[0] == "ok"
else:
return self.send("end_edit_map")[0] == "ok"

@command(
click.option("--version", default=1)
)
def fresh_map(self, version):
"""Return fresh map?"""
if version == 1:
return self.send("get_fresh_map")
elif version == 2:
return self.send("get_fresh_map_v2")
else:
raise VacuumException("Unknown map version: %s" % version)

@command(
click.option("--version", default=1)
)
def persist_map(self, version):
"""Return fresh map?"""
if version == 1:
return self.send("get_persist_map")
elif version == 2:
return self.send("get_persist_map_v2")
else:
raise VacuumException("Unknown map version: %s" % version)

@command(
click.argument("x1", type=int),
click.argument("y1", type=int),
click.argument("x2", type=int),
click.argument("y2", type=int),
)
def create_software_barrier(self, x1, y1, x2, y2):
"""Create software barrier (gen2 only?).
NOTE: Multiple nogo zones and barriers could be added by passing
a list of them to save_map.
Requires new fw version.
3.3.9_001633+?
"""
# First parameter indicates the type, 1 = barrier
payload = [1, x1, y1, x2, y2]
return self.send("save_map", payload)[0] == "ok"

@command(
click.argument("x1", type=int),
click.argument("y1", type=int),
click.argument("x2", type=int),
click.argument("y2", type=int),
click.argument("x3", type=int),
click.argument("y3", type=int),
click.argument("x4", type=int),
click.argument("y4", type=int),
)
def create_nogo_zone(self, x1, y1, x2, y2, x3, y3, x4, y4):
"""Create a rectangular no-go zone (gen2 only?).
NOTE: Multiple nogo zones and barriers could be added by passing
a list of them to save_map.
Requires new fw version.
3.3.9_001633+?
"""
# First parameter indicates the type, 0 = zone
payload = [0, x1, y1, x2, y2, x3, y3, x4, y4]
return self.send("save_map", payload)[0] == "ok"

@command(
click.argument("enable", type=bool)
)
def enable_lab_mode(self, enable):
"""Enable persistent maps and software barriers.
This is required to use create_nogo_zone and create_software_barrier
commands."""
return self.send("set_lab_status", int(enable))['ok']

@command()
def clean_history(self) -> CleaningSummary:
"""Return generic cleaning history."""
Expand Down

6 comments on commit bce9a29

@cbruegg
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rytilahti, sorry to bother you, but I couldn't find any sample on how to use these functions. I assume I have to call them in some sequence to add a nogo zone? Also, is it possible to delete nogo zones somehow? Thanks!

@rytilahti
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @cbruegg, unfortunately I don't really know how they do work, expect that the "lab mode" has to turned on in order to execute the other commands, so I cannot help you there.

Maybe switching it on and off will reset the existing zones? I commited it in in hopes of someone coming around and helping to make sense to these and document them alongside :-)

@cbruegg
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick response! I'll have to tinker with them then. If I find out anything, I'll submit a PR with docs.

@rytilahti
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks!

@cbruegg
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, so I don't know enough about this @command framework to make a PR, but here's what I've discovered:

Whenever I issue a command to the save_map API, the list of barriers and nogo-zones I pass in override the previously saved zones. That is, if I pass in an empty list, it clears all nogo-zones and barriers. This has the effect that create_software_barrier and create_nogo_zone clear everything else.

I'd suggest adding a method that just takes a list of lists and calls the save_map API. To give you an idea how the API works, here's a snippet that works for me:

# Coordinates for barriers are specified as: leftX, leftY, rightX, rightY
# 1 denotes the type barrier
barriers = [
    [1, 28300, 22000, 28400, 20000], [1, 28000, 23000, 29000, 23000]
]
# Coordinates for nogo-zones are specified as: topLeftX, topLeftY, topRightX, topRightY, bottomRightX, bottomRightY, bottomLeftX, bottomLeftY
# 0 denotes the type nogo-zone
nogos = [
    [0, 31300, 27200, 32000, 27200, 32000, 26600, 31300, 26600],
    [0, 25000, 31500, 27100, 31500, 27100, 30900, 25000, 30900]
]

vac = Vacuum(ip, token)
vac.send("save_map", barriers + nogos)

There seems to be no requirement to call start_edit_map and end_edit_map. I've tinkered with these methods, but I couldn't figure out what they are useful for. I also had no success with getting the currently stored zones and barriers.

@rytilahti
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update! So the command decorator is in principle quite simple to use, it just wraps regular functions (and makes them accessible for the console tool, eacho @command wrapped function is available when calling miiocli vacuum, for Vacuum class' commands that is).

But anyway, sounds like the interface needs a bit adapting, could you please copy create an issue with your comment so it doesn't get forgotten.

Please sign in to comment.