diff --git a/custom_components/ecovacs/__init__.py b/custom_components/ecovacs/__init__.py index 850b9a0..4eb1b03 100644 --- a/custom_components/ecovacs/__init__.py +++ b/custom_components/ecovacs/__init__.py @@ -1,7 +1,16 @@ """Support for Ecovacs Deebot vacuums.""" +import asyncio +from functools import partial +import async_timeout + import random import string -#import asyncio ## to do will need to convert to slixmpp to do this i believe +import logging +# Use local sucks +from .sucks import EcoVacsAPI, VacBot + +from homeassistant import exceptions +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_USERNAME, @@ -9,36 +18,39 @@ from homeassistant.const import ( CONF_COUNTRY, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP, - Platform, ) + from homeassistant.core import HomeAssistant -from homeassistant.helpers import discovery -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, + PLATFORMS, CONF_CONTINENT, - LOGGER ) -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Required(CONF_COUNTRY): vol.All(vol.Lower, cv.string), - vol.Required(CONF_CONTINENT): vol.All(vol.Lower, cv.string), - vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, # can probably get rid of this and set verify ssl false if - } - ) - }, - extra=vol.ALLOW_EXTRA, -) +# from homeassistant.core import HomeAssistant +# from homeassistant.helpers import discovery +# from homeassistant.helpers.typing import ConfigType +# import homeassistant.helpers.config_validation as cv +# import voluptuous as vol + +_LOGGER = logging.getLogger(__name__) + +# CONFIG_SCHEMA = vol.Schema( + # { + # DOMAIN: vol.Schema( + # { + # vol.Required(CONF_USERNAME): cv.string, + # vol.Required(CONF_PASSWORD): cv.string, + # vol.Required(CONF_COUNTRY): vol.All(vol.Lower, cv.string), + # vol.Required(CONF_CONTINENT): vol.All(vol.Lower, cv.string), + # vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, # can probably get rid of this and set verify ssl false if + # } + # ) + # }, + # extra=vol.ALLOW_EXTRA, +# ) # Generate a random device ID on each bootup ECOVACS_API_DEVICEID = "".join( @@ -47,7 +59,7 @@ ECOVACS_API_DEVICEID = "".join( def setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Ecovacs component.""" - LOGGER.debug("Creating new Ecovacs component") + _LOGGER.debug("Creating new Ecovacs component") hass.data[ECOVACS_DEVICES] = [] SERVER_ADDRESS = None @@ -61,10 +73,10 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: ) devices = ecovacs_api.devices() - LOGGER.debug("Ecobot devices: %s", devices) + _LOGGER.debug("Ecobot devices: %s", devices) for device in devices: - LOGGER.info( + _LOGGER.info( "Discovered Ecovacs device on account: %s with nickname %s", device.get("did"), device.get("nick"), @@ -85,7 +97,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: def stop(event: object) -> None: """Shut down open connections to Ecovacs XMPP server.""" for device in hass.data[ECOVACS_DEVICES]: - LOGGER.info( + _LOGGER.info( "Shutting down connection to Ecovacs device %s", device.vacuum.get("did"), ) @@ -94,6 +106,70 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: # Listen for HA stop to disconnect. hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop) if hass.data[ECOVACS_DEVICES]: - LOGGER.debug("Starting vacuum components") + _LOGGER.debug("Starting vacuum components") discovery.load_platform(hass, Platform.VACUUM, DOMAIN, {}, config) return True + +async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Set the config entry up.""" + # Set up Ecovacs platforms with config entry + _LOGGER.debug("Creating new Ecovacs component") + hass.data[ECOVACS_DEVICES] = [] + SERVER_ADDRESS = None + + ecovacs_api = EcoVacsAPI( + ECOVACS_API_DEVICEID, + config[DOMAIN].get(CONF_USERNAME), + EcoVacsAPI.md5(config[DOMAIN].get(CONF_PASSWORD)), + config[DOMAIN].get(CONF_COUNTRY), + config[DOMAIN].get(CONF_CONTINENT), + config[DOMAIN].get(CONF_VERIFY_SSL), # add to class call + ) + + + if not config_entry.options: + hass.config_entries.async_update_entry( + config_entry, + options={ + CONF_CONTINUOUS: config_entry.data[CONF_CONTINUOUS], + CONF_DELAY: config_entry.data[CONF_DELAY], + }, + ) + + roomba = await hass.async_add_executor_job( + partial( + RoombaFactory.create_roomba, + address=config_entry.data[CONF_HOST], + blid=config_entry.data[CONF_BLID], + password=config_entry.data[CONF_PASSWORD], + continuous=config_entry.options[CONF_CONTINUOUS], + delay=config_entry.options[CONF_DELAY], + ) + ) + + try: + if not await async_connect_or_timeout(hass, roomba): + return False + except CannotConnect as err: + raise exceptions.ConfigEntryNotReady from err + + async def _async_disconnect_roomba(event): + await async_disconnect_or_timeout(hass, roomba) + + cancel_stop = hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, _async_disconnect_roomba + ) + + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][config_entry.entry_id] = { + ROOMBA_SESSION: roomba, + BLID: config_entry.data[CONF_BLID], + CANCEL_STOP: cancel_stop, + } + + await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) + + if not config_entry.update_listeners: + config_entry.add_update_listener(async_update_options) + + return True diff --git a/custom_components/ecovacs/const.py b/custom_components/ecovacs/const.py index c04ec8c..f3ec38e 100644 --- a/custom_components/ecovacs/const.py +++ b/custom_components/ecovacs/const.py @@ -1,8 +1,8 @@ -import logging -LOGGER = logging.getLogger(__name__) +""" Ecovacs constants. """ +from homeassistant.const import Platform -#ecovacs constants -#init constants -ECOVACS_DEVICES = "ecovacs_devices" DOMAIN = "ecovacs" +PLATFORMS = [Platform.Vacuum] +ECOVACS_DEVICES = "ecovacs_devices" + CONF_CONTINENT = "continent" \ No newline at end of file diff --git a/custom_components/ecovacs/sucks.py b/custom_components/ecovacs/sucks.py index f42223d..9e39f9d 100644 --- a/custom_components/ecovacs/sucks.py +++ b/custom_components/ecovacs/sucks.py @@ -2,6 +2,8 @@ import hashlib import time import requests import os +import logging +import aiohttp from base64 import b64decode, b64encode from collections import OrderedDict from sleekxmppfs.xmlstream import ET @@ -10,9 +12,11 @@ from sleekxmppfs.exceptions import XMPPError from .sucks_mqtt import EcoVacsIOTMQ from .sucks_xmpp import EcoVacsXMPP -from .const import LOGGER +#from .const import LOGGER from .sucks_const import * +_LOGGER = logging.getLogger(__name__) + def str_to_bool_or_cert(s): if s == 'True' or s == True: return True @@ -27,6 +31,10 @@ def str_to_bool_or_cert(s): raise ValueError("Certificate path provided is not a file - {}".format(s)) raise ValueError("Cannot covert {} to a bool or certificate path".format(s)) +def get_ecovacs_api(device_id: str, username: str, password: str, country: str, continent: str, verify_ssl: bool, websession: Optional[aiohttp.ClientSession] = None) + """ Get Ecovacs api object """ + return EcoVacsAPI(device_id, username, password, country, continent, verify_ssl, websession) + class EcoVacsAPI: CLIENT_KEY = "eJUWrzRv34qFSaYk" SECRET = "Cyu5jcR4zyK6QEPn1hdIGXB5QIDAQABMA0GC" @@ -39,7 +47,15 @@ class EcoVacsAPI: 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): + def __init__( + self, + device_id, + account_id, + password_hash, + country, + continent, + verify_ssl=True, + websession: Optional[aiohttp.ClientSession] = None): self.meta = { 'country': country, 'lang': 'en', @@ -54,7 +70,7 @@ class EcoVacsAPI: #'deviceType': '2' - iphone } self.verify_ssl = str_to_bool_or_cert(verify_ssl) - LOGGER.debug("Setting up EcoVacsAPI") + _LOGGER.debug("Setting up EcoVacsAPI") self.resource = device_id[0:8] self.country = country self.continent = continent @@ -69,9 +85,16 @@ class EcoVacsAPI: 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']) + _LOGGER.debug("Switching to shorter UID " + login_response['userId']) self.uid = login_response['userId'] - LOGGER.debug("EcoVacsAPI connection complete") + self.websession = websession + _LOGGER.debug("EcoVacsAPI connection complete") + + def ensure_session(self) -> aiohttp.ClientSession: + """Ensure that we have an aiohttp ClientSession""" + if self.websession is None: + self.websession = aiohttp.ClientSession() + return self.websession def __sign(self, params): result = params.copy() @@ -86,34 +109,34 @@ class EcoVacsAPI: return result def __call_main_api(self, function, *args): - LOGGER.debug("calling main api {} with {}".format(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)) + _LOGGER.debug("got {}".format(json)) if json['code'] == '0000': return json['data'] elif json['code'] == '1005': - LOGGER.error("incorrect email or password") + _LOGGER.error("incorrect email or password") raise ValueError("incorrect email or password") else: - LOGGER.error("call to {} failed with {}".format(function, json)) + _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)) + _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)) + _LOGGER.debug("got {}".format(json)) if json['result'] == 'ok': return json else: - LOGGER.error("call to {} failed with {}".format(function, json)) + _LOGGER.error("call to {} failed with {}".format(function, json)) raise RuntimeError( "failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params)) @@ -124,33 +147,33 @@ class EcoVacsAPI: else: params = {} params.update(args) - LOGGER.debug("calling portal api {} function {} with {}".format(api, function, params)) + _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)) + _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)") + _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)") + _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") + _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)) + _LOGGER.error("call to {} failed with {}".format(function, json)) raise RuntimeError( "failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params)) @@ -175,6 +198,31 @@ class EcoVacsAPI: } }, verify_ssl=self.verify_ssl)['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') + +""" older currently unused code from before app update def getiotProducts(self): return self.__call_portal_api(self.PRODUCTAPI + '/getProductIotMap','', { 'channel': '', @@ -194,33 +242,8 @@ class EcoVacsAPI: 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') +""" class EventEmitter(object): """A very simple event emitting system.""" @@ -318,23 +341,23 @@ class VacBot(): error = event['errs'] if not error == '': self.errorEvents.notify(error) - LOGGER.error("*** error = " + error) + _LOGGER.error("*** error = " + error) def _handle_life_span(self, event): type = event['type'] try: type = COMPONENT_FROM_ECOVACS[type] except KeyError: - LOGGER.warning("Unknown component type: '" + type + "'") + _LOGGER.warning("Unknown component type: '" + type + "'") if 'val' in event: lifespan = int(event['val']) / 100 - LOGGER.info("**********Component " + type + " has lifespan of " + str(lifespan) + ".") + _LOGGER.info("**********Component " + type + " has lifespan of " + str(lifespan) + ".") else: lifespan = int(event['left']) / 60 #This works for a D901 self.components[type] = lifespan lifespan_event = {'type': type, 'lifespan': lifespan} self.lifespanEvents.notify(lifespan_event) - LOGGER.info("*** life_span " + type + " = " + str(lifespan)) + _LOGGER.info("*** life_span " + type + " = " + str(lifespan)) def _handle_clean_report(self, event): type = event['type'] @@ -346,7 +369,7 @@ class VacBot(): if statustype == CLEAN_ACTION_STOP or statustype == CLEAN_ACTION_PAUSE: type = statustype except KeyError: - LOGGER.warning("Unknown cleaning status '" + type + "'") + _LOGGER.warning("Unknown cleaning status '" + type + "'") self.clean_status = type self.vacuum_status = type fan = event.get('speed', None) @@ -354,22 +377,22 @@ class VacBot(): try: fan = FAN_SPEED_FROM_ECOVACS[fan] except KeyError: - LOGGER.warning("Unknown fan speed: '" + fan + "'") + _LOGGER.warning("Unknown fan speed: '" + fan + "'") self.fan_speed = fan self.statusEvents.notify(self.vacuum_status) if self.fan_speed: - LOGGER.info("*** clean_status = " + self.clean_status + " fan_speed = " + self.fan_speed) + _LOGGER.info("*** clean_status = " + self.clean_status + " fan_speed = " + self.fan_speed) else: - LOGGER.info("*** clean_status = " + self.clean_status + " fan_speed = None") + _LOGGER.info("*** clean_status = " + self.clean_status + " fan_speed = None") def _handle_battery_info(self, iq): try: self.battery_status = float(iq['power']) / 100 except ValueError: - LOGGER.warning("couldn't parse battery status " + ET.tostring(iq)) + _LOGGER.warning("couldn't parse battery status " + ET.tostring(iq)) else: self.batteryEvents.notify(self.battery_status) - LOGGER.info("*** battery_status = {:.0%}".format(self.battery_status)) + _LOGGER.info("*** battery_status = {:.0%}".format(self.battery_status)) def _handle_charge_state(self, event): if 'type' in event: @@ -383,11 +406,11 @@ class VacBot(): status = 'idle' else: status = 'idle' #Fall back to Idle status - LOGGER.error("Unknown charging status '" + event['errno'] + "'") #Log this so we can identify more errors + _LOGGER.error("Unknown charging status '" + event['errno'] + "'") #Log this so we can identify more errors try: status = CHARGE_MODE_FROM_ECOVACS[status] except KeyError: - LOGGER.warning("Unknown charging status '" + status + "'") + _LOGGER.warning("Unknown charging status '" + status + "'") self.charge_status = status if status != 'idle' or self.vacuum_status == 'charging': # We have to ignore the idle messages, because all it means is that it's not @@ -395,7 +418,7 @@ class VacBot(): # of what the vacuum is currently up to. self.vacuum_status = status self.statusEvents.notify(self.vacuum_status) - LOGGER.info("*** charge_status = " + self.charge_status) + _LOGGER.info("*** charge_status = " + self.charge_status) def _vacuum_address(self): if not self.vacuum['iotmq']: @@ -419,15 +442,15 @@ class VacBot(): if not self.iotmq.send_ping(): raise RuntimeError() except XMPPError as err: - LOGGER.warning("Ping did not reach VacBot. Will retry.") - LOGGER.error("*** Error type: " + err.etype) - LOGGER.error("*** Error condition: " + err.condition) + _LOGGER.warning("Ping did not reach VacBot. Will retry.") + _LOGGER.error("*** Error type: " + err.etype) + _LOGGER.error("*** Error condition: " + err.condition) self._failed_pings += 1 if self._failed_pings >= 4: self.vacuum_status = 'offline' self.statusEvents.notify(self.vacuum_status) except RuntimeError as err: - LOGGER.warning("Ping did not reach VacBot. Will retry.") + _LOGGER.warning("Ping did not reach VacBot. Will retry.") self._failed_pings += 1 if self._failed_pings >= 4: self.vacuum_status = 'offline' @@ -450,9 +473,9 @@ class VacBot(): self.run(GetLifeSpan('side_brush')) self.run(GetLifeSpan('filter')) except XMPPError as err: - LOGGER.warning("Component refresh requests failed to reach VacBot. Will try again later.") - LOGGER.error("*** Error type: " + err.etype) - LOGGER.error("*** Error condition: " + err.condition) + _LOGGER.warning("Component refresh requests failed to reach VacBot. Will try again later.") + _LOGGER.error("*** Error type: " + err.etype) + _LOGGER.error("*** Error condition: " + err.condition) def refresh_statuses(self): try: @@ -460,9 +483,9 @@ class VacBot(): self.run(GetChargeState()) self.run(GetBatteryState()) except XMPPError as err: - LOGGER.warning("Initial status requests failed to reach VacBot. Will try again on next ping.") - LOGGER.error("*** Error type: " + err.etype) - LOGGER.error("*** Error condition: " + err.condition) + _LOGGER.warning("Initial status requests failed to reach VacBot. Will try again on next ping.") + _LOGGER.error("*** Error type: " + err.etype) + _LOGGER.error("*** Error condition: " + err.condition) def request_all_statuses(self): self.refresh_statuses() diff --git a/custom_components/ecovacs/sucks_mqtt.py b/custom_components/ecovacs/sucks_mqtt.py index 09ec826..fd5cf17 100644 --- a/custom_components/ecovacs/sucks_mqtt.py +++ b/custom_components/ecovacs/sucks_mqtt.py @@ -4,13 +4,16 @@ import threading import ssl import requests import stringcase +import logging from threading import Event 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 .const import LOGGER +#from .const import LOGGER + +_LOGGER = logging.getLogger(__name__) #This is used by EcoVacsIOTMQ and EcoVacsXMPP for _ctl_to_dict def RepresentsInt(stringvar): @@ -83,19 +86,19 @@ class EcoVacsIOTMQ(ClientMQTT): def on_connect(self, client, userdata, flags, rc): if rc != 0: - LOGGER.error("EcoVacsMQTT - error connecting with MQTT Return {}".format(rc)) + _LOGGER.error("EcoVacsMQTT - error connecting with MQTT Return {}".format(rc)) raise RuntimeError("EcoVacsMQTT - error connecting with MQTT Return {}".format(rc)) else: - LOGGER.debug("EcoVacsMQTT - Connected with result code "+str(rc)) - LOGGER.debug("EcoVacsMQTT - Subscribing to all") + _LOGGER.debug("EcoVacsMQTT - Connected with result code "+str(rc)) + _LOGGER.debug("EcoVacsMQTT - Subscribing to all") self.subscribe('iot/atr/+/' + self.vacuum['did'] + '/' + self.vacuum['class'] + '/' + self.vacuum['resource'] + '/+', qos=0) self.ready_flag.set() #def on_log(self, client, userdata, level, buf): #This is very noisy and verbose - # LOGGER.debug("EcoVacsMQTT Log: {} ".format(buf)) + # _LOGGER.debug("EcoVacsMQTT Log: {} ".format(buf)) def send_ping(self): - LOGGER.debug("*** MQTT sending ping ***") + _LOGGER.debug("*** MQTT sending ping ***") rc = self._send_simple_command(MQTTPublish.paho.PINGREQ) if rc == MQTTPublish.paho.MQTT_ERR_SUCCESS: return True @@ -106,7 +109,7 @@ class EcoVacsIOTMQ(ClientMQTT): if action.name == "Clean": #For handling Clean when action not specified (i.e. CLI) action.args['clean']['act'] = CLEAN_ACTION_TO_ECOVACS['start'] #Inject a start action c = self._wrap_command(action, recipient) - LOGGER.debug('Sending command {0}'.format(c)) + _LOGGER.debug('Sending command {0}'.format(c)) self._handle_ctl_api(action, self.__call_iotdevmanager_api(c ,verify_ssl=self.verify_ssl ) ) @@ -134,7 +137,7 @@ class EcoVacsIOTMQ(ClientMQTT): } def __call_iotdevmanager_api(self, args, verify_ssl=True): - LOGGER.debug("calling iotdevmanager api with {}".format(args)) + _LOGGER.debug("calling iotdevmanager api with {}".format(args)) params = {} params.update(args) url = (EcoVacsAPI.PORTAL_URL_FORMAT + "/iot/devmanager.do").format(continent=self.continent) @@ -142,7 +145,7 @@ class EcoVacsIOTMQ(ClientMQTT): 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 except requests.exceptions.ReadTimeout: - LOGGER.debug("call to iotdevmanager failed with ReadTimeout") + _LOGGER.debug("call to iotdevmanager failed with ReadTimeout") return {} json = response.json() if json['ret'] == 'ok': @@ -151,11 +154,11 @@ class EcoVacsIOTMQ(ClientMQTT): if 'debug' in json: if json['debug'] == 'wait for response timed out': #TODO - Maybe handle timeout for IOT better in the future - LOGGER.error("call to iotdevmanager failed with {}".format(json)) + _LOGGER.error("call to iotdevmanager failed with {}".format(json)) return {} else: #TODO - Not sure if we want to raise an error yet, just return empty for now - LOGGER.error("call to iotdevmanager failed with {}".format(json)) + _LOGGER.error("call to iotdevmanager failed with {}".format(json)) return {} #raise RuntimeError( #"failure {} ({}) for call {} and parameters {}".format(json['error'], json['errno'], function, params)) @@ -195,7 +198,7 @@ class EcoVacsIOTMQ(ClientMQTT): return result def _handle_ctl_mqtt(self, client, userdata, message): - #LOGGER.debug("EcoVacs MQTT Received Message on Topic: {} - Message: {}".format(message.topic, str(message.payload.decode("utf-8")))) + #_LOGGER.debug("EcoVacs MQTT Received Message on Topic: {} - Message: {}".format(message.topic, str(message.payload.decode("utf-8")))) as_dict = self._ctl_to_dict_mqtt(message.topic, str(message.payload.decode("utf-8"))) if as_dict is not None: for s in self.ctl_subscribers: diff --git a/custom_components/ecovacs/sucks_xmpp.py b/custom_components/ecovacs/sucks_xmpp.py index e021387..b97a6d1 100644 --- a/custom_components/ecovacs/sucks_xmpp.py +++ b/custom_components/ecovacs/sucks_xmpp.py @@ -1,11 +1,13 @@ import stringcase import random +import logging from threading import Event from sleekxmppfs import ClientXMPP, Callback, MatchXPath from sleekxmppfs.xmlstream import ET #from sleekxmppfs.exceptions import XMPPError -from .const import LOGGER +#from .const import LOGGER +_LOGGER = logging.getLogger(__name__) #This is used by EcoVacsIOTMQ and EcoVacsXMPP for _ctl_to_dict def RepresentsInt(stringvar): try: @@ -35,8 +37,8 @@ class EcoVacsXMPP(ClientXMPP): self.ready_flag.wait() def session_start(self, event): - LOGGER.debug("----------------- starting session ----------------") - LOGGER.debug("event = {}".format(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)) @@ -63,7 +65,7 @@ class EcoVacsXMPP(ClientXMPP): try: # check for child xml childxml = xml[0] except IndexError: - LOGGER.debug("No child xml") + _LOGGER.debug("No child xml") if 'td' not in result: # Handle response data with no 'td' if 'type' in result: # single element with type and val @@ -98,7 +100,7 @@ class EcoVacsXMPP(ClientXMPP): def send_command(self, xml, recipient): c = self._wrap_command(xml, recipient) - LOGGER.debug('Sending command {0}'.format(c)) + _LOGGER.debug('Sending command {0}'.format(c)) c.send() def _wrap_command(self, ctl, recipient): @@ -129,12 +131,12 @@ class EcoVacsXMPP(ClientXMPP): def send_ping(self, to): q = self.make_iq_get(ito=to, ifrom=self._my_address()) q.xml.append(ET.Element('ping', {'xmlns': 'urn:xmpp:ping'})) - LOGGER.debug("*** sending ping ***") + _LOGGER.debug("*** sending ping ***") q.send() # used some code from a sleekxmppfs plugin, seems to work fine def _handle_ping(self, iq): - LOGGER.debug("Pinged by %s", iq['from']) + _LOGGER.debug("Pinged by %s", iq['from']) iq.reply().send() def connect_and_wait_until_ready(self):