-
-
Save Soltroy/b3ae9a48f4030e3d00fda22af52723d8 to your computer and use it in GitHub Desktop.
"""Support for Ecovacs Deebot vacuums.""" | |
import logging | |
import random | |
import string | |
import voluptuous as vol | |
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP | |
from homeassistant.helpers import discovery | |
import homeassistant.helpers.config_validation as cv | |
_LOGGER = logging.getLogger(__name__) | |
DOMAIN = "deebot" | |
CONF_COUNTRY = "country" | |
CONF_CONTINENT = "continent" | |
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), | |
} | |
) | |
}, | |
extra=vol.ALLOW_EXTRA, | |
) | |
ECOVACS_DEVICES = "ecovacs_devices" | |
# Generate a random device ID on each bootup | |
ECOVACS_API_DEVICEID = "".join( | |
random.choice(string.ascii_uppercase + string.digits) for _ in range(8) | |
) | |
def setup(hass, config): | |
"""Set up the Ecovacs component.""" | |
_LOGGER.debug("Creating new Ecovacs component") | |
hass.data[ECOVACS_DEVICES] = [] | |
from ozmo import EcoVacsAPI, VacBot | |
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), | |
) | |
devices = ecovacs_api.devices() | |
_LOGGER.debug("Ecobot devices: %s", devices) | |
for device in devices: | |
_LOGGER.info( | |
"Discovered Ecovacs device on account: %s with nickname %s", | |
device["did"], | |
device["nick"], | |
) | |
vacbot = VacBot( | |
ecovacs_api.uid, | |
ecovacs_api.REALM, | |
ecovacs_api.resource, | |
ecovacs_api.user_access_token, | |
device, | |
config[DOMAIN].get(CONF_CONTINENT).lower(), | |
monitor=True, | |
) | |
hass.data[ECOVACS_DEVICES].append(vacbot) | |
def stop(event: object) -> None: | |
"""Shut down open connections to Ecovacs XMPP server.""" | |
for device in hass.data[ECOVACS_DEVICES]: | |
_LOGGER.info( | |
"Shutting down connection to Ecovacs device %s", device.vacuum["did"] | |
) | |
device.disconnect() | |
# Listen for HA stop to disconnect. | |
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop) | |
if hass.data[ECOVACS_DEVICES]: | |
_LOGGER.debug("Starting vacuum components") | |
discovery.load_platform(hass, "vacuum", DOMAIN, {}, config) | |
return True |
#ecovacs: | |
deebot: | |
username: !secret Ecovacs_username | |
password: !secret Ecovacs_password | |
country: !secret Ecovacs_coutry | |
continent: !secret Ecovacs_continent |
{ | |
"domain": "deebot", | |
"name": "deebot", | |
"documentation": "https://www.home-assistant.io/components/deebot", | |
"requirements": [ | |
"ozmo==1.0" | |
], | |
"dependencies": [], | |
"codeowners": [ | |
"@OverloadUT" | |
] | |
} |
"""Support for Ecovacs Ecovacs Vaccums.""" | |
import logging | |
from homeassistant.components.vacuum import ( | |
SUPPORT_BATTERY, | |
SUPPORT_CLEAN_SPOT, | |
SUPPORT_FAN_SPEED, | |
SUPPORT_LOCATE, | |
SUPPORT_RETURN_HOME, | |
SUPPORT_SEND_COMMAND, | |
SUPPORT_STATUS, | |
SUPPORT_STOP, | |
SUPPORT_TURN_OFF, | |
SUPPORT_TURN_ON, | |
VacuumDevice, | |
) | |
from homeassistant.helpers.icon import icon_for_battery_level | |
from . import ECOVACS_DEVICES | |
_LOGGER = logging.getLogger(__name__) | |
SUPPORT_ECOVACS = ( | |
SUPPORT_BATTERY | |
| SUPPORT_RETURN_HOME | |
| SUPPORT_CLEAN_SPOT | |
| SUPPORT_STOP | |
| SUPPORT_TURN_OFF | |
| SUPPORT_TURN_ON | |
| SUPPORT_LOCATE | |
| SUPPORT_STATUS | |
| SUPPORT_SEND_COMMAND | |
| SUPPORT_FAN_SPEED | |
) | |
ATTR_ERROR = "error" | |
ATTR_COMPONENT_PREFIX = "component_" | |
def setup_platform(hass, config, add_entities, discovery_info=None): | |
"""Set up the Ecovacs vacuums.""" | |
vacuums = [] | |
for device in hass.data[ECOVACS_DEVICES]: | |
vacuums.append(EcovacsVacuum(device)) | |
_LOGGER.debug("Adding Ecovacs Vacuums to Hass: %s", vacuums) | |
add_entities(vacuums, True) | |
class EcovacsVacuum(VacuumDevice): | |
"""Ecovacs Vacuums such as Deebot.""" | |
def __init__(self, device): | |
"""Initialize the Ecovacs Vacuum.""" | |
self.device = device | |
self.device.connect_and_wait_until_ready() | |
if self.device.vacuum.get("nick", None) is not None: | |
self._name = "{}".format(self.device.vacuum["nick"]) | |
else: | |
# In case there is no nickname defined, use the device id | |
self._name = "{}".format(self.device.vacuum["did"]) | |
self._fan_speed = None | |
self._error = None | |
_LOGGER.debug("Vacuum initialized: %s", self.name) | |
async def async_added_to_hass(self) -> None: | |
"""Set up the event listeners now that hass is ready.""" | |
self.device.statusEvents.subscribe(lambda _: self.schedule_update_ha_state()) | |
self.device.batteryEvents.subscribe(lambda _: self.schedule_update_ha_state()) | |
self.device.lifespanEvents.subscribe(lambda _: self.schedule_update_ha_state()) | |
self.device.errorEvents.subscribe(self.on_error) | |
def on_error(self, error): | |
"""Handle an error event from the robot. | |
This will not change the entity's state. If the error caused the state | |
to change, that will come through as a separate on_status event | |
""" | |
if error == "no_error": | |
self._error = None | |
else: | |
self._error = error | |
self.hass.bus.fire( | |
"ecovacs_error", {"entity_id": self.entity_id, "error": error} | |
) | |
self.schedule_update_ha_state() | |
@property | |
def should_poll(self) -> bool: | |
"""Return True if entity has to be polled for state.""" | |
return False | |
@property | |
def unique_id(self) -> str: | |
"""Return an unique ID.""" | |
return self.device.vacuum.get("did", None) | |
@property | |
def is_on(self): | |
"""Return true if vacuum is currently cleaning.""" | |
return self.device.is_cleaning | |
@property | |
def is_charging(self): | |
"""Return true if vacuum is currently charging.""" | |
return self.device.is_charging | |
@property | |
def name(self): | |
"""Return the name of the device.""" | |
return self._name | |
@property | |
def supported_features(self): | |
"""Flag vacuum cleaner robot features that are supported.""" | |
return SUPPORT_ECOVACS | |
@property | |
def status(self): | |
"""Return the status of the vacuum cleaner.""" | |
return self.device.vacuum_status | |
def return_to_base(self, **kwargs): | |
"""Set the vacuum cleaner to return to the dock.""" | |
from ozmo import Charge | |
self.device.run(Charge()) | |
@property | |
def battery_icon(self): | |
"""Return the battery icon for the vacuum cleaner.""" | |
return icon_for_battery_level( | |
battery_level=self.battery_level, charging=self.is_charging | |
) | |
@property | |
def battery_level(self): | |
"""Return the battery level of the vacuum cleaner.""" | |
if self.device.battery_status is not None: | |
return self.device.battery_status * 100 | |
return super().battery_level | |
@property | |
def fan_speed(self): | |
"""Return the fan speed of the vacuum cleaner.""" | |
return self.device.fan_speed | |
@property | |
def fan_speed_list(self): | |
"""Get the list of available fan speed steps of the vacuum cleaner.""" | |
from ozmo import FAN_SPEED_NORMAL, FAN_SPEED_HIGH | |
return [FAN_SPEED_NORMAL, FAN_SPEED_HIGH] | |
def turn_on(self, **kwargs): | |
"""Turn the vacuum on and start cleaning.""" | |
from ozmo import Clean | |
self.device.run(Clean()) | |
def turn_off(self, **kwargs): | |
"""Turn the vacuum off stopping the cleaning and returning home.""" | |
self.return_to_base() | |
def stop(self, **kwargs): | |
"""Stop the vacuum cleaner.""" | |
from ozmo import Stop | |
self.device.run(Stop()) | |
def clean_spot(self, **kwargs): | |
"""Perform a spot clean-up.""" | |
from ozmo import Spot | |
self.device.run(Spot()) | |
def locate(self, **kwargs): | |
"""Locate the vacuum cleaner.""" | |
from ozmo import PlaySound | |
self.device.run(PlaySound()) | |
def set_fan_speed(self, fan_speed, **kwargs): | |
"""Set fan speed.""" | |
if self.is_on: | |
from ozmo import Clean | |
self.device.run(Clean(mode=self.device.clean_status, speed=fan_speed)) | |
def send_command(self, command, params=None, **kwargs): | |
"""Send a command to a vacuum cleaner.""" | |
from ozmo import VacBotCommand | |
self.device.run(VacBotCommand(command, params)) | |
@property | |
def device_state_attributes(self): | |
"""Return the device-specific state attributes of this vacuum.""" | |
data = {} | |
data[ATTR_ERROR] = self._error | |
for key, val in self.device.components.items(): | |
attr_name = ATTR_COMPONENT_PREFIX + key | |
data[attr_name] = int(val * 100) | |
return data |
Missing the s in your folder: custom_components
# ls custom_components/deebot/
__init__.py __pycache__ manifest.json vacuum.py```
Hey! This doesn't seem to be working anymore. Is it still working for you?
I'm using it every day :)
Sorry - haven't used it for 2 Months ....
Sorry - haven't used it for 2 Months ....
What are you using instead?
Hi, for I unknown reason my robot did not update his status until I reboot my home assistant... could it be doable to replace a command ( let's say "STOP" ) with a "refresh" button something that could pull the info or even log out the Ecovacs server and then reload it with a renew info ? so at least I would be able to force a status update
Sorry - haven't used it for 2 Months ....
What are you using instead?
I haven't used Automation via HomeAssistant - ATM the vacuum is controlled with the native app.
Yes, this sucks but currently I can't free up time to fix my HA setup ;-(
Addendum: nothings really broken, I just did every HA update since almost a year without doing anything against breaking changes and therefore many things do not work at the moment
Cool, working perfect with my deebot 605. Any tip on how to add filters status and send commands (return to dock, start cleaning) in an automation?
@Soltroy I updated __init__.py
because isAlive and getchildren are deprecated in Python 3.9
Have you keeping updated the Deebot custom components? My 605 is only working with this...
At the moment with these little changes I keep on working, but I'm not sure I'll alble to do it if something more complicated is needed...
Thank you so much...
@Soltroy I updated
__init__.py
because isAlive and getchildren are deprecated in Python 3.9
Have you keeping updated the Deebot custom components? My 605 is only working with this...
At the moment with these little changes I keep on working, but I'm not sure I'll alble to do it if something more complicated is needed...
Thank you so much...
@vannetta how did u change it?
@sunsgs go to the path you find in the log. It should be something like /usr/bin/local/python3.9/size-packages/ozmo/__init__.py
My logs said that the line 700 with isAlive was desprecated and line 800 with getchildren.
You have to change isAlive with is_alive and erase getchildren
You have to enter in HassOS to reach this path
@vannetta, got it. how did u access to that folder?
I've tried to access trough ssh but no luck to find /usr/bin/local/python3.9/size-packages/ozmo/init.py.
in bin folder there is no local folder
@sunsgs I don't rimember the right path; it is shown in the log error. In any case you must access via 22222 port because you need to work on the OS
Hi all. I'm trying the deebot integration. I'm on Windows installation.
I copied the files into: C:\Users--username--\AppData\Roaming.homeassistant\custom_component\deebot
I get the following error in the log:
2020-03-26 19:04:59 ERROR (MainThread) [homeassistant.setup] Setup failed for deebot: Integration not found.