Skip to content
This repository has been archived by the owner on Apr 5, 2021. It is now read-only.

Add D900 (IOTMQ) and Ozmo930 Support #63

Open
wants to merge 62 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
58ace66
Update .gitignore
bmartin5692 Jan 11, 2019
e408157
Basic support for D90X
bmartin5692 Jan 12, 2019
0da4532
Add spotarea
bmartin5692 Jan 13, 2019
c1f6814
Add clean_status for IOT
bmartin5692 Jan 13, 2019
ef0bd2e
Add backward action
bmartin5692 Jan 14, 2019
d317e4b
Fix tests & Clean
bmartin5692 Jan 14, 2019
304c576
Add spotarea command tests
bmartin5692 Jan 14, 2019
62185d1
Add spotclean to cli
bmartin5692 Jan 14, 2019
484502b
Cleanup init code
bmartin5692 Jan 14, 2019
205214c
Update .gitignore
bmartin5692 Jan 14, 2019
d070ab7
Delete launch.json
bmartin5692 Jan 14, 2019
ef467ac
Remove egg & update gitignore for vscode files
bmartin5692 Jan 14, 2019
4ca9750
Fix xmpp
bmartin5692 Jan 15, 2019
335ac6a
Handle already charging
bmartin5692 Jan 15, 2019
3c01cd2
Set timeout for IOT api calls
bmartin5692 Jan 15, 2019
ca7d37c
WIP: Initial MQTT work
bmartin5692 Jan 16, 2019
c72be55
MQTT Plumbing
bmartin5692 Jan 17, 2019
e2b0ea7
Add test MQTTPing
bmartin5692 Jan 17, 2019
c0eda3a
Fix clean from CLI
bmartin5692 Jan 17, 2019
6ff7173
Update __init__.py
bmartin5692 Jan 18, 2019
5a590d6
Merge pull request #1 from bmartin5692/MQTT
bmartin5692 Jan 18, 2019
605b1b5
Add to protocol.md
bmartin5692 Jan 18, 2019
10993e6
Fix tables in protocol
bmartin5692 Jan 18, 2019
f3bcfb4
Add more iot tests
bmartin5692 Jan 20, 2019
1812a21
Add mqtt tests
bmartin5692 Jan 21, 2019
2cfa5ec
Add ignore ssl to requests
bmartin5692 Jan 31, 2019
57979c3
Make ssl verification optional
bmartin5692 Feb 3, 2019
5d6df85
Fix custom commands
bmartin5692 Feb 8, 2019
b0bf467
stop printing positiion updates
bmartin5692 Feb 8, 2019
94a7844
Fix spot area clean
bmartin5692 Feb 16, 2019
409b04b
add tests
bmartin5692 Feb 16, 2019
9919baa
more tests
bmartin5692 Feb 16, 2019
597129d
Combine MQTT and IOT into IOTMQ
bmartin5692 Feb 18, 2019
ab30371
Update protocol.md
bmartin5692 Feb 19, 2019
6b23d85
Add SpotArea details
bmartin5692 Feb 19, 2019
f1257b8
format updates
bmartin5692 Feb 19, 2019
6c31e07
add comments and cleanup
bmartin5692 Feb 19, 2019
7fac965
Update setup.py
ecpunk Feb 19, 2019
bfca7e9
Merge pull request #2 from ecpunk/patch-1
bmartin5692 Feb 19, 2019
b7341b0
Update SpotArea
bmartin5692 Feb 20, 2019
0fa5fe4
Update protocol.md
bmartin5692 Feb 21, 2019
c37ebdd
Ozmo930 working
bmartin5692 Feb 25, 2019
f0aef13
Merge pull request #3 from bmartin5692/Ozmo930
bmartin5692 Feb 28, 2019
3dbaccd
Update test_ecovacs_xmpp.py
bmartin5692 Feb 28, 2019
91e3f3d
fix failing tests?
bmartin5692 Feb 28, 2019
e4848cb
Fix failing test
bmartin5692 Feb 28, 2019
3942e22
Add D600 to iotmqdevices
bmartin5692 Mar 14, 2019
70ecd77
Removing test made on assumptions
bmartin5692 Mar 14, 2019
79f9d67
Update protocol.md with mopping setting
bmartin5692 Mar 19, 2019
6da9a96
Update README API example
bmartin5692 Mar 19, 2019
5fda81e
Add verify_ssl to cli login
bmartin5692 Mar 19, 2019
3e1efae
Merge pull request #4 from bmartin5692/D901
bmartin5692 Mar 19, 2019
4b47e8e
Merge branch 'master' into pr/6
bmartin5692 Jun 14, 2019
813c620
Merge pull request #7 from bmartin5692/pr/6
bmartin5692 Jun 14, 2019
dd783b6
Merge pull request #8 from bmartin5692/master
bmartin5692 Jun 14, 2019
3c9c834
change str_to_bool func
bmartin5692 Jun 14, 2019
054f401
Merge branch 'master' into bumper-certs
bmartin5692 Jun 14, 2019
a14a294
Merge pull request #9 from bmartin5692/bumper-certs
bmartin5692 Jun 14, 2019
9d9ebf3
Fix de and setIOT
bmartin5692 Jun 15, 2019
cd0d360
Merge pull request #11 from bmartin5692/fix-de
bmartin5692 Jun 15, 2019
25f806d
fix lookup tests
bmartin5692 Jun 15, 2019
c8d9473
Merge pull request #13 from bmartin5692/fix-de
bmartin5692 Jun 15, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ dist
# Nosetests files
cover/
.coverage

