Compare commits

..

No commits in common. "main" and "v0.9.5" have entirely different histories.
main ... v0.9.5

8 changed files with 25 additions and 87 deletions

View File

@ -8,7 +8,6 @@ from typing import Any
from pybotvac.exceptions import NeatoException, NeatoRobotException from pybotvac.exceptions import NeatoException, NeatoRobotException
from pybotvac.robot import Robot from pybotvac.robot import Robot
from pybotvac.vorwerk import Vorwerk from pybotvac.vorwerk import Vorwerk
from pybotvac.session import PasswordlessSession
import voluptuous as vol import voluptuous as vol
from homeassistant.components.vacuum import ( from homeassistant.components.vacuum import (
@ -25,7 +24,6 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.typing import ConfigType, HomeAssistantType from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.const import CONF_TOKEN
from .const import ( from .const import (
ACTION, ACTION,
@ -99,7 +97,6 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
{ {
VORWERK_ROBOT_API: r, VORWERK_ROBOT_API: r,
VORWERK_ROBOT_COORDINATOR: _create_coordinator(hass, r), VORWERK_ROBOT_COORDINATOR: _create_coordinator(hass, r),
CONF_TOKEN: entry.data[CONF_TOKEN]
} }
for r in robot_states for r in robot_states
] ]

View File

@ -23,7 +23,6 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
ATTR_NAVIGATION = "navigation" ATTR_NAVIGATION = "navigation"
ATTR_CATEGORY = "category" ATTR_CATEGORY = "category"
ATTR_ZONE = "zone" ATTR_ZONE = "zone"
ATTR_MAP = "map"
ROBOT_STATE_INVALID = 0 ROBOT_STATE_INVALID = 0
ROBOT_STATE_IDLE = 1 ROBOT_STATE_IDLE = 1

View File

@ -2,17 +2,16 @@
"domain": "vorwerk", "domain": "vorwerk",
"name": "Vorwerk Kobold", "name": "Vorwerk Kobold",
"config_flow": true, "config_flow": true,
"documentation": "https://github.com/trunneml/homeassistant-vorwerk", "documentation": "https://www.home-assistant.io/integrations/vorwerk",
"requirements": [ "requirements": [
"pybotvac==0.0.23" "pybotvac==0.0.20"
], ],
"codeowners": [ "codeowners": [
"@trunneml", "@trunneml"
"@fuempel"
], ],
"dependencies": [ "dependencies": [
"http" "http"
], ],
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"version": "0.10.0" "version": "0.9.5"
} }

View File

@ -1,5 +0,0 @@
{
"name": "Eriks homeassistant-vorwerk addon",
"url": "https://git.sfs.ddnss.org/SFS/homeassistant-vorwerk",
"maintainer": "Erik Foris <erikfor@outlook.de"
}

View File

@ -16,6 +16,3 @@ custom_cleaning:
zone: zone:
description: Only supported on the VR300. Name of the zone to clean. Defaults to no zone i.e. complete house cleanup. description: Only supported on the VR300. Name of the zone to clean. Defaults to no zone i.e. complete house cleanup.
example: "Kitchen" example: "Kitchen"
map:
description: Only supported on the VR300. Name of the map for the zone to clean. Defaults to no zone i.e. complete house cleanup.
example: "Ground Floor"

View File

