Basic support for D90X
Add EcoVacsIOT in VacBot
This commit is contained in:
Vendored
+75
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python: Current File (Integrated Terminal)",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: Run Sucks CLI (Integrated Terminal)",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/sucks/cli.py",
|
||||||
|
"args" : [
|
||||||
|
"--debug",
|
||||||
|
"charge"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: Attach",
|
||||||
|
"type": "python",
|
||||||
|
"request": "attach",
|
||||||
|
"port": 5678,
|
||||||
|
"host": "localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: Module",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "enter-your-module-name-here",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: Django",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/manage.py",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"args": [
|
||||||
|
"runserver",
|
||||||
|
"--noreload",
|
||||||
|
"--nothreading"
|
||||||
|
],
|
||||||
|
"django": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: Flask",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "flask",
|
||||||
|
"env": {
|
||||||
|
"FLASK_APP": "app.py"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"--no-debugger",
|
||||||
|
"--no-reload"
|
||||||
|
],
|
||||||
|
"jinja": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Python: Current File (External Terminal)",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "externalTerminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
Metadata-Version: 2.1
|
||||||
|
Name: sucks
|
||||||
|
Version: 0.9.3
|
||||||
|
Summary: a library for controlling certain robot vacuums
|
||||||
|
Home-page: https://github.com/wpietri/sucks
|
||||||
|
Author: William Pietri
|
||||||
|
Author-email: sucks-users@googlegroups.com
|
||||||
|
License: GPL-3.0
|
||||||
|
Description: UNKNOWN
|
||||||
|
Keywords: home automation vacuum robot
|
||||||
|
Platform: UNKNOWN
|
||||||
|
Classifier: Development Status :: 4 - Beta
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: Topic :: Software Development :: Libraries
|
||||||
|
Classifier: Topic :: Home Automation
|
||||||
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
||||||
|
Classifier: Programming Language :: Python :: 3.5
|
||||||
|
Provides-Extra: dev
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
README.md
|
||||||
|
setup.py
|
||||||
|
sucks/__init__.py
|
||||||
|
sucks/cli.py
|
||||||
|
sucks.egg-info/PKG-INFO
|
||||||
|
sucks.egg-info/SOURCES.txt
|
||||||
|
sucks.egg-info/dependency_links.txt
|
||||||
|
sucks.egg-info/entry_points.txt
|
||||||
|
sucks.egg-info/requires.txt
|
||||||
|
sucks.egg-info/top_level.txt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[console_scripts]
|
||||||
|
sucks = sucks.cli:cli
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
sleekxmpp>=1.3
|
||||||
|
click>=6
|
||||||
|
requests>=2.18
|
||||||
|
pycryptodome>=3.4
|
||||||
|
pycountry-convert>=0.5
|
||||||
|
stringcase>=1.2
|
||||||
|
|
||||||
|
[dev]
|
||||||
|
nose
|
||||||
|
requests-mock>=1.3
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
sucks
|
||||||
+198
-7
@@ -99,6 +99,13 @@ class EcoVacsAPI:
|
|||||||
PUBLIC_KEY = 'MIIB/TCCAWYCCQDJ7TMYJFzqYDANBgkqhkiG9w0BAQUFADBCMQswCQYDVQQGEwJjbjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMCAXDTE3MDUwOTA1MTkxMFoYDzIxMTcwNDE1MDUxOTEwWjBCMQswCQYDVQQGEwJjbjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb8V0OYUGP3Fs63E1gJzJh+7iqeymjFUKJUqSD60nhWReZ+Fg3tZvKKqgNcgl7EGXp1yNifJKUNC/SedFG1IJRh5hBeDMGq0m0RQYDpf9l0umqYURpJ5fmfvH/gjfHe3Eg/NTLm7QEa0a0Il2t3Cyu5jcR4zyK6QEPn1hdIGXB5QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBANhIMT0+IyJa9SU8AEyaWZZmT2KEYrjakuadOvlkn3vFdhpvNpnnXiL+cyWy2oU1Q9MAdCTiOPfXmAQt8zIvP2JC8j6yRTcxJCvBwORDyv/uBtXFxBPEC6MDfzU2gKAaHeeJUWrzRv34qFSaYkYta8canK+PSInylQTjJK9VqmjQ'
|
PUBLIC_KEY = 'MIIB/TCCAWYCCQDJ7TMYJFzqYDANBgkqhkiG9w0BAQUFADBCMQswCQYDVQQGEwJjbjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMCAXDTE3MDUwOTA1MTkxMFoYDzIxMTcwNDE1MDUxOTEwWjBCMQswCQYDVQQGEwJjbjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb8V0OYUGP3Fs63E1gJzJh+7iqeymjFUKJUqSD60nhWReZ+Fg3tZvKKqgNcgl7EGXp1yNifJKUNC/SedFG1IJRh5hBeDMGq0m0RQYDpf9l0umqYURpJ5fmfvH/gjfHe3Eg/NTLm7QEa0a0Il2t3Cyu5jcR4zyK6QEPn1hdIGXB5QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBANhIMT0+IyJa9SU8AEyaWZZmT2KEYrjakuadOvlkn3vFdhpvNpnnXiL+cyWy2oU1Q9MAdCTiOPfXmAQt8zIvP2JC8j6yRTcxJCvBwORDyv/uBtXFxBPEC6MDfzU2gKAaHeeJUWrzRv34qFSaYkYta8canK+PSInylQTjJK9VqmjQ'
|
||||||
MAIN_URL_FORMAT = 'https://eco-{country}-api.ecovacs.com/v1/private/{country}/{lang}/{deviceId}/{appCode}/{appVersion}/{channel}/{deviceType}'
|
MAIN_URL_FORMAT = 'https://eco-{country}-api.ecovacs.com/v1/private/{country}/{lang}/{deviceId}/{appCode}/{appVersion}/{channel}/{deviceType}'
|
||||||
USER_URL_FORMAT = 'https://users-{continent}.ecouser.net:8000/user.do'
|
USER_URL_FORMAT = 'https://users-{continent}.ecouser.net:8000/user.do'
|
||||||
|
PORTAL_URL_FORMAT = 'https://portal-{continent}.ecouser.net/api'
|
||||||
|
|
||||||
|
USERSAPI = 'users/user.do'
|
||||||
|
IOTDEVMANAGERAPI = 'iot/devmanager.do' # IOT Device Manager - This provides control of "IOT" products via API, no longer XMPP
|
||||||
|
PRODUCTAPI = 'pim/product' # Leaving this open, the only endpoint known currently is "Product IOT Map" - pim/product/getProductIotMap - This provides a list of "IOT" products, which is assumed should use IOT API instead of XMPP
|
||||||
|
|
||||||
|
|
||||||
REALM = 'ecouser.net'
|
REALM = 'ecouser.net'
|
||||||
|
|
||||||
def __init__(self, device_id, account_id, password_hash, country, continent):
|
def __init__(self, device_id, account_id, password_hash, country, continent):
|
||||||
@@ -176,8 +183,43 @@ class EcoVacsAPI:
|
|||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params))
|
"failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params))
|
||||||
|
|
||||||
|
def __call_portal_api(self, api, function, args):
|
||||||
|
_LOGGER.debug("calling portal api {} function {} with {}".format(api, function, args))
|
||||||
|
if api == self.USERSAPI:
|
||||||
|
params = {'todo': function}
|
||||||
|
params.update(args)
|
||||||
|
else:
|
||||||
|
params = {}
|
||||||
|
params.update(args)
|
||||||
|
|
||||||
|
url = (EcoVacsAPI.PORTAL_URL_FORMAT + "/" + api).format(continent=self.continent, **self.meta)
|
||||||
|
response = requests.post(url, json=params)
|
||||||
|
json = response.json()
|
||||||
|
_LOGGER.debug("got {}".format(json))
|
||||||
|
if api == self.USERSAPI:
|
||||||
|
if json['result'] == 'ok':
|
||||||
|
return json
|
||||||
|
|
||||||
|
if api == self.IOTDEVMANAGERAPI:
|
||||||
|
if json['ret'] == 'ok':
|
||||||
|
return json
|
||||||
|
elif json['ret'] == 'fail' and json['debug'] == 'wait for response timed out': #Maybe handle timeout for IOT better in the future
|
||||||
|
_LOGGER.error("call to {} failed with {}".format(function, json))
|
||||||
|
return {}
|
||||||
|
#raise RuntimeError(
|
||||||
|
# "failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params))
|
||||||
|
|
||||||
|
if api.startswith(self.PRODUCTAPI):
|
||||||
|
if json['code'] == 0:
|
||||||
|
return json
|
||||||
|
|
||||||
|
else:
|
||||||
|
_LOGGER.error("call to {} failed with {}".format(function, json))
|
||||||
|
raise RuntimeError(
|
||||||
|
"failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params))
|
||||||
|
|
||||||
def __call_login_by_it_token(self):
|
def __call_login_by_it_token(self):
|
||||||
return self.__call_user_api('loginByItToken',
|
return self.__call_portal_api(self.USERSAPI,'loginByItToken',
|
||||||
{'country': self.meta['country'].upper(),
|
{'country': self.meta['country'].upper(),
|
||||||
'resource': self.resource,
|
'resource': self.resource,
|
||||||
'realm': EcoVacsAPI.REALM,
|
'realm': EcoVacsAPI.REALM,
|
||||||
@@ -186,7 +228,7 @@ class EcoVacsAPI:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def devices(self):
|
def devices(self):
|
||||||
devices = self.__call_user_api('GetDeviceList', {
|
devices = self.__call_portal_api(self.USERSAPI,'GetDeviceList', {
|
||||||
'userid': self.uid,
|
'userid': self.uid,
|
||||||
'auth': {
|
'auth': {
|
||||||
'with': 'users',
|
'with': 'users',
|
||||||
@@ -196,8 +238,29 @@ class EcoVacsAPI:
|
|||||||
'resource': self.resource
|
'resource': self.resource
|
||||||
}
|
}
|
||||||
})['devices']
|
})['devices']
|
||||||
|
|
||||||
|
iotProducts = self.__call_portal_api(self.PRODUCTAPI + '/getProductIotMap','', {
|
||||||
|
'channel': '',
|
||||||
|
'auth': {
|
||||||
|
'with': 'users',
|
||||||
|
'userid': self.uid,
|
||||||
|
'realm': EcoVacsAPI.REALM,
|
||||||
|
'token': self.user_access_token,
|
||||||
|
'resource': self.resource
|
||||||
|
}
|
||||||
|
})['data']
|
||||||
|
|
||||||
|
for device in devices: #Check if the device is part of iotProducts and add an iot flag.
|
||||||
|
for iotProducts in iotProducts:
|
||||||
|
if device['class'] == iotProducts['classid']:
|
||||||
|
device['iot'] = True
|
||||||
|
else:
|
||||||
|
device['iot'] = False
|
||||||
|
|
||||||
|
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def md5(text):
|
def md5(text):
|
||||||
return hashlib.md5(bytes(str(text), 'utf8')).hexdigest()
|
return hashlib.md5(bytes(str(text), 'utf8')).hexdigest()
|
||||||
@@ -270,12 +333,21 @@ class VacBot():
|
|||||||
self.lifespanEvents = EventEmitter()
|
self.lifespanEvents = EventEmitter()
|
||||||
self.errorEvents = EventEmitter()
|
self.errorEvents = EventEmitter()
|
||||||
|
|
||||||
|
if vacuum['iot']:
|
||||||
|
self.iot = EcoVacsIOT(user, domain, resource, secret, continent, vacuum)
|
||||||
|
self.iot.subscribe_to_ctls(self._handle_ctl)
|
||||||
|
else:
|
||||||
self.xmpp = EcoVacsXMPP(user, domain, resource, secret, continent, server_address)
|
self.xmpp = EcoVacsXMPP(user, domain, resource, secret, continent, server_address)
|
||||||
self.xmpp.subscribe_to_ctls(self._handle_ctl)
|
self.xmpp.subscribe_to_ctls(self._handle_ctl)
|
||||||
|
|
||||||
def connect_and_wait_until_ready(self):
|
|
||||||
self.xmpp.connect_and_wait_until_ready()
|
|
||||||
|
|
||||||
|
def connect_and_wait_until_ready(self):
|
||||||
|
if self.vacuum['iot']:
|
||||||
|
self.iot.connect_and_wait_until_ready()
|
||||||
|
self.iot.schedule('Ping', 30, lambda: self.send_ping(), repeat=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.xmpp.connect_and_wait_until_ready()
|
||||||
self.xmpp.schedule('Ping', 30, lambda: self.send_ping(), repeat=True)
|
self.xmpp.schedule('Ping', 30, lambda: self.send_ping(), repeat=True)
|
||||||
|
|
||||||
if self._monitor:
|
if self._monitor:
|
||||||
@@ -300,9 +372,12 @@ class VacBot():
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
_LOGGER.warning("Unknown component type: '" + type + "'")
|
_LOGGER.warning("Unknown component type: '" + type + "'")
|
||||||
|
|
||||||
|
if 'val' in event:
|
||||||
lifespan = int(event['val']) / 100
|
lifespan = int(event['val']) / 100
|
||||||
|
else:
|
||||||
|
lifespan = int(event['left'].replace("_","-")) / 60 #This works for a D901, I also ran into a negative number and had to replace _ with -
|
||||||
self.components[type] = lifespan
|
self.components[type] = lifespan
|
||||||
|
#lifespan = str(int(int(event['left']) / 60) + " (" + int(int(event['left'])/int(event['total'])*100) + "%)") #This works for a D901
|
||||||
lifespan_event = {'type': type, 'lifespan': lifespan}
|
lifespan_event = {'type': type, 'lifespan': lifespan}
|
||||||
self.lifespanEvents.notify(lifespan_event)
|
self.lifespanEvents.notify(lifespan_event)
|
||||||
_LOGGER.debug("*** life_span " + type + " = " + str(lifespan))
|
_LOGGER.debug("*** life_span " + type + " = " + str(lifespan))
|
||||||
@@ -354,6 +429,9 @@ class VacBot():
|
|||||||
_LOGGER.debug("*** charge_status = " + self.charge_status)
|
_LOGGER.debug("*** charge_status = " + self.charge_status)
|
||||||
|
|
||||||
def _vacuum_address(self):
|
def _vacuum_address(self):
|
||||||
|
if self.vacuum['iot']:
|
||||||
|
return self.vacuum['did']
|
||||||
|
else:
|
||||||
return self.vacuum['did'] + '@' + self.vacuum['class'] + '.ecorobot.net/atom'
|
return self.vacuum['did'] + '@' + self.vacuum['class'] + '.ecorobot.net/atom'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -365,6 +443,10 @@ class VacBot():
|
|||||||
return self.vacuum_status in CLEANING_STATES
|
return self.vacuum_status in CLEANING_STATES
|
||||||
|
|
||||||
def send_ping(self):
|
def send_ping(self):
|
||||||
|
if self.vacuum['iot']:
|
||||||
|
#TODO
|
||||||
|
print("IOT Ping")
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
self.xmpp.send_ping(self._vacuum_address())
|
self.xmpp.send_ping(self._vacuum_address())
|
||||||
except XMPPError as err:
|
except XMPPError as err:
|
||||||
@@ -409,15 +491,113 @@ class VacBot():
|
|||||||
else:
|
else:
|
||||||
self.refresh_components()
|
self.refresh_components()
|
||||||
|
|
||||||
def send_command(self, xml):
|
def send_command(self, action):
|
||||||
self.xmpp.send_command(xml, self._vacuum_address())
|
if self.vacuum['iot']:
|
||||||
|
self.iot.send_command(action, self._vacuum_address())
|
||||||
|
else:
|
||||||
|
self.xmpp.send_command(action, self._vacuum_address())
|
||||||
|
|
||||||
def run(self, action):
|
def run(self, action):
|
||||||
|
if self.vacuum['iot']:
|
||||||
|
self.send_command(action)
|
||||||
|
else:
|
||||||
self.send_command(action.to_xml())
|
self.send_command(action.to_xml())
|
||||||
|
|
||||||
def disconnect(self, wait=False):
|
def disconnect(self, wait=False):
|
||||||
self.xmpp.disconnect(wait=wait)
|
self.xmpp.disconnect(wait=wait)
|
||||||
|
|
||||||
|
class EcoVacsIOT():
|
||||||
|
def __init__(self, user, domain, resource, secret, continent, vacuum):
|
||||||
|
self.uid = user
|
||||||
|
self.domain = domain
|
||||||
|
self.resource = resource
|
||||||
|
self.secret = secret
|
||||||
|
self.continent = continent
|
||||||
|
self.vacuum = vacuum
|
||||||
|
self.api = EcoVacsAPI
|
||||||
|
self.api.continent = continent
|
||||||
|
self.api.meta = {}
|
||||||
|
#self.add_event_handler("session_start", self.session_start)
|
||||||
|
self.ctl_subscribers = []
|
||||||
|
self.ready_flag = Event()
|
||||||
|
|
||||||
|
|
||||||
|
def connect_and_wait_until_ready(self):
|
||||||
|
self.connect(EcoVacsAPI._EcoVacsAPI__call_portal_api())
|
||||||
|
self.process()
|
||||||
|
self.wait_until_ready()
|
||||||
|
|
||||||
|
def send_command(self, action, recipient):
|
||||||
|
c = self._wrap_command(action, recipient)
|
||||||
|
_LOGGER.debug('Sending command {0}'.format(c))
|
||||||
|
self._handle_ctl(action, self.api._EcoVacsAPI__call_portal_api(self.api, self.api.IOTDEVMANAGERAPI,'',c ))
|
||||||
|
|
||||||
|
|
||||||
|
def _wrap_command(self, cmd, recipient):
|
||||||
|
|
||||||
|
q = {
|
||||||
|
'auth': {
|
||||||
|
'realm': EcoVacsAPI.REALM,
|
||||||
|
'resource': self.resource,
|
||||||
|
'token': self.secret,
|
||||||
|
'userid': self.uid,
|
||||||
|
'with': 'users',
|
||||||
|
},
|
||||||
|
"cmdName": cmd.name,
|
||||||
|
"payload": cmd.args_to_xml(),
|
||||||
|
"payloadType": "x",
|
||||||
|
"td": "q",
|
||||||
|
"toId": recipient,
|
||||||
|
"toRes": self.vacuum['resource'],
|
||||||
|
"toType": self.vacuum['class']
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
|
||||||
|
|
||||||
|
def subscribe_to_ctls(self, function):
|
||||||
|
self.ctl_subscribers.append(function)
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_ctl(self, action, message):
|
||||||
|
resp = self._ctl_to_dict(action, message['resp'])
|
||||||
|
if resp is not None:
|
||||||
|
for s in self.ctl_subscribers:
|
||||||
|
s(resp)
|
||||||
|
|
||||||
|
|
||||||
|
def _ctl_to_dict(self, action, xmlstring):
|
||||||
|
xml = ET.fromstring(xmlstring)
|
||||||
|
|
||||||
|
xmlchild = xml.getchildren()
|
||||||
|
if len(xmlchild) > 0:
|
||||||
|
result = xmlchild[0].attrib.copy()
|
||||||
|
#Fix for difference in XMPP vs IOT response
|
||||||
|
#Depending on the report will use the tag and add "report" to fit the mold of sucks library
|
||||||
|
if xmlchild[0].tag == "clean":
|
||||||
|
result['event'] = xmlchild[0].tag + "_report"
|
||||||
|
else: #Default back to replacing Get from the api cmdName
|
||||||
|
result['event'] = action.name.replace("Get","",1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
result = xml.attrib.copy()
|
||||||
|
result['event'] = action.name.replace("Get","",1)
|
||||||
|
|
||||||
|
for key in result:
|
||||||
|
result[key] = stringcase.snakecase(result[key])
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def session_start(self, event):
|
||||||
|
_LOGGER.debug("----------------- starting session ----------------")
|
||||||
|
_LOGGER.debug("event = {}".format(event))
|
||||||
|
#self.register_handler(Callback("general",
|
||||||
|
# MatchXPath('{jabber:client}iq/{com:ctl}query/{com:ctl}'),
|
||||||
|
# self._handle_ctl))
|
||||||
|
self.ready_flag.set()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EcoVacsXMPP(ClientXMPP):
|
class EcoVacsXMPP(ClientXMPP):
|
||||||
def __init__(self, user, domain, resource, secret, continent, server_address=None):
|
def __init__(self, user, domain, resource, secret, continent, server_address=None):
|
||||||
@@ -529,6 +709,17 @@ class VacBotCommand:
|
|||||||
ctl.set(key, value)
|
ctl.set(key, value)
|
||||||
return ctl
|
return ctl
|
||||||
|
|
||||||
|
def args_to_xml(self):
|
||||||
|
ctl = ET.Element('ctl',{})
|
||||||
|
for key, value in self.args.items():
|
||||||
|
if type(value) is dict:
|
||||||
|
inner = ET.Element(key, value)
|
||||||
|
ctl.append(inner)
|
||||||
|
else:
|
||||||
|
ctl.set(key, value)
|
||||||
|
return ET.tostring(ctl).decode()
|
||||||
|
|
||||||
|
|
||||||
def __str__(self, *args, **kwargs):
|
def __str__(self, *args, **kwargs):
|
||||||
return self.command_name() + " command"
|
return self.command_name() + " command"
|
||||||
|
|
||||||
|
|||||||
+17
-6
@@ -211,15 +211,26 @@ def run(actions, debug):
|
|||||||
api = EcoVacsAPI(config['device_id'], config['email'], config['password_hash'],
|
api = EcoVacsAPI(config['device_id'], config['email'], config['password_hash'],
|
||||||
config['country'], config['continent'])
|
config['country'], config['continent'])
|
||||||
vacuum = api.devices()[0]
|
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'])
|
||||||
vacbot.connect_and_wait_until_ready()
|
#vacbot.connect_and_wait_until_ready()
|
||||||
|
|
||||||
for action in actions:
|
|
||||||
click.echo("performing " + str(action.vac_command))
|
|
||||||
vacbot.run(action.vac_command)
|
|
||||||
action.wait.wait(vacbot)
|
|
||||||
|
|
||||||
vacbot.disconnect(wait=True)
|
|
||||||
|
vacbot.request_all_statuses()
|
||||||
|
|
||||||
|
print(vacbot.components)
|
||||||
|
print(vacbot.vacuum_status)
|
||||||
|
|
||||||
|
print(vacbot)
|
||||||
|
|
||||||
|
|
||||||
|
#for action in actions:
|
||||||
|
# click.echo("performing " + str(action.vac_command))
|
||||||
|
# vacbot.run(action.vac_command)
|
||||||
|
# action.wait.wait(vacbot)
|
||||||
|
|
||||||
|
#vacbot.disconnect(wait=True)
|
||||||
|
|
||||||
click.echo("done")
|
click.echo("done")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user