mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 01:07:10 +00:00
Add custom and zone cleaning to Neato Vacuums (#20779)
* Adding custom and zone cleaning to Neato Vacuums * Fixing line length and missing imports * Line too long * Adding details to the custom service * Fix linting issues * Reverting ACTION * Code cleanup * Typo * Requested modifications * Changing the custom service domain * No service schema depency anymore * Removing useless code * Linting * Requested changes * Requested changes for domain * Revert the service domain back to vacuum
This commit is contained in:
parent
dc5b8fd8c4
commit
a8a2daeac5
@ -18,6 +18,7 @@ DOMAIN = 'neato'
|
|||||||
NEATO_ROBOTS = 'neato_robots'
|
NEATO_ROBOTS = 'neato_robots'
|
||||||
NEATO_LOGIN = 'neato_login'
|
NEATO_LOGIN = 'neato_login'
|
||||||
NEATO_MAP_DATA = 'neato_map_data'
|
NEATO_MAP_DATA = 'neato_map_data'
|
||||||
|
NEATO_PERSISTENT_MAPS = 'neato_persistent_maps'
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
DOMAIN: vol.Schema({
|
DOMAIN: vol.Schema({
|
||||||
@ -197,6 +198,7 @@ class NeatoHub:
|
|||||||
domain_config[CONF_USERNAME],
|
domain_config[CONF_USERNAME],
|
||||||
domain_config[CONF_PASSWORD])
|
domain_config[CONF_PASSWORD])
|
||||||
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
|
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
|
||||||
|
self._hass.data[NEATO_PERSISTENT_MAPS] = self.my_neato.persistent_maps
|
||||||
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
|
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
@ -216,6 +218,7 @@ class NeatoHub:
|
|||||||
_LOGGER.debug("Running HUB.update_robots %s",
|
_LOGGER.debug("Running HUB.update_robots %s",
|
||||||
self._hass.data[NEATO_ROBOTS])
|
self._hass.data[NEATO_ROBOTS])
|
||||||
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
|
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
|
||||||
|
self._hass.data[NEATO_PERSISTENT_MAPS] = self.my_neato.persistent_maps
|
||||||
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
|
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
|
||||||
|
|
||||||
def download_map(self, url):
|
def download_map(self, url):
|
||||||
|
@ -2,15 +2,21 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import requests
|
import requests
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import (ATTR_ENTITY_ID)
|
||||||
from homeassistant.components.vacuum import (
|
from homeassistant.components.vacuum import (
|
||||||
StateVacuumDevice, SUPPORT_BATTERY, SUPPORT_PAUSE, SUPPORT_RETURN_HOME,
|
StateVacuumDevice, SUPPORT_BATTERY, SUPPORT_PAUSE, SUPPORT_RETURN_HOME,
|
||||||
SUPPORT_STATE, SUPPORT_STOP, SUPPORT_START, STATE_IDLE,
|
SUPPORT_STATE, SUPPORT_STOP, SUPPORT_START, STATE_IDLE,
|
||||||
STATE_PAUSED, STATE_CLEANING, STATE_DOCKED, STATE_RETURNING, STATE_ERROR,
|
STATE_PAUSED, STATE_CLEANING, STATE_DOCKED, STATE_RETURNING, STATE_ERROR,
|
||||||
SUPPORT_MAP, ATTR_STATUS, ATTR_BATTERY_LEVEL, ATTR_BATTERY_ICON,
|
SUPPORT_MAP, ATTR_STATUS, ATTR_BATTERY_LEVEL, ATTR_BATTERY_ICON,
|
||||||
SUPPORT_LOCATE, SUPPORT_CLEAN_SPOT)
|
SUPPORT_LOCATE, SUPPORT_CLEAN_SPOT, DOMAIN)
|
||||||
from homeassistant.components.neato import (
|
from homeassistant.components.neato import (
|
||||||
NEATO_ROBOTS, NEATO_LOGIN, NEATO_MAP_DATA, ACTION, ERRORS, MODE, ALERTS)
|
NEATO_ROBOTS, NEATO_LOGIN, NEATO_MAP_DATA, ACTION, ERRORS, MODE, ALERTS,
|
||||||
|
NEATO_PERSISTENT_MAPS)
|
||||||
|
|
||||||
|
from homeassistant.helpers.service import extract_entity_ids
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -19,8 +25,8 @@ DEPENDENCIES = ['neato']
|
|||||||
SCAN_INTERVAL = timedelta(minutes=5)
|
SCAN_INTERVAL = timedelta(minutes=5)
|
||||||
|
|
||||||
SUPPORT_NEATO = SUPPORT_BATTERY | SUPPORT_PAUSE | SUPPORT_RETURN_HOME | \
|
SUPPORT_NEATO = SUPPORT_BATTERY | SUPPORT_PAUSE | SUPPORT_RETURN_HOME | \
|
||||||
SUPPORT_STOP | SUPPORT_START | SUPPORT_CLEAN_SPOT | \
|
SUPPORT_STOP | SUPPORT_START | SUPPORT_CLEAN_SPOT | \
|
||||||
SUPPORT_STATE | SUPPORT_MAP | SUPPORT_LOCATE
|
SUPPORT_STATE | SUPPORT_MAP | SUPPORT_LOCATE
|
||||||
|
|
||||||
ATTR_CLEAN_START = 'clean_start'
|
ATTR_CLEAN_START = 'clean_start'
|
||||||
ATTR_CLEAN_STOP = 'clean_stop'
|
ATTR_CLEAN_STOP = 'clean_stop'
|
||||||
@ -30,15 +36,56 @@ ATTR_CLEAN_BATTERY_END = 'battery_level_at_clean_end'
|
|||||||
ATTR_CLEAN_SUSP_COUNT = 'clean_suspension_count'
|
ATTR_CLEAN_SUSP_COUNT = 'clean_suspension_count'
|
||||||
ATTR_CLEAN_SUSP_TIME = 'clean_suspension_time'
|
ATTR_CLEAN_SUSP_TIME = 'clean_suspension_time'
|
||||||
|
|
||||||
|
ATTR_MODE = 'mode'
|
||||||
|
ATTR_NAVIGATION = 'navigation'
|
||||||
|
ATTR_CATEGORY = 'category'
|
||||||
|
ATTR_ZONE = 'zone'
|
||||||
|
|
||||||
|
SERVICE_NEATO_CUSTOM_CLEANING = 'neato_custom_cleaning'
|
||||||
|
|
||||||
|
SERVICE_NEATO_CUSTOM_CLEANING_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the Neato vacuum."""
|
"""Set up the Neato vacuum."""
|
||||||
dev = []
|
dev = []
|
||||||
for robot in hass.data[NEATO_ROBOTS]:
|
for robot in hass.data[NEATO_ROBOTS]:
|
||||||
dev.append(NeatoConnectedVacuum(hass, robot))
|
dev.append(NeatoConnectedVacuum(hass, robot))
|
||||||
|
|
||||||
|
if not dev:
|
||||||
|
return
|
||||||
|
|
||||||
_LOGGER.debug("Adding vacuums %s", dev)
|
_LOGGER.debug("Adding vacuums %s", dev)
|
||||||
add_entities(dev, True)
|
add_entities(dev, True)
|
||||||
|
|
||||||
|
def neato_custom_cleaning_service(call):
|
||||||
|
"""Zone cleaning service that allows user to change options."""
|
||||||
|
for robot in service_to_entities(call):
|
||||||
|
if call.service == SERVICE_NEATO_CUSTOM_CLEANING:
|
||||||
|
mode = call.data.get(ATTR_MODE)
|
||||||
|
navigation = call.data.get(ATTR_NAVIGATION)
|
||||||
|
category = call.data.get(ATTR_CATEGORY)
|
||||||
|
zone = call.data.get(ATTR_ZONE)
|
||||||
|
robot.neato_custom_cleaning(
|
||||||
|
mode, navigation, category, zone)
|
||||||
|
|
||||||
|
def service_to_entities(call):
|
||||||
|
"""Return the known devices that a service call mentions."""
|
||||||
|
entity_ids = extract_entity_ids(hass, call)
|
||||||
|
entities = [entity for entity in dev
|
||||||
|
if entity.entity_id in entity_ids]
|
||||||
|
return entities
|
||||||
|
|
||||||
|
hass.services.register(DOMAIN, SERVICE_NEATO_CUSTOM_CLEANING,
|
||||||
|
neato_custom_cleaning_service,
|
||||||
|
schema=SERVICE_NEATO_CUSTOM_CLEANING_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
class NeatoConnectedVacuum(StateVacuumDevice):
|
class NeatoConnectedVacuum(StateVacuumDevice):
|
||||||
"""Representation of a Neato Connected Vacuum."""
|
"""Representation of a Neato Connected Vacuum."""
|
||||||
@ -62,6 +109,9 @@ class NeatoConnectedVacuum(StateVacuumDevice):
|
|||||||
self._available = False
|
self._available = False
|
||||||
self._battery_level = None
|
self._battery_level = None
|
||||||
self._robot_serial = self.robot.serial
|
self._robot_serial = self.robot.serial
|
||||||
|
self._robot_maps = hass.data[NEATO_PERSISTENT_MAPS]
|
||||||
|
self._robot_boundaries = {}
|
||||||
|
self._robot_has_map = self.robot.has_persistent_maps
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the states of Neato Vacuums."""
|
"""Update the states of Neato Vacuums."""
|
||||||
@ -129,12 +179,18 @@ class NeatoConnectedVacuum(StateVacuumDevice):
|
|||||||
['time_in_suspended_cleaning'])
|
['time_in_suspended_cleaning'])
|
||||||
self.clean_battery_start = (
|
self.clean_battery_start = (
|
||||||
self._mapdata[self._robot_serial]['maps'][0]['run_charge_at_start']
|
self._mapdata[self._robot_serial]['maps'][0]['run_charge_at_start']
|
||||||
)
|
)
|
||||||
self.clean_battery_end = (
|
self.clean_battery_end = (
|
||||||
self._mapdata[self._robot_serial]['maps'][0]['run_charge_at_end'])
|
self._mapdata[self._robot_serial]['maps'][0]['run_charge_at_end'])
|
||||||
|
|
||||||
self._battery_level = self._state['details']['charge']
|
self._battery_level = self._state['details']['charge']
|
||||||
|
|
||||||
|
if self._robot_has_map:
|
||||||
|
robot_map_id = self._robot_maps[self._robot_serial][0]['id']
|
||||||
|
|
||||||
|
self._robot_boundaries = self.robot.get_map_boundaries(
|
||||||
|
robot_map_id).json()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the device."""
|
"""Return the name of the device."""
|
||||||
@ -224,3 +280,20 @@ class NeatoConnectedVacuum(StateVacuumDevice):
|
|||||||
def clean_spot(self, **kwargs):
|
def clean_spot(self, **kwargs):
|
||||||
"""Run a spot cleaning starting from the base."""
|
"""Run a spot cleaning starting from the base."""
|
||||||
self.robot.start_spot_cleaning()
|
self.robot.start_spot_cleaning()
|
||||||
|
|
||||||
|
def neato_custom_cleaning(self, mode, navigation, category,
|
||||||
|
zone=None, **kwargs):
|
||||||
|
"""Zone cleaning service call."""
|
||||||
|
boundary_id = None
|
||||||
|
if zone is not None:
|
||||||
|
for boundary in self._robot_boundaries['data']['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._name)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._clean_state = STATE_CLEANING
|
||||||
|
self.robot.start_cleaning(mode, navigation, category, boundary_id)
|
||||||
|
@ -144,3 +144,22 @@ xiaomi_clean_zone:
|
|||||||
repeats:
|
repeats:
|
||||||
description: Number of cleaning repeats for each zone between 1 and 3.
|
description: Number of cleaning repeats for each zone between 1 and 3.
|
||||||
example: '1'
|
example: '1'
|
||||||
|
|
||||||
|
neato_custom_cleaning:
|
||||||
|
description: Zone Cleaning service call specific to Neato Botvacs.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name of the vacuum entity. [Required]
|
||||||
|
example: 'vacuum.neato'
|
||||||
|
mode:
|
||||||
|
description: "Set the cleaning mode: 1 for eco and 2 for turbo. Defaults to turbo if not set."
|
||||||
|
example: 2
|
||||||
|
navigation:
|
||||||
|
description: "Set the navigation mode: 1 for normal, 2 for extra care, 3 for deep. Defaults to normal if not set."
|
||||||
|
example: 1
|
||||||
|
category:
|
||||||
|
description: "Whether to use a persistent map or not for cleaning (i.e. No go lines): 2 for no map, 4 for map. Default to using map if not set (and fallback to no map if no map is found)."
|
||||||
|
example: 2
|
||||||
|
zone:
|
||||||
|
description: Only supported on the Botvac D7. Name of the zone to clean. Defaults to no zone i.e. complete house cleanup.
|
||||||
|
example: "Kitchen"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user