homeassistant-vorwerk/vacuum.py
2021-04-25 20:30:12 +02:00

276 lines
8.4 KiB
Python

"""Support for Neato Connected Vacuums."""
import logging
from typing import Any
from pybotvac.exceptions import NeatoRobotException
from pybotvac.robot import Robot
import voluptuous as vol
from homeassistant.components.vacuum import (
ATTR_STATUS,
STATE_CLEANING,
STATE_IDLE,
STATE_PAUSED,
SUPPORT_BATTERY,
SUPPORT_CLEAN_SPOT,
SUPPORT_LOCATE,
SUPPORT_PAUSE,
SUPPORT_RETURN_HOME,
SUPPORT_START,
SUPPORT_STATE,
SUPPORT_STOP,
StateVacuumEntity,
)
from homeassistant.const import ATTR_MODE
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .api import VorwerkState
from .const import (
ATTR_CATEGORY,
ATTR_CLEAN_AREA,
ATTR_CLEAN_BATTERY_END,
ATTR_CLEAN_BATTERY_START,
ATTR_CLEAN_ERROR_TIME,
ATTR_CLEAN_PAUSE_TIME,
ATTR_CLEAN_START,
ATTR_CLEAN_STOP,
ATTR_CLEAN_SUSP_COUNT,
ATTR_CLEAN_SUSP_TIME,
ATTR_LAUNCHED_FROM,
ATTR_NAVIGATION,
ATTR_ZONE,
VORWERK_DOMAIN,
VORWERK_ROBOT_API,
VORWERK_ROBOT_COORDINATOR,
VORWERK_ROBOTS,
)
_LOGGER = logging.getLogger(__name__)
SUPPORT_VORWERK = (
SUPPORT_BATTERY
| SUPPORT_PAUSE
| SUPPORT_RETURN_HOME
| SUPPORT_STOP
| SUPPORT_START
| SUPPORT_CLEAN_SPOT
| SUPPORT_STATE
| SUPPORT_LOCATE
)
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up Vorwerk vacuum with config entry."""
_LOGGER.debug("Adding vorwerk vacuums")
async_add_entities(
[
VorwerkConnectedVacuum(
robot[VORWERK_ROBOT_API], robot[VORWERK_ROBOT_COORDINATOR]
)
for robot in hass.data[VORWERK_DOMAIN][entry.entry_id][VORWERK_ROBOTS]
],
True,
)
platform = entity_platform.current_platform.get()
assert platform is not None
platform.async_register_entity_service(
"custom_cleaning",
{
vol.Optional(ATTR_MODE, default=2): cv.positive_int,
vol.Optional(ATTR_NAVIGATION, default=1): cv.positive_int,
vol.Optional(ATTR_CATEGORY, default=4): cv.positive_int,
vol.Optional(ATTR_ZONE): cv.string,
},
"vorwerk_custom_cleaning",
)
class VorwerkConnectedVacuum(CoordinatorEntity, StateVacuumEntity):
"""Representation of a Vorwerk Connected Vacuum."""
def __init__(
self, robot_state: VorwerkState, coordinator: DataUpdateCoordinator[Any]
) -> None:
"""Initialize the Vorwerk Connected Vacuum."""
super().__init__(coordinator)
self.robot: Robot = robot_state.robot
self._state: VorwerkState = robot_state
self._name = f"{self.robot.name}"
self._robot_serial = self.robot.serial
# Variables form neato impl
# We keep it here for later implementations
self._clean_state = None
self._clean_time_start = None
self._clean_time_stop = None
self._clean_area = None
self._clean_battery_start = None
self._clean_battery_end = None
self._clean_susp_charge_count = None
self._clean_susp_time = None
self._clean_pause_time = None
self._clean_error_time = None
self._launched_from = None
self._robot_boundaries = []
@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_VORWERK
@property
def battery_level(self):
"""Return the battery level of the vacuum cleaner."""
return self._state.battery_level
@property
def available(self):
"""Return if the robot is available."""
return self._state.available
@property
def icon(self):
"""Return specific icon."""
return "mdi:robot-vacuum-variant"
@property
def state(self):
"""Return the status of the vacuum cleaner."""
return self._state.state if self._state else None
@property
def unique_id(self):
"""Return a unique ID."""
return self._robot_serial
@property
def device_state_attributes(self):
"""Return the state attributes of the vacuum cleaner."""
data = {}
if self._state.status is not None:
data[ATTR_STATUS] = self._state.status
if self._clean_time_start is not None:
data[ATTR_CLEAN_START] = self._clean_time_start
if self._clean_time_stop is not None:
data[ATTR_CLEAN_STOP] = self._clean_time_stop
if self._clean_area is not None:
data[ATTR_CLEAN_AREA] = self._clean_area
if self._clean_susp_charge_count is not None:
data[ATTR_CLEAN_SUSP_COUNT] = self._clean_susp_charge_count
if self._clean_susp_time is not None:
data[ATTR_CLEAN_SUSP_TIME] = self._clean_susp_time
if self._clean_pause_time is not None:
data[ATTR_CLEAN_PAUSE_TIME] = self._clean_pause_time
if self._clean_error_time is not None:
data[ATTR_CLEAN_ERROR_TIME] = self._clean_error_time
if self._clean_battery_start is not None:
data[ATTR_CLEAN_BATTERY_START] = self._clean_battery_start
if self._clean_battery_end is not None:
data[ATTR_CLEAN_BATTERY_END] = self._clean_battery_end
if self._launched_from is not None:
data[ATTR_LAUNCHED_FROM] = self._launched_from
return data
@property
def device_info(self):
"""Device info for robot."""
return self._state.device_info
def start(self):
"""Start cleaning or resume cleaning."""
if not self._state:
return
try:
if self._state.state == STATE_IDLE:
self.robot.start_cleaning()
elif self._state.state == STATE_PAUSED:
self.robot.resume_cleaning()
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)
def pause(self):
"""Pause the vacuum."""
try:
self.robot.pause_cleaning()
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)
def return_to_base(self, **kwargs):
"""Set the vacuum cleaner to return to the dock."""
try:
if self._state.state == STATE_CLEANING:
self.robot.pause_cleaning()
self.robot.send_to_base()
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)
def stop(self, **kwargs):
"""Stop the vacuum cleaner."""
try:
self.robot.stop_cleaning()
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)
def locate(self, **kwargs):
"""Locate the robot by making it emit a sound."""
try:
self.robot.locate()
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)
def clean_spot(self, **kwargs):
"""Run a spot cleaning starting from the base."""
try:
self.robot.start_spot_cleaning()
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)
def vorwerk_custom_cleaning(self, mode, navigation, category, zone=None):
"""Zone cleaning service call."""
boundary_id = None
if zone is not None:
for boundary in self._robot_boundaries:
if zone in boundary["name"]:
boundary_id = boundary["id"]
if boundary_id is None:
_LOGGER.error(
"Zone '%s' was not found for the robot '%s'", zone, self.entity_id
)
return
try:
self.robot.start_cleaning(mode, navigation, category, boundary_id)
except NeatoRobotException as ex:
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex
)