homeassistant-vorwerk/api.py

210 lines
6.3 KiB
Python
Raw Normal View History

2021-04-25 20:30:12 +02:00
"""Auth sessions for pybotvac."""
2021-04-27 11:12:23 +02:00
from __future__ import annotations
2021-04-25 20:30:12 +02:00
import logging
2021-04-28 17:14:09 +02:00
from typing import Any
2021-04-25 20:30:12 +02:00
import pybotvac
from pybotvac.exceptions import NeatoRobotException
from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
STATE_ERROR,
STATE_IDLE,
STATE_PAUSED,
STATE_RETURNING,
)
from .const import (
ACTION,
ALERTS,
ERRORS,
MODE,
ROBOT_ACTION_DOCKING,
ROBOT_STATE_BUSY,
ROBOT_STATE_ERROR,
ROBOT_STATE_IDLE,
ROBOT_STATE_PAUSE,
VORWERK_DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
class VorwerkSession(pybotvac.PasswordlessSession):
"""PasswordlessSession pybotvac session for Vorwerk cloud."""
# The client_id is the same for all users.
CLIENT_ID = "KY4YbVAvtgB7lp8vIbWQ7zLk3hssZlhR"
def __init__(self):
"""Initialize Vorwerk cloud session."""
super().__init__(client_id=VorwerkSession.CLIENT_ID, vendor=pybotvac.Vorwerk())
@property
def token(self):
"""Return the token dict. Contains id_token, access_token and refresh_token."""
return self._token
class VorwerkState:
"""Class to convert robot_state dict to more useful object."""
def __init__(self, robot: pybotvac.Robot) -> None:
"""Initialize new vorwerk vacuum state."""
self.robot = robot
2021-04-28 17:14:09 +02:00
self.robot_state: dict[Any, Any] = {}
self.robot_info: dict[Any, Any] = {}
2021-04-25 20:30:12 +02:00
2021-04-27 11:12:01 +02:00
@property
2021-04-25 20:30:12 +02:00
def available(self) -> bool:
"""Return true when robot state is available."""
return bool(self.robot_state)
def update(self):
"""Update robot state and robot info."""
_LOGGER.debug("Running Vorwerk Vacuums update for '%s'", self.robot.name)
self._update_robot_info()
self._update_state()
def _update_state(self):
try:
if self.robot_info is None:
self.robot_info = self.robot.get_general_info().json().get("data")
except NeatoRobotException:
_LOGGER.warning("Couldn't fetch robot information of %s", self.robot.name)
def _update_robot_info(self):
try:
self.robot_state = self.robot.state
except NeatoRobotException as ex:
if self.available: # print only once when available
_LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.robot.name, ex
)
self.robot_state = {}
return
@property
2021-04-28 17:14:09 +02:00
def docked(self) -> bool | None:
2021-04-25 20:30:12 +02:00
"""Vacuum is docked."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
return (
self.robot_state["state"] == ROBOT_STATE_IDLE
and self.robot_state["details"]["isDocked"]
)
@property
2021-04-28 17:14:09 +02:00
def charging(self) -> bool | None:
2021-04-25 20:30:12 +02:00
"""Vacuum is charging."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
return (
self.robot_state.get("state") == ROBOT_STATE_IDLE
and self.robot_state["details"]["isCharging"]
)
@property
2021-04-27 11:12:23 +02:00
def state(self) -> str | None:
2021-04-25 20:30:12 +02:00
"""Return Home Assistant vacuum state."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
robot_state = self.robot_state.get("state")
2021-04-27 11:12:23 +02:00
state = None
2021-04-25 20:30:12 +02:00
if self.charging or self.docked:
2021-04-27 11:12:23 +02:00
state = STATE_DOCKED
2021-04-25 20:30:12 +02:00
elif robot_state == ROBOT_STATE_IDLE:
2021-04-27 11:12:23 +02:00
state = STATE_IDLE
2021-04-25 20:30:12 +02:00
elif robot_state == ROBOT_STATE_BUSY:
if robot_state["action"] != ROBOT_ACTION_DOCKING:
2021-04-27 11:12:23 +02:00
state = STATE_RETURNING
2021-04-25 20:30:12 +02:00
else:
2021-04-27 11:12:23 +02:00
state = STATE_CLEANING
2021-04-25 20:30:12 +02:00
elif robot_state == ROBOT_STATE_PAUSE:
2021-04-27 11:12:23 +02:00
state = STATE_PAUSED
2021-04-25 20:30:12 +02:00
elif robot_state == ROBOT_STATE_ERROR:
2021-04-27 11:12:23 +02:00
state = STATE_ERROR
return state
2021-04-25 20:30:12 +02:00
@property
2021-04-27 11:12:23 +02:00
def alert(self) -> str | None:
2021-04-25 20:30:12 +02:00
"""Return vacuum alert message."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
if "alert" in self.robot_state:
return ALERTS.get(self.robot_state["alert"], self.robot_state["alert"])
return None
@property
2021-04-27 11:12:23 +02:00
def status(self) -> str | None:
2021-04-25 20:30:12 +02:00
"""Return vacuum status message."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
2021-04-28 17:14:09 +02:00
status = None
2021-04-25 20:30:12 +02:00
if self.state == STATE_ERROR:
status = self._error_status()
elif self.alert:
status = self.alert
elif self.state == STATE_DOCKED:
if self.charging:
status = "Charging"
if self.docked:
status = "Docked"
elif self.state == STATE_IDLE:
status = "Stopped"
elif self.state == STATE_CLEANING:
status = self._cleaning_status()
elif self.state == STATE_PAUSED:
status = "Paused"
return status
def _error_status(self):
"""Return error status."""
robot_state = self.robot_state.get("state")
return ERRORS.get(robot_state["error"], robot_state["error"])
def _cleaning_status(self):
"""Return cleaning status."""
robot_state = self.robot_state.get("state")
status_items = [
MODE.get(robot_state["cleaning"]["mode"]),
ACTION.get(robot_state["action"]),
]
if (
"boundary" in robot_state["cleaning"]
and "name" in robot_state["cleaning"]["boundary"]
):
status_items.append(robot_state["cleaning"]["boundary"]["name"])
return " ".join(s for s in status_items if s)
@property
2021-04-28 17:14:09 +02:00
def battery_level(self) -> str | None:
2021-04-25 20:30:12 +02:00
"""Return the battery level of the vacuum cleaner."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
return self.robot_state["details"]["charge"]
@property
2021-04-28 17:14:09 +02:00
def device_info(self) -> dict[str, str]:
2021-04-25 20:30:12 +02:00
"""Device info for robot."""
info = {
"identifiers": {(VORWERK_DOMAIN, self.robot.serial)},
"name": self.robot.name,
}
if self.robot_info:
info["manufacturer"] = self.robot_info["battery"]["vendor"]
info["model"] = self.robot_info["model"]
info["sw_version"] = self.robot_info["firmware"]
return info
@property
2021-04-27 11:12:23 +02:00
def schedule_enabled(self):
2021-04-25 20:30:12 +02:00
"""Return True when schedule is enabled."""
2021-04-28 17:14:09 +02:00
if not self.available:
return None
2021-04-25 20:30:12 +02:00
return bool(self.robot_state["details"]["isScheduleEnabled"])