@ -6,14 +6,14 @@
"data": { "data": {
"email": "E-Mailaddresse" "email": "E-Mailaddresse"
}, },
"description": "Um einen Authentifizierungscode per E-Mail zu erhalten, gib die E-Mailadresse deines Vorwerk-Accounts ein." "description": "Um einen Authentifizierungscode per E-Mail zu erhalten, gib die E-Mailadresse deines Vorwerk-Accounts ein.\n\nSiehe [Vorwerk-Dokumentation]({docs_url})."
}, },
"code": { "code": {
"title": "Vorwerk Account Info", "title": "Vorwerk Account Info",
"data": { "data": {
"code": "Code" "code": "Code"
}, },
"description": "Gib den per E-Mail erhaltenen Code ein." "description": "Gib den per E-Mail erhaltenen Code ein.\n\nSee [Vorwerk documentation]({docs_url})."
} }
}, },
"error": { "error": {

View File

@ -6,14 +6,14 @@
"data": { "data": {
"email": "Email" "email": "Email"
}, },
"description": "To recieve an authentication code, enter the email address of your vorwerk account." "description": "To recieve an authentication code, enter the email address of your vorwerk account.\n\nSee [Vorwerk documentation]({docs_url})."
}, },
"code": { "code": {
"title": "Vorwerk Account Info", "title": "Vorwerk Account Info",
"data": { "data": {
"code": "Code" "code": "Code"
}, },
"description": "Enter the code you received by email." "description": "Enter the code you received by email.\n\nSee [Vorwerk documentation]({docs_url})."
} }
}, },
"error": { "error": {

View File

@ -5,8 +5,6 @@ import logging
from typing import Any from typing import Any
from pybotvac import Robot from pybotvac import Robot
from pybotvac.session import PasswordlessSession
from pybotvac.account import Account
from pybotvac.exceptions import NeatoRobotException from pybotvac.exceptions import NeatoRobotException
import voluptuous as vol import voluptuous as vol
@ -26,7 +24,7 @@ from homeassistant.components.vacuum import (
SUPPORT_STOP, SUPPORT_STOP,
StateVacuumEntity, StateVacuumEntity,
) )
from homeassistant.const import ATTR_MODE, CONF_TOKEN from homeassistant.const import ATTR_MODE
from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import (
@ -38,12 +36,10 @@ from .const import (
ATTR_CATEGORY, ATTR_CATEGORY,
ATTR_NAVIGATION, ATTR_NAVIGATION,
ATTR_ZONE, ATTR_ZONE,
ATTR_MAP,
VORWERK_DOMAIN, VORWERK_DOMAIN,
VORWERK_ROBOT_API, VORWERK_ROBOT_API,
VORWERK_ROBOT_COORDINATOR, VORWERK_ROBOT_COORDINATOR,
VORWERK_ROBOTS, VORWERK_ROBOTS,
VORWERK_CLIENT_ID,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -68,7 +64,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
async_add_entities( async_add_entities(
[ [
VorwerkConnectedVacuum( VorwerkConnectedVacuum(
robot[VORWERK_ROBOT_API], robot[VORWERK_ROBOT_COORDINATOR], robot[CONF_TOKEN] robot[VORWERK_ROBOT_API], robot[VORWERK_ROBOT_COORDINATOR]
) )
for robot in hass.data[VORWERK_DOMAIN][entry.entry_id][VORWERK_ROBOTS] for robot in hass.data[VORWERK_DOMAIN][entry.entry_id][VORWERK_ROBOTS]
], ],
@ -85,7 +81,6 @@ async def async_setup_entry(hass, entry, async_add_entities):
vol.Optional(ATTR_NAVIGATION, default=1): cv.positive_int, vol.Optional(ATTR_NAVIGATION, default=1): cv.positive_int,
vol.Optional(ATTR_CATEGORY, default=4): cv.positive_int, vol.Optional(ATTR_CATEGORY, default=4): cv.positive_int,
vol.Optional(ATTR_ZONE): cv.string, vol.Optional(ATTR_ZONE): cv.string,
vol.Optional(ATTR_MAP): cv.string,
}, },
"vorwerk_custom_cleaning", "vorwerk_custom_cleaning",
) )
@ -95,7 +90,7 @@ class VorwerkConnectedVacuum(CoordinatorEntity, StateVacuumEntity):
"""Representation of a Vorwerk Connected Vacuum.""" """Representation of a Vorwerk Connected Vacuum."""
def __init__( def __init__(
self, robot_state: VorwerkState, coordinator: DataUpdateCoordinator[Any], token self, robot_state: VorwerkState, coordinator: DataUpdateCoordinator[Any]
) -> None: ) -> None:
"""Initialize the Vorwerk Connected Vacuum.""" """Initialize the Vorwerk Connected Vacuum."""
super().__init__(coordinator) super().__init__(coordinator)
@ -104,7 +99,7 @@ class VorwerkConnectedVacuum(CoordinatorEntity, StateVacuumEntity):
self._name = f"{self.robot.name}" self._name = f"{self.robot.name}"
self._robot_serial = self.robot.serial self._robot_serial = self.robot.serial
self._token = token self._robot_boundaries: list[str] = []
@property @property
def name(self) -> str: def name(self) -> str:
@ -161,7 +156,7 @@ class VorwerkConnectedVacuum(CoordinatorEntity, StateVacuumEntity):
if not self._state: if not self._state:
return return
try: try:
if self._state.state == STATE_IDLE or self._state.state == STATE_DOCKED: if self._state.state == STATE_IDLE:
self.robot.start_cleaning() self.robot.start_cleaning()
elif self._state.state == STATE_PAUSED: elif self._state.state == STATE_PAUSED:
self.robot.resume_cleaning() self.robot.resume_cleaning()
@ -182,7 +177,7 @@ class VorwerkConnectedVacuum(CoordinatorEntity, StateVacuumEntity):
def return_to_base(self, **kwargs: Any) -> None: def return_to_base(self, **kwargs: Any) -> None:
"""Set the vacuum cleaner to return to the dock.""" """Set the vacuum cleaner to return to the dock."""
try: try:
if self._state.state == STATE_CLEANING: if self._state.state == STATE_CLEANING or self._state.state == STATE_DOCKED:
self.robot.pause_cleaning() self.robot.pause_cleaning()
self.robot.send_to_base() self.robot.send_to_base()
except NeatoRobotException as ex: except NeatoRobotException as ex:
@ -218,67 +213,23 @@ class VorwerkConnectedVacuum(CoordinatorEntity, StateVacuumEntity):
) )
def vorwerk_custom_cleaning( def vorwerk_custom_cleaning(
self, mode: str, navigation: str, category: str, zone: str, map: str | None = None self, mode: str, navigation: str, category: str, zone: str | None = None
) -> None: ) -> None:
"""Zone cleaning service call.""" """Zone cleaning service call."""
_LOGGER.debug("vorwerk_custom_cleaning called for %s / %s with token %s", map, zone, self._token)
# create Vorwerk API session + account object and populate the robot list
# (this necessary to update pybotvac internal states)
session = PasswordlessSession(client_id=VORWERK_CLIENT_ID, token=self._token)
account = Account(session)
robots = account.robots
_LOGGER.debug(" Robot list = %s", robots)
map_id = None
boundary_id = None boundary_id = None
if zone is not None:
if map is not None: for boundary in self._robot_boundaries:
# search map if zone in boundary["name"]:
maps = account.persistent_maps[self._robot_serial] boundary_id = boundary["id"]
if boundary_id is None:
_LOGGER.debug(" Persistent map list = %s", maps) _LOGGER.error(
"Zone '%s' was not found for the robot '%s'", zone, self.entity_id
map_obj = None )
available_maps = []
for m in maps:
available_maps.append(m['name'])
if map in m['name']:
map_obj = m
if map_obj is None:
_LOGGER.error("Map '%s' was not found for the robot '%s', list of valid maps: %s", map, self.entity_id, available_maps)
return return
_LOGGER.info("Start cleaning zone '%s' with robot %s", zone, self.entity_id)
map_id = map_obj['id']
_LOGGER.debug(" Found map %s = ID %s", map, map_id)
if zone is not None:
# search zone = boundary ID
boundaries = self.robot.get_map_boundaries(map_id).json()
_LOGGER.debug(" Boundary list = %s", boundaries)
boundary_obj = None
available_zones = []
for b in boundaries['data']['boundaries']:
available_zones.append(b['name'])
if zone in b['name']:
boundary_obj = b
if boundary_obj is None:
_LOGGER.error("Zone '%s' was not found for the robot '%s' on map '%s', list of valid zones: %s", zone, self.entity_id, map, available_zones)
return
boundary_id = boundary_obj['id']
_LOGGER.debug(" Found baundary / zone %s = ID %s", zone, boundary_id)
# start cleaning now
_LOGGER.info("Start cleaning zone '%s' on map '%s' with robot %s", zone, map, self.entity_id)
try: try:
self.robot.start_cleaning(mode, navigation, category, boundary_id, map_id) self.robot.start_cleaning(mode, navigation, category, boundary_id)
except NeatoRobotException as ex: except NeatoRobotException as ex:
_LOGGER.error( _LOGGER.error(
"Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex "Vorwerk vacuum connection error for '%s': %s", self.entity_id, ex