1 """Support for Neato Connected Vacuums."""
3 from __future__
import annotations
5 from datetime
import timedelta
9 from pybotvac
import Robot
10 from pybotvac.exceptions
import NeatoRobotException
11 import voluptuous
as vol
36 NEATO_PERSISTENT_MAPS,
38 SCAN_INTERVAL_MINUTES,
40 from .entity
import NeatoEntity
41 from .hub
import NeatoHub
43 _LOGGER = logging.getLogger(__name__)
45 SCAN_INTERVAL =
timedelta(minutes=SCAN_INTERVAL_MINUTES)
47 ATTR_CLEAN_START =
"clean_start"
48 ATTR_CLEAN_STOP =
"clean_stop"
49 ATTR_CLEAN_AREA =
"clean_area"
50 ATTR_CLEAN_BATTERY_START =
"battery_level_at_clean_start"
51 ATTR_CLEAN_BATTERY_END =
"battery_level_at_clean_end"
52 ATTR_CLEAN_SUSP_COUNT =
"clean_suspension_count"
53 ATTR_CLEAN_SUSP_TIME =
"clean_suspension_time"
54 ATTR_CLEAN_PAUSE_TIME =
"clean_pause_time"
55 ATTR_CLEAN_ERROR_TIME =
"clean_error_time"
56 ATTR_LAUNCHED_FROM =
"launched_from"
58 ATTR_NAVIGATION =
"navigation"
59 ATTR_CATEGORY =
"category"
64 hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
66 """Set up Neato vacuum with config entry."""
67 neato: NeatoHub = hass.data[NEATO_LOGIN]
68 mapdata: dict[str, Any] |
None = hass.data.get(NEATO_MAP_DATA)
69 persistent_maps: dict[str, Any] |
None = hass.data.get(NEATO_PERSISTENT_MAPS)
72 for robot
in hass.data[NEATO_ROBOTS]
78 _LOGGER.debug(
"Adding vacuums %s", dev)
81 platform = entity_platform.async_get_current_platform()
82 assert platform
is not None
84 platform.async_register_entity_service(
87 vol.Optional(ATTR_MODE, default=2): cv.positive_int,
88 vol.Optional(ATTR_NAVIGATION, default=1): cv.positive_int,
89 vol.Optional(ATTR_CATEGORY, default=4): cv.positive_int,
90 vol.Optional(ATTR_ZONE): cv.string,
92 "neato_custom_cleaning",
97 """Representation of a Neato Connected Vacuum."""
99 _attr_supported_features = (
100 VacuumEntityFeature.BATTERY
101 | VacuumEntityFeature.PAUSE
102 | VacuumEntityFeature.RETURN_HOME
103 | VacuumEntityFeature.STOP
104 | VacuumEntityFeature.START
105 | VacuumEntityFeature.CLEAN_SPOT
106 | VacuumEntityFeature.STATE
107 | VacuumEntityFeature.MAP
108 | VacuumEntityFeature.LOCATE
116 mapdata: dict[str, Any] |
None,
117 persistent_maps: dict[str, Any] |
None,
119 """Initialize the Neato Connected Vacuum."""
123 self._robot_has_map: bool = self.
robotrobot.has_persistent_maps
125 self._robot_serial: str = self.
robotrobot.serial
126 self._attr_unique_id: str = self.
robotrobot.serial
128 self.
_state_state: dict[str, Any] |
None =
None
140 self.
_robot_stats_robot_stats: dict[str, Any] |
None =
None
143 """Update the states of Neato Vacuums."""
144 _LOGGER.debug(
"Running Neato Vacuums update for '%s'", self.
entity_identity_id)
148 except NeatoRobotException:
149 _LOGGER.warning(
"Couldn't fetch robot information of %s", self.
entity_identity_id)
153 except NeatoRobotException
as ex:
156 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
162 if self.
_state_state
is None:
165 _LOGGER.debug(
"self._state=%s", self.
_state_state)
166 if "alert" in self.
_state_state:
167 robot_alert = ALERTS.get(self.
_state_state[
"alert"])
170 if self.
_state_state[
"state"] == 1:
171 if self.
_state_state[
"details"][
"isCharging"]:
175 self.
_state_state[
"details"][
"isDocked"]
176 and not self.
_state_state[
"details"][
"isCharging"]
184 if robot_alert
is not None:
186 elif self.
_state_state[
"state"] == 2:
187 if robot_alert
is None:
190 f
"{MODE.get(self._state['cleaning']['mode'])} "
191 f
"{ACTION.get(self._state['action'])}"
194 "boundary" in self.
_state_state[
"cleaning"]
195 and "name" in self.
_state_state[
"cleaning"][
"boundary"]
198 f
" {self._state['cleaning']['boundary']['name']}"
202 elif self.
_state_state[
"state"] == 3:
205 elif self.
_state_state[
"state"] == 4:
216 mapdata: dict[str, Any] = self.
_mapdata_mapdata[self._robot_serial][
"maps"][0]
231 and self.
_state_state[
"availableServices"][
"maps"] !=
"basic-1"
234 allmaps: dict = self.
_robot_maps_robot_maps[self._robot_serial]
236 "Found the following maps for '%s': %s", self.
entity_identity_id, allmaps
241 robot_boundaries = self.
robotrobot.get_map_boundaries(maps[
"id"]).
json()
242 except NeatoRobotException
as ex:
244 "Could not fetch map boundaries for '%s': %s",
251 "Boundaries for robot '%s' in map '%s': %s",
256 if "boundaries" in robot_boundaries[
"data"]:
257 self.
_robot_boundaries_robot_boundaries += robot_boundaries[
"data"][
"boundaries"]
259 "List of boundaries for '%s': %s",
266 """Return the state attributes of the vacuum cleaner."""
267 data: dict[str, Any] = {}
276 data[ATTR_CLEAN_AREA] = self.
_clean_area_clean_area
296 """Device info for neato robot."""
297 device_info = self._attr_device_info
299 device_info[
"manufacturer"] = self.
_robot_stats_robot_stats[
"battery"][
"vendor"]
300 device_info[
"model"] = self.
_robot_stats_robot_stats[
"model"]
301 device_info[
"sw_version"] = self.
_robot_stats_robot_stats[
"firmware"]
305 """Start cleaning or resume cleaning."""
308 if self.
_state_state[
"state"] == 1:
309 self.
robotrobot.start_cleaning()
310 elif self.
_state_state[
"state"] == 3:
311 self.
robotrobot.resume_cleaning()
312 except NeatoRobotException
as ex:
314 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
318 """Pause the vacuum."""
320 self.
robotrobot.pause_cleaning()
321 except NeatoRobotException
as ex:
323 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
327 """Set the vacuum cleaner to return to the dock."""
330 self.
robotrobot.pause_cleaning()
332 self.
robotrobot.send_to_base()
333 except NeatoRobotException
as ex:
335 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
338 def stop(self, **kwargs: Any) ->
None:
339 """Stop the vacuum cleaner."""
341 self.
robotrobot.stop_cleaning()
342 except NeatoRobotException
as ex:
344 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
348 """Locate the robot by making it emit a sound."""
351 except NeatoRobotException
as ex:
353 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
357 """Run a spot cleaning starting from the base."""
359 self.
robotrobot.start_spot_cleaning()
360 except NeatoRobotException
as ex:
362 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
366 self, mode: str, navigation: str, category: str, zone: str |
None =
None
368 """Zone cleaning service call."""
372 if zone
in boundary[
"name"]:
373 boundary_id = boundary[
"id"]
374 if boundary_id
is None:
376 "Zone '%s' was not found for the robot '%s'", zone, self.
entity_identity_id
380 "Start cleaning zone '%s' with robot %s", zone, self.
entity_identity_id
385 self.
robotrobot.start_cleaning(mode, navigation, category, boundary_id)
386 except NeatoRobotException
as ex:
388 "Neato vacuum connection error for '%s': %s", self.
entity_identity_id, ex
DeviceInfo device_info(self)
dict[str, Any] extra_state_attributes(self)
None clean_spot(self, **Any kwargs)
None locate(self, **Any kwargs)
None return_to_base(self, **Any kwargs)
None __init__(self, NeatoHub neato, Robot robot, dict[str, Any]|None mapdata, dict[str, Any]|None persistent_maps)
None stop(self, **Any kwargs)
None neato_custom_cleaning(self, str mode, str navigation, str category, str|None zone=None)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)