diff --git a/custom_components/ecovacs/__init__.py b/custom_components/ecovacs/__init__.py index 850b9a0..0716120 100644 --- a/custom_components/ecovacs/__init__.py +++ b/custom_components/ecovacs/__init__.py @@ -17,13 +17,12 @@ from homeassistant.helpers.typing import ConfigType import homeassistant.helpers.config_validation as cv import voluptuous as vol #use local sucks -from .sucks import EcoVacsAPI, VacBot -from .const import ( - ECOVACS_DEVICES, - DOMAIN, - CONF_CONTINENT, - LOGGER -) +from .sucks import VacBot +from .sucks_api import EcoVacsAPI +from .const import * + +import logging +LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( { diff --git a/custom_components/ecovacs/const.py b/custom_components/ecovacs/const.py index c04ec8c..4dc8fd8 100644 --- a/custom_components/ecovacs/const.py +++ b/custom_components/ecovacs/const.py @@ -1,6 +1,3 @@ -import logging -LOGGER = logging.getLogger(__name__) - #ecovacs constants #init constants ECOVACS_DEVICES = "ecovacs_devices" diff --git a/custom_components/ecovacs/manifest.json b/custom_components/ecovacs/manifest.json index e58d1b2..c7ec192 100644 --- a/custom_components/ecovacs/manifest.json +++ b/custom_components/ecovacs/manifest.json @@ -1,7 +1,7 @@ { "domain": "ecovacs", "name": "Ecovacs Bumper", - "version": "1.4.5", + "version": "1.5.0", "documentation": "https://github.com/bittles/ha_ecovacs_bumper", "issue_tracker": "https://github.com/bittles/ha_ecovacs_bumper/issues", "requirements": ["sleekxmppfs==1.4.1", "requests>=2.18", "pycryptodome>=3.4", "pycountry-convert>=0.5", "paho-mqtt>=1.4", "stringcase>=1.2"], diff --git a/custom_components/ecovacs/sucks.py b/custom_components/ecovacs/sucks.py index f42223d..264ac1e 100644 --- a/custom_components/ecovacs/sucks.py +++ b/custom_components/ecovacs/sucks.py @@ -1,226 +1,19 @@ -import hashlib -import time -import requests +#import hashlib +#import time +#import requests import os -from base64 import b64decode, b64encode -from collections import OrderedDict +#from base64 import b64decode, b64encode +#from collections import OrderedDict from sleekxmppfs.xmlstream import ET from sleekxmppfs.exceptions import XMPPError from .sucks_mqtt import EcoVacsIOTMQ from .sucks_xmpp import EcoVacsXMPP -from .const import LOGGER from .sucks_const import * -def str_to_bool_or_cert(s): - if s == 'True' or s == True: - return True - elif s == 'False' or s == False: - return False - else: - if not s == None: - if os.path.exists(s): # User could provide a path to a CA Cert as well, which is useful for Bumper - if os.path.isfile(s): - return s - else: - raise ValueError("Certificate path provided is not a file - {}".format(s)) - raise ValueError("Cannot covert {} to a bool or certificate path".format(s)) - -class EcoVacsAPI: - CLIENT_KEY = "eJUWrzRv34qFSaYk" - SECRET = "Cyu5jcR4zyK6QEPn1hdIGXB5QIDAQABMA0GC" - 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}' - 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 RestAPI, some bots use this instead of 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. Not sure what this provides the app. - REALM = 'ecouser.net' - - def __init__(self, device_id, account_id, password_hash, country, continent, verify_ssl=True): - self.meta = { - 'country': country, - 'lang': 'en', - 'deviceId': device_id, - 'appCode': 'i_eco_e', - #'appCode': 'i_eco_a' - iphone - 'appVersion': '1.3.5', - #'appVersion': '1.4.6' - iphone - 'channel': 'c_googleplay', - #'channel': 'c_iphone', - iphone - 'deviceType': '1' - #'deviceType': '2' - iphone - } - self.verify_ssl = str_to_bool_or_cert(verify_ssl) - LOGGER.debug("Setting up EcoVacsAPI") - self.resource = device_id[0:8] - self.country = country - self.continent = continent - login_info = self.__call_main_api('user/login', - ('account', self.encrypt(account_id)), - ('password', self.encrypt(password_hash))) - self.uid = login_info['uid'] - self.login_access_token = login_info['accessToken'] - self.auth_code = self.__call_main_api('user/getAuthCode', - ('uid', self.uid), - ('accessToken', self.login_access_token))['authCode'] - login_response = self.__call_login_by_it_token() - self.user_access_token = login_response['token'] - if login_response['userId'] != self.uid: - LOGGER.debug("Switching to shorter UID " + login_response['userId']) - self.uid = login_response['userId'] - LOGGER.debug("EcoVacsAPI connection complete") - - def __sign(self, params): - result = params.copy() - result['authTimespan'] = int(time.time() * 1000) - result['authTimeZone'] = 'GMT-8' - sign_on = self.meta.copy() - sign_on.update(result) - sign_on_text = EcoVacsAPI.CLIENT_KEY + ''.join( - [k + '=' + str(sign_on[k]) for k in sorted(sign_on.keys())]) + EcoVacsAPI.SECRET - result['authAppkey'] = EcoVacsAPI.CLIENT_KEY - result['authSign'] = self.md5(sign_on_text) - return result - - def __call_main_api(self, function, *args): - LOGGER.debug("calling main api {} with {}".format(function, args)) - params = OrderedDict(args) - params['requestId'] = self.md5(time.time()) - url = (EcoVacsAPI.MAIN_URL_FORMAT + "/" + function).format(**self.meta) - api_response = requests.get(url, self.__sign(params), verify=self.verify_ssl) - json = api_response.json() - LOGGER.debug("got {}".format(json)) - if json['code'] == '0000': - return json['data'] - elif json['code'] == '1005': - LOGGER.error("incorrect email or password") - raise ValueError("incorrect email or password") - else: - LOGGER.error("call to {} failed with {}".format(function, json)) - raise RuntimeError("failure code {} ({}) for call {} and parameters {}".format( - json['code'], json['msg'], function, args)) - - def __call_user_api(self, function, args): - LOGGER.debug("calling user api {} with {}".format(function, args)) - params = {'todo': function} - params.update(args) - response = requests.post(EcoVacsAPI.USER_URL_FORMAT.format(continent=self.continent), json=params, verify=self.verify_ssl) - json = response.json() - LOGGER.debug("got {}".format(json)) - if json['result'] == 'ok': - 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_portal_api(self, api, function, args, verify_ssl=True, **kwargs): - if api == self.USERSAPI: - params = {'todo': function} - params.update(args) - else: - params = {} - params.update(args) - LOGGER.debug("calling portal api {} function {} with {}".format(api, function, params)) - continent = self.continent - if 'continent' in kwargs: - continent = kwargs.get('continent') - url = (EcoVacsAPI.PORTAL_URL_FORMAT + "/" + api).format(continent=continent, **self.meta) - response = requests.post(url, json=params, verify=verify_ssl) - json = response.json() - LOGGER.debug("got {}".format(json)) - if api == self.USERSAPI: - if json['result'] == 'ok': - return json - elif json['result'] == 'fail': - if json['error'] == 'set token error.': # If it is a set token error try again - if not 'set_token' in kwargs: - LOGGER.debug("loginByItToken set token error, trying again (2/3)") - return self.__call_portal_api(self.USERSAPI, function, args, verify_ssl=verify_ssl, set_token=1) - elif kwargs.get('set_token') == 1: - LOGGER.debug("loginByItToken set token error, trying again with ww (3/3)") - return self.__call_portal_api(self.USERSAPI, function, args, verify_ssl=verify_ssl, set_token=2, continent="ww") - else: - LOGGER.debug("loginByItToken set token error, failed after 3 attempts") - 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): - return self.__call_portal_api(self.USERSAPI,'loginByItToken', - {'country': self.meta['country'].upper(), - 'resource': self.resource, - 'realm': EcoVacsAPI.REALM, - 'userId': self.uid, - 'token': self.auth_code} - , verify_ssl=self.verify_ssl) - - def getdevices(self): - return self.__call_portal_api(self.USERSAPI,'GetDeviceList', { - 'userid': self.uid, - 'auth': { - 'with': 'users', - 'userid': self.uid, - 'realm': EcoVacsAPI.REALM, - 'token': self.user_access_token, - 'resource': self.resource - } - }, verify_ssl=self.verify_ssl)['devices'] - - def getiotProducts(self): - return 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 - } - }, verify_ssl=self.verify_ssl)['data'] - - def SetIOTDevices(self, devices, iotproducts): - #Originally added for D900, and not actively used in code now - Not sure what the app checks the items in this list for - for device in devices: #Check if the device is part of iotProducts - device['iot_product'] = False - for iotProduct in iotproducts: - if device['class'] in iotProduct['classid']: - device['iot_product'] = True - - return devices - - def SetIOTMQDevices(self, devices): - #Added for devices that utilize MQTT instead of XMPP for communication - for device in devices: - device['iotmq'] = False - if device['company'] == 'eco-ng': #Check if the device is part of the list - device['iotmq'] = True - - return devices - - def devices(self): - return self.SetIOTMQDevices(self.getdevices()) - - @staticmethod - def md5(text): - return hashlib.md5(bytes(str(text), 'utf8')).hexdigest() - - @staticmethod - def encrypt(text): - from Crypto.PublicKey import RSA - from Crypto.Cipher import PKCS1_v1_5 - key = RSA.import_key(b64decode(EcoVacsAPI.PUBLIC_KEY)) - cipher = PKCS1_v1_5.new(key) - result = cipher.encrypt(bytes(text, 'utf8')) - return str(b64encode(result), 'utf8') +import logging +LOGGER = logging.getLogger(__name__) class EventEmitter(object): """A very simple event emitting system.""" diff --git a/custom_components/ecovacs/sucks_api.py b/custom_components/ecovacs/sucks_api.py new file mode 100644 index 0000000..d863d2f --- /dev/null +++ b/custom_components/ecovacs/sucks_api.py @@ -0,0 +1,224 @@ +import hashlib +import time +import requests +#import os +from base64 import b64decode, b64encode +from collections import OrderedDict +#from sleekxmppfs.xmlstream import ET +#from sleekxmppfs.exceptions import XMPPError + +#from .sucks_mqtt import EcoVacsIOTMQ +#from .sucks_xmpp import EcoVacsXMPP +from .sucks_api_const import * + +import logging +LOGGER = logging.getLogger(__name__) + +def str_to_bool_or_cert(s): + if s == 'True' or s == True: + return True + elif s == 'False' or s == False: + return False + else: + if not s == None: + if os.path.exists(s): # User could provide a path to a CA Cert as well, which is useful for Bumper + if os.path.isfile(s): + return s + else: + raise ValueError("Certificate path provided is not a file - {}".format(s)) + raise ValueError("Cannot covert {} to a bool or certificate path".format(s)) + +class EcoVacsAPI: + CLIENT_KEY = API_CLIENT_KEY + SECRET = API_SECRET + PUBLIC_KEY = API_PUBLIC_KEY + MAIN_URL_FORMAT = API_MAIN_URL_FORMAT + USER_URL_FORMAT = API_USER_URL_FORMAT + PORTAL_URL_FORMAT = API_PORTAL_URL_FORMAT + USERSAPI = API_USERSAPI + IOTDEVMANAGERAPI = API_IOTDEVMANAGERAPI # IOT Device Manager - This provides control of "IOT" products via RestAPI, some bots use this instead of XMPP + PRODUCTAPI = API_PRODUCTAPI # Leaving this open, the only endpoint known currently is "Product IOT Map" - pim/product/getProductIotMap - This provides a list of "IOT" products. Not sure what this provides the app. + REALM = API_REALM + + def __init__(self, device_id, account_id, password_hash, country, continent, verify_ssl=True): + self.meta = { + 'country': country, + 'lang': 'en', + 'deviceId': device_id, + 'appCode': 'i_eco_e', + #'appCode': 'i_eco_a' - iphone + 'appVersion': '1.3.5', + #'appVersion': '1.4.6' - iphone + 'channel': 'c_googleplay', + #'channel': 'c_iphone', - iphone + 'deviceType': '1' + #'deviceType': '2' - iphone + } + self.verify_ssl = str_to_bool_or_cert(verify_ssl) + LOGGER.debug("Setting up EcoVacsAPI") + self.resource = device_id[0:8] + self.country = country + self.continent = continent + login_info = self.__call_main_api('user/login', + ('account', self.encrypt(account_id)), + ('password', self.encrypt(password_hash))) + self.uid = login_info['uid'] + self.login_access_token = login_info['accessToken'] + self.auth_code = self.__call_main_api('user/getAuthCode', + ('uid', self.uid), + ('accessToken', self.login_access_token))['authCode'] + login_response = self.__call_login_by_it_token() + self.user_access_token = login_response['token'] + if login_response['userId'] != self.uid: + LOGGER.debug("Switching to shorter UID " + login_response['userId']) + self.uid = login_response['userId'] + LOGGER.debug("EcoVacsAPI connection complete") + + def __sign(self, params): + result = params.copy() + result['authTimespan'] = int(time.time() * 1000) + result['authTimeZone'] = 'GMT-8' + sign_on = self.meta.copy() + sign_on.update(result) + sign_on_text = EcoVacsAPI.CLIENT_KEY + ''.join( + [k + '=' + str(sign_on[k]) for k in sorted(sign_on.keys())]) + EcoVacsAPI.SECRET + result['authAppkey'] = EcoVacsAPI.CLIENT_KEY + result['authSign'] = self.md5(sign_on_text) + return result + + def __call_main_api(self, function, *args): + LOGGER.debug("calling main api {} with {}".format(function, args)) + params = OrderedDict(args) + params['requestId'] = self.md5(time.time()) + url = (EcoVacsAPI.MAIN_URL_FORMAT + "/" + function).format(**self.meta) + api_response = requests.get(url, self.__sign(params), verify=self.verify_ssl) + json = api_response.json() + LOGGER.debug("got {}".format(json)) + if json['code'] == '0000': + return json['data'] + elif json['code'] == '1005': + LOGGER.error("incorrect email or password") + raise ValueError("incorrect email or password") + else: + LOGGER.error("call to {} failed with {}".format(function, json)) + raise RuntimeError("failure code {} ({}) for call {} and parameters {}".format( + json['code'], json['msg'], function, args)) + + def __call_user_api(self, function, args): + LOGGER.debug("calling user api {} with {}".format(function, args)) + params = {'todo': function} + params.update(args) + response = requests.post(EcoVacsAPI.USER_URL_FORMAT.format(continent=self.continent), json=params, verify=self.verify_ssl) + json = response.json() + LOGGER.debug("got {}".format(json)) + if json['result'] == 'ok': + 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_portal_api(self, api, function, args, verify_ssl=True, **kwargs): + if api == self.USERSAPI: + params = {'todo': function} + params.update(args) + else: + params = {} + params.update(args) + LOGGER.debug("calling portal api {} function {} with {}".format(api, function, params)) + continent = self.continent + if 'continent' in kwargs: + continent = kwargs.get('continent') + url = (EcoVacsAPI.PORTAL_URL_FORMAT + "/" + api).format(continent=continent, **self.meta) + response = requests.post(url, json=params, verify=verify_ssl) + json = response.json() + LOGGER.debug("got {}".format(json)) + if api == self.USERSAPI: + if json['result'] == 'ok': + return json + elif json['result'] == 'fail': + if json['error'] == 'set token error.': # If it is a set token error try again + if not 'set_token' in kwargs: + LOGGER.debug("loginByItToken set token error, trying again (2/3)") + return self.__call_portal_api(self.USERSAPI, function, args, verify_ssl=verify_ssl, set_token=1) + elif kwargs.get('set_token') == 1: + LOGGER.debug("loginByItToken set token error, trying again with ww (3/3)") + return self.__call_portal_api(self.USERSAPI, function, args, verify_ssl=verify_ssl, set_token=2, continent="ww") + else: + LOGGER.debug("loginByItToken set token error, failed after 3 attempts") + 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): + return self.__call_portal_api(self.USERSAPI,'loginByItToken', + {'country': self.meta['country'].upper(), + 'resource': self.resource, + 'realm': EcoVacsAPI.REALM, + 'userId': self.uid, + 'token': self.auth_code} + , verify_ssl=self.verify_ssl) + + def getdevices(self): + return self.__call_portal_api(self.USERSAPI,'GetDeviceList', { + 'userid': self.uid, + 'auth': { + 'with': 'users', + 'userid': self.uid, + 'realm': EcoVacsAPI.REALM, + 'token': self.user_access_token, + 'resource': self.resource + } + }, verify_ssl=self.verify_ssl)['devices'] + + def getiotProducts(self): + return 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 + } + }, verify_ssl=self.verify_ssl)['data'] + + def SetIOTDevices(self, devices, iotproducts): + #Originally added for D900, and not actively used in code now - Not sure what the app checks the items in this list for + for device in devices: #Check if the device is part of iotProducts + device['iot_product'] = False + for iotProduct in iotproducts: + if device['class'] in iotProduct['classid']: + device['iot_product'] = True + + return devices + + def SetIOTMQDevices(self, devices): + #Added for devices that utilize MQTT instead of XMPP for communication + for device in devices: + device['iotmq'] = False + if device['company'] == 'eco-ng': #Check if the device is part of the list + device['iotmq'] = True + + return devices + + def devices(self): + return self.SetIOTMQDevices(self.getdevices()) + + @staticmethod + def md5(text): + return hashlib.md5(bytes(str(text), 'utf8')).hexdigest() + + @staticmethod + def encrypt(text): + from Crypto.PublicKey import RSA + from Crypto.Cipher import PKCS1_v1_5 + key = RSA.import_key(b64decode(EcoVacsAPI.PUBLIC_KEY)) + cipher = PKCS1_v1_5.new(key) + result = cipher.encrypt(bytes(text, 'utf8')) + return str(b64encode(result), 'utf8') \ No newline at end of file diff --git a/custom_components/ecovacs/sucks_api_const.py b/custom_components/ecovacs/sucks_api_const.py new file mode 100644 index 0000000..4283a2c --- /dev/null +++ b/custom_components/ecovacs/sucks_api_const.py @@ -0,0 +1,11 @@ +# API Constants +API_CLIENT_KEY = "eJUWrzRv34qFSaYk" +API_SECRET = "Cyu5jcR4zyK6QEPn1hdIGXB5QIDAQABMA0GC" +API_PUBLIC_KEY = 'MIIB/TCCAWYCCQDJ7TMYJFzqYDANBgkqhkiG9w0BAQUFADBCMQswCQYDVQQGEwJjbjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMCAXDTE3MDUwOTA1MTkxMFoYDzIxMTcwNDE1MDUxOTEwWjBCMQswCQYDVQQGEwJjbjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb8V0OYUGP3Fs63E1gJzJh+7iqeymjFUKJUqSD60nhWReZ+Fg3tZvKKqgNcgl7EGXp1yNifJKUNC/SedFG1IJRh5hBeDMGq0m0RQYDpf9l0umqYURpJ5fmfvH/gjfHe3Eg/NTLm7QEa0a0Il2t3Cyu5jcR4zyK6QEPn1hdIGXB5QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBANhIMT0+IyJa9SU8AEyaWZZmT2KEYrjakuadOvlkn3vFdhpvNpnnXiL+cyWy2oU1Q9MAdCTiOPfXmAQt8zIvP2JC8j6yRTcxJCvBwORDyv/uBtXFxBPEC6MDfzU2gKAaHeeJUWrzRv34qFSaYkYta8canK+PSInylQTjJK9VqmjQ' +API_MAIN_URL_FORMAT = 'https://eco-{country}-api.ecovacs.com/v1/private/{country}/{lang}/{deviceId}/{appCode}/{appVersion}/{channel}/{deviceType}' +API_USER_URL_FORMAT = 'https://users-{continent}.ecouser.net:8000/user.do' +API_PORTAL_URL_FORMAT = 'https://portal-{continent}.ecouser.net/api' +API_USERSAPI = 'users/user.do' +API_IOTDEVMANAGERAPI = 'iot/devmanager.do' +API_PRODUCTAPI = 'pim/product' +API_REALM = 'ecouser.net' \ No newline at end of file diff --git a/custom_components/ecovacs/sucks_mqtt.py b/custom_components/ecovacs/sucks_mqtt.py index 3a04897..4289ab9 100644 --- a/custom_components/ecovacs/sucks_mqtt.py +++ b/custom_components/ecovacs/sucks_mqtt.py @@ -9,9 +9,11 @@ from paho.mqtt.client import Client as ClientMQTT from paho.mqtt import publish as MQTTPublish from paho.mqtt import subscribe as MQTTSubscribe from sleekxmppfs.xmlstream import ET -from .sucks import EcoVacsAPI -from .const import LOGGER +from .sucks_api_const import API_REALM, API_IOTDEVMANAGERAPI + +import logging +LOGGER = logging.getLogger(__name__) def str_to_bool_or_cert(s): if s == 'True' or s == True: @@ -132,7 +134,7 @@ class EcoVacsIOTMQ(ClientMQTT): payloadxml.attrib.pop("td") return { 'auth': { - 'realm': EcoVacsAPI.REALM, + 'realm': API_REALM, 'resource': self.resource, 'token': self.secret, 'userid': self.user, @@ -152,7 +154,7 @@ class EcoVacsIOTMQ(ClientMQTT): LOGGER.debug("calling iotdevmanager api with {}".format(args)) params = {} params.update(args) - url = (EcoVacsAPI.PORTAL_URL_FORMAT + "/iot/devmanager.do").format(continent=self.continent) + url = (API_PORTAL_URL_FORMAT + "/" + API_IOTDEVMANAGERAPI).format(continent=self.continent) response = None try: #The RestAPI sometimes doesnt provide a response depending on command, reduce timeout to 3 to accomodate and make requests faster response = requests.post(url, json=params, timeout=3, verify=verify_ssl) #May think about having timeout as an arg that could be provided in the future diff --git a/custom_components/ecovacs/sucks_xmpp.py b/custom_components/ecovacs/sucks_xmpp.py index e021387..5ed8f4e 100644 --- a/custom_components/ecovacs/sucks_xmpp.py +++ b/custom_components/ecovacs/sucks_xmpp.py @@ -4,7 +4,9 @@ from threading import Event from sleekxmppfs import ClientXMPP, Callback, MatchXPath from sleekxmppfs.xmlstream import ET #from sleekxmppfs.exceptions import XMPPError -from .const import LOGGER + +import logging +LOGGER = logging.getLogger(__name__) #This is used by EcoVacsIOTMQ and EcoVacsXMPP for _ctl_to_dict def RepresentsInt(stringvar):