# Ignore Vscode files
.vscode/

# Ignore sucks.egg-info
sucks.egg-info/
.noseids
nosetests.xml
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ shaping the API.

A simple usage might go something like this:

```
import sucks
```python
from sucks import *

config = ...

Expand Down
293 changes: 245 additions & 48 deletions protocol.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
'requests>=2.18',
'pycryptodome>=3.4',
'pycountry-convert>=0.5',
'paho-mqtt>=1.4',
'stringcase>=1.2'
],

Expand Down
584 changes: 534 additions & 50 deletions sucks/__init__.py

Large diffs are not rendered by default.

20 changes: 16 additions & 4 deletions sucks/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,16 @@ def cli(debug):
@click.option('--country-code', prompt='your two-letter country code', default=lambda: current_country())
@click.option('--continent-code', prompt='your two-letter continent code',
default=lambda: continent_for_country(click.get_current_context().params['country_code']))
def login(email, password, country_code, continent_code):
@click.option('--verify-ssl', prompt='Verify SSL for API requests', default=True)
def login(email, password, country_code, continent_code, verify_ssl):
if config_file_exists() and not click.confirm('overwrite existing config?'):
click.echo("Skipping login.")
exit(0)
config = OrderedDict()
password_hash = EcoVacsAPI.md5(password)
device_id = EcoVacsAPI.md5(str(time.time()))
try:
EcoVacsAPI(device_id, email, password_hash, country_code, continent_code)
EcoVacsAPI(device_id, email, password_hash, country_code, continent_code, verify_ssl)
except ValueError as e:
click.echo(e.args[0])
exit(1)
Expand All @@ -158,6 +159,7 @@ def login(email, password, country_code, continent_code):
config['device_id'] = device_id
config['country'] = country_code.lower()
config['continent'] = continent_code.lower()
config['verify_ssl'] = verify_ssl
write_config(config)
click.echo("Config saved.")
exit(0)
Expand All @@ -179,6 +181,16 @@ def edge(frequency, minutes):
return CliAction(Edge(), wait=TimeWait(minutes * 60))


@cli.command(help='cleans provided area(s), ex: "0,1"',context_settings={"ignore_unknown_options": True}) #ignore_unknown for map coordinates with negatives
@click.option("--map-position","-p", is_flag=True, help='clean provided map position instead of area, ex: "-602,1812,800,723"')
@click.argument('area', type=click.STRING, required=True)
def area(area, map_position):
if map_position:
return CliAction(SpotArea('start', map_position=area), wait=StatusWait('charge_status', 'returning'))
else:
return CliAction(SpotArea('start', area=area), wait=StatusWait('charge_status', 'returning'))


@cli.command(help='returns to charger')
def charge():
return charge_action()
Expand Down Expand Up @@ -209,9 +221,9 @@ def run(actions, debug):
if actions:
config = read_config()
api = EcoVacsAPI(config['device_id'], config['email'], config['password_hash'],
config['country'], config['continent'])
config['country'], config['continent'], verify_ssl=config['verify_ssl'])
vacuum = api.devices()[0]
vacbot = VacBot(api.uid, api.REALM, api.resource, api.user_access_token, vacuum, config['continent'])
vacbot = VacBot(api.uid, api.REALM, api.resource, api.user_access_token, vacuum, config['continent'], verify_ssl=config['verify_ssl'])
vacbot.connect_and_wait_until_ready()

for action in actions:
Expand Down
76 changes: 70 additions & 6 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ def test_custom_command_inner_tag():
b'<ctl td="CustomCommand"><customtag customvar="customvalue" /></ctl>')


def test_custom_command_multiple_inner_tag():
# Ensure a custom-built command with multiple inner tags generates the expected XML payload
c = VacBotCommand('CustomCommand', {"customtag":[{"customvar":"customvalue1"},{"customvar":"customvalue2"}]})
logging.info(ElementTree.tostring(c.to_xml()))
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="CustomCommand"><customtag customvar="customvalue1" /><customtag customvar="customvalue2" /></ctl>')

def test_custom_command_args_multiple_inner_tag():
# Ensure a custom-built command with args and multiple inner tags generates the expected XML payload
c = VacBotCommand('CustomCommand', {"arg1":"value1","customtag":[{"customvar":"customvalue1"},{"customvar":"customvalue2"}]})
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl arg1="value1" td="CustomCommand"><customtag customvar="customvalue1" /><customtag customvar="customvalue2" /></ctl>')


def test_custom_command_noargs():
# Ensure a custom-built command with no args generates XML without an args element
c = VacBotCommand('CustomCommand')
Expand All @@ -29,22 +43,63 @@ def test_custom_command_noargs():
def test_clean_command():
c = Clean()
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean speed="standard" type="auto" /></ctl>') # protocol has attribs in other order
b'<ctl td="Clean"><clean act="s" speed="standard" type="auto" /></ctl>') # protocol has attribs in other order

c = Clean('edge', 'high')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean speed="strong" type="border" /></ctl>') # protocol has attribs in other order
b'<ctl td="Clean"><clean act="s" speed="strong" type="border" /></ctl>') # protocol has attribs in other order

c = Clean(iotmq=True)
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" speed="standard" type="auto" /></ctl>') # test for iot act is added


def test_spotarea_command():
assert_raises(ValueError, SpotArea, 'start') #Value error if SpotArea doesn't include a mid or p

c = SpotArea('start', '0')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" mid="0" speed="standard" type="SpotArea" /></ctl>') #Test namedarea clean

c = SpotArea('start', area='0')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" mid="0" speed="standard" type="SpotArea" /></ctl>') #Test namedarea keyword clean

c = SpotArea('start', '', '-602,1812,800,723')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" deep="1" p="-602,1812,800,723" speed="standard" type="SpotArea" /></ctl>') #Test customarea clean

c = SpotArea('start', '', '-602,1812,800,723', '2')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" deep="2" p="-602,1812,800,723" speed="standard" type="SpotArea" /></ctl>') #Test customarea clean with deep 2

c = SpotArea('start', '', map_position='-602,1812,800,723')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" deep="1" p="-602,1812,800,723" speed="standard" type="SpotArea" /></ctl>') #Test customarea keyword clean with deep default

c = SpotArea('start', map_position='-602,1812,800,723', cleanings='2')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" deep="2" p="-602,1812,800,723" speed="standard" type="SpotArea" /></ctl>') #Test customarea keyword and cleanings keyword clean with deep default

c = SpotArea('start', area='0', map_position='-602,1812,800,723', cleanings='2')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" mid="0" speed="standard" type="SpotArea" /></ctl>') #Test all keywords specified, should default to only mid

c = SpotArea('start', '0', '-602,1812,800,723','2')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean act="s" mid="0" speed="standard" type="SpotArea" /></ctl>') #Test all keywords specified, should default to only mid


def test_edge_command():
c = Edge()
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean speed="strong" type="border" /></ctl>') # protocol has attribs in other order
b'<ctl td="Clean"><clean act="s" speed="strong" type="border" /></ctl>') # protocol has attribs in other order


def test_spot_command():
c = Spot()
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean speed="strong" type="spot" /></ctl>') # protocol has attribs in other order
b'<ctl td="Clean"><clean act="s" speed="strong" type="spot" /></ctl>') # protocol has attribs in other order


def test_charge_command():
Expand All @@ -56,7 +111,7 @@ def test_charge_command():
def test_stop_command():
c = Stop()
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Clean"><clean speed="standard" type="stop" /></ctl>')
b'<ctl td="Clean"><clean act="s" speed="standard" type="stop" /></ctl>')


def test_play_sound_command():
Expand Down Expand Up @@ -89,7 +144,6 @@ def test_get_battery_state_command():
b'<ctl td="GetBatteryInfo" />')



def test_move_command():
c = Move(action='left')
assert_equals(ElementTree.tostring(c.to_xml()),
Expand All @@ -103,6 +157,9 @@ def test_move_command():
c = Move(action='forward')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Move"><move action="forward" /></ctl>')
c = Move(action='backward')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Move"><move action="backward" /></ctl>')
c = Move(action='stop')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="Move"><move action="stop" /></ctl>')
Expand All @@ -112,9 +169,16 @@ def test_get_lifepsan_command():
c = GetLifeSpan('main_brush')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="GetLifeSpan" type="Brush" />')

c = GetLifeSpan('side_brush')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="GetLifeSpan" type="SideBrush" />')

c = GetLifeSpan('filter')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="GetLifeSpan" type="DustCaseHeap" />')

def test_set_time_command():
c = SetTime('1234', 'GMT-5')
assert_equals(ElementTree.tostring(c.to_xml()),
b'<ctl td="SetTime"><time t="1234" tz="GMT-5" /></ctl>')
Loading