diff --git a/sucks/__init__.py b/sucks/__init__.py index 1316cba..3f37169 100644 --- a/sucks/__init__.py +++ b/sucks/__init__.py @@ -27,13 +27,14 @@ _LOGGER = logging.getLogger(__name__) CLEAN_MODE_AUTO = 'auto' CLEAN_MODE_EDGE = 'edge' CLEAN_MODE_SPOT = 'spot' -CLEAN_MODE_SPOT_AREA = 'spotarea' +CLEAN_MODE_SPOT_AREA = 'spot_area' CLEAN_MODE_SINGLE_ROOM = 'single_room' CLEAN_MODE_STOP = 'stop' CLEAN_ACTION_START = 'start' CLEAN_ACTION_PAUSE = 'pause' CLEAN_ACTION_RESUME = 'resume' +CLEAN_ACTION_STOP = 'stop' FAN_SPEED_NORMAL = 'normal' FAN_SPEED_HIGH = 'high' @@ -67,13 +68,21 @@ CLEAN_ACTION_TO_ECOVACS = { CLEAN_ACTION_START: 's', CLEAN_ACTION_PAUSE: 'p', CLEAN_ACTION_RESUME: 'r', + CLEAN_ACTION_STOP: 'h', +} + +CLEAN_ACTION_FROM_ECOVACS = { + 's': CLEAN_ACTION_START, + 'p': CLEAN_ACTION_PAUSE, + 'r': CLEAN_ACTION_RESUME, + 'h': CLEAN_ACTION_STOP, } CLEAN_MODE_FROM_ECOVACS = { 'auto': CLEAN_MODE_AUTO, 'border': CLEAN_MODE_EDGE, 'spot': CLEAN_MODE_SPOT, - 'SpotArea': CLEAN_MODE_SPOT_AREA, + 'spot_area': CLEAN_MODE_SPOT_AREA, 'singleroom': CLEAN_MODE_SINGLE_ROOM, 'stop': CLEAN_MODE_STOP, 'going': CHARGE_MODE_RETURNING @@ -397,12 +406,15 @@ class VacBot(): self.iot.subscribe_to_ctls(self._handle_ctl) self.mqtt = EcoVacsMQTT(user, domain, resource, secret, continent, vacuum, server_address) self.mqtt.subscribe_to_ctls(self._handle_ctl) - self.xmpp = EcoVacsXMPP(user, domain, resource, secret, continent, vacuum, server_address) - self.xmpp.subscribe_to_ctls(self._handle_ctl) - + #self.xmpp = EcoVacsXMPP(user, domain, resource, secret, continent, vacuum, server_address) + #Uncomment line to allow unencrypted plain auth + #self.xmpp['feature_mechanisms'].unencrypted_plain = True + #self.xmpp.subscribe_to_ctls(self._handle_ctl) else: self.xmpp = EcoVacsXMPP(user, domain, resource, secret, continent, vacuum, server_address) + #Uncomment line to allow unencrypted plain auth + #self.xmpp['feature_mechanisms'].unencrypted_plain = True self.xmpp.subscribe_to_ctls(self._handle_ctl) @@ -413,6 +425,7 @@ class VacBot(): else: self.mqtt.connect_and_wait_until_ready() self.mqtt.schedule(30, self.send_ping) + #self.xmpp.connect_and_wait_until_ready() if self._monitor: # Do a first ping, which will also fetch initial statuses if the ping succeeds @@ -459,19 +472,15 @@ class VacBot(): type = event['type'] try: type = CLEAN_MODE_FROM_ECOVACS[type] + if self.vacuum['iot']: #Was able to parse additional status from the IOT, may apply to XMPP too + statustype = event['st'] + statustype = CLEAN_ACTION_FROM_ECOVACS[statustype] + if statustype == CLEAN_ACTION_STOP or statustype == CLEAN_ACTION_PAUSE: + type = statustype except KeyError: _LOGGER.warning("Unknown cleaning status '" + type + "'") self.clean_status = type - self.vacuum_status = type - - if self.vacuum['iot']: #Was able to parse additional status from the IOT, may apply to XMPP too - cleaning = event.get('st', None) - if cleaning == 'p': - self.clean_status = 'paused' - elif cleaning == 'h': - self.clean_status = 'standby' - else: - self.clean_status = 'cleaning' + self.vacuum_status = type fan = event.get('speed', None) if fan is not None: @@ -541,13 +550,13 @@ class VacBot(): try: if not self.vacuum['iot']: self.xmpp.send_ping(self._vacuum_address()) - elif self.vacuum['iot']: + elif self.vacuum['iot']: if not self.mqtt.send_ping(): raise RuntimeError() #self.xmpp.send_ping(EcoVacsAPI.REALM) #IOT vacuums are using the realm instead #Some devices may utilize this, but it appears to - # just be an oversight in the app communidcations. IOT should probably be using MQTT pings (which are automatic when connected) + # just be an oversight in the app communications. IOT should probably be using MQTT pings (which are automatic when connected) except XMPPError as err: @@ -618,6 +627,7 @@ class VacBot(): self.xmpp.disconnect(wait=wait) else: self.mqtt._disconnect() + #self.xmpp.disconnect(wait=wait) @@ -793,7 +803,7 @@ class EcoVacsMQTT(ClientMQTT): self.ctl_subscribers.append(function) def _handle_ctl(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(message.topic, str(message.payload.decode("utf-8"))) if as_dict is not None: for s in self.ctl_subscribers: @@ -843,7 +853,6 @@ class EcoVacsMQTT(ClientMQTT): _LOGGER.debug("*** MQTT sending ping ***") rc = self._send_simple_command(MQTTPublish.paho.PINGREQ) if rc == MQTTPublish.paho.MQTT_ERR_SUCCESS: - _LOGGER.debug("*** MQTT ping acknowledged ***") return True else: return False @@ -868,33 +877,9 @@ class EcoVacsMQTT(ClientMQTT): self.wait_until_ready() - # def send_command(self, xml, recipient): #MQTT doesn't seem to care about commands we send today, but leaving in case of futures - # #c = self._wrap_command(xml, recipient) - # #_LOGGER.debug('Sending command {0}'.format(c)) - # txml = '"' - # _LOGGER.debug('Sending command {0}'.format(txml)) - # self.publish('iot/atr/Move/' + self.vacuum['did'] + '/' + self.vacuum['class'] + '/' + self.vacuum['resource'] + '/x', txml) - # # - - # def _wrap_command(self, ctl, recipient): - # q = self.make_iq_query(xmlns=u'com:ctl', ito=recipient, ifrom=self._my_address()) - # q['type'] = 'set' - # for child in q.xml: - # if child.tag.endswith('query'): - # child.append(ctl) - # return q - - # def _my_address(self): - # if not self.vacuum['iot']: - # return self.user + '@' + self.domain + '/' + self.boundjid.resource - # else: - # return self.user + '@' + self.domain + '/' + self.resource - - class EcoVacsXMPP(ClientXMPP): def __init__(self, user, domain, resource, secret, continent, vacuum, server_address=None ): ClientXMPP.__init__(self, user + '@' + domain, '0/' + resource + '/' + secret) - self.user = user self.domain = domain self.resource = resource @@ -909,6 +894,7 @@ class EcoVacsXMPP(ClientXMPP): self.ctl_subscribers = [] self.ready_flag = Event() + def wait_until_ready(self): self.ready_flag.wait() @@ -979,6 +965,7 @@ class EcoVacsXMPP(ClientXMPP): q.send() def connect_and_wait_until_ready(self): + self.connect(self.server_address) self.process() self.wait_until_ready() @@ -1059,9 +1046,9 @@ class Stop(Clean): class SpotArea(Clean): def __init__(self, action='start', namedarea='', customarea='', cleanings='1'): if namedarea != '': #For cleaning specified map area - super().__init__('spotarea', 'normal', act=CLEAN_ACTION_TO_ECOVACS[action], mid=namedarea) + super().__init__('spot_area', 'normal', act=CLEAN_ACTION_TO_ECOVACS[action], mid=namedarea) elif customarea != '': #For cleaning custom map area, and specify deep amount 1x/2x - super().__init__('spotarea' ,'normal',act=CLEAN_ACTION_TO_ECOVACS[action], p=customarea, deep=cleanings) + super().__init__('spot_area' ,'normal',act=CLEAN_ACTION_TO_ECOVACS[action], p=customarea, deep=cleanings) else: #no valid entries raise ValueError("must provide namedarea or customarea for spotarea clean") diff --git a/sucks/cli.py b/sucks/cli.py index 6c26c2f..a241ddf 100644 --- a/sucks/cli.py +++ b/sucks/cli.py @@ -179,11 +179,10 @@ def edge(frequency, minutes): return CliAction(Edge(), wait=TimeWait(minutes * 60)) -@cli.command(help='spotcleans provided room(s) for the specified number of minutes') +@cli.command(help='spotcleans provided room(s)') @click.argument('room', type=click.STRING) -@click.argument('minutes', type=click.FLOAT) -def spotclean(room, minutes): - return CliAction(SpotArea('start', room), wait=TimeWait(minutes * 60)) +def spotclean(room): + return CliAction(SpotArea('start', room), wait=StatusWait('charge_status', 'returning')) @cli.command(help='returns to charger') def charge():