Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion custom_components/robovac/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError

from .const import CONF_AUTODISCOVERY, CONF_VACS, DOMAIN
from .const import (
CONF_AUTODISCOVERY,
CONF_ROOM_SEGMENT_MAP_ID,
CONF_ROOM_SEGMENTS,
CONF_VACS,
DOMAIN,
)
from .countries import (
get_phone_code_by_country_code,
get_phone_code_by_region,
Expand Down Expand Up @@ -283,6 +289,12 @@ async def async_step_edit(self, user_input: dict[str, Any] | None = None) -> Con
updated_vacuums[self.selected_vacuum][CONF_IP_ADDRESS] = user_input[
CONF_IP_ADDRESS
]
updated_vacuums[self.selected_vacuum][CONF_ROOM_SEGMENT_MAP_ID] = (
user_input[CONF_ROOM_SEGMENT_MAP_ID]
)
updated_vacuums[self.selected_vacuum][CONF_ROOM_SEGMENTS] = user_input[
CONF_ROOM_SEGMENTS
]

self.hass.config_entries.async_update_entry(
self.config_entry,
Expand All @@ -301,6 +313,14 @@ async def async_step_edit(self, user_input: dict[str, Any] | None = None) -> Con
CONF_IP_ADDRESS,
default=vacuums[self.selected_vacuum].get(CONF_IP_ADDRESS),
): str,
vol.Required(
CONF_ROOM_SEGMENT_MAP_ID,
default=vacuums[self.selected_vacuum].get(CONF_ROOM_SEGMENT_MAP_ID, 1),
): int,
vol.Optional(
CONF_ROOM_SEGMENTS,
default=vacuums[self.selected_vacuum].get(CONF_ROOM_SEGMENTS, ""),
): str,
}
)

Expand Down
2 changes: 2 additions & 0 deletions custom_components/robovac/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
DOMAIN = "robovac"
CONF_VACS = "vacuums"
CONF_AUTODISCOVERY = "autodiscovery"
CONF_ROOM_SEGMENT_MAP_ID = "room_segment_map_id"
CONF_ROOM_SEGMENTS = "room_segments"
REFRESH_RATE = 60
PING_RATE = 10
TIMEOUT = 5
8 changes: 6 additions & 2 deletions custom_components/robovac/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Edit vacuum",
"data": {
"autodiscovery": "Enable autodiscovery",
"ip_address": "IP Address"
"ip_address": "IP Address",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Automatically find the vacuum on the network.",
"ip_address": "The static IP address of your vacuum on your local network (optional if autodiscovery is enabled)."
"ip_address": "The static IP address of your vacuum on your local network (optional if autodiscovery is enabled).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
Comment on lines 39 to 50
"description": "Autodiscovery will automatically update the IP address"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/cy.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Golygu sugnwr llwch",
"data": {
"autodiscovery": "Galluogi awto-ddarganfod",
"ip_address": "Cyfeiriad IP"
"ip_address": "Cyfeiriad IP",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Dod o hyd i'r sugnwr llwch yn awtomatig ar y rhwydwaith.",
"ip_address": "Cyfeiriad IP statig eich sugnwr llwch ar eich rhwydwaith lleol (dewisol os yw awto-ddarganfod wedi'i alluogi)."
"ip_address": "Cyfeiriad IP statig eich sugnwr llwch ar eich rhwydwaith lleol (dewisol os yw awto-ddarganfod wedi'i alluogi).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "Bydd awto-ddarganfod yn diweddaru'r cyfeiriad IP yn awtomatig"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Staubsauger bearbeiten",
"data": {
"autodiscovery": "Autodiscovery aktivieren",
"ip_address": "IP-Adresse"
"ip_address": "IP-Adresse",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Den Staubsauger automatisch im Netzwerk finden.",
"ip_address": "Die statische IP-Adresse Ihres Staubsaugers in Ihrem lokalen Netzwerk (optional, wenn Autodiscovery aktiviert ist)."
"ip_address": "Die statische IP-Adresse Ihres Staubsaugers in Ihrem lokalen Netzwerk (optional, wenn Autodiscovery aktiviert ist).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "Autodiscovery aktualisiert die IP-Adresse automatisch"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Edit vacuum",
"data": {
"autodiscovery": "Enable autodiscovery",
"ip_address": "IP Address"
"ip_address": "IP Address",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Automatically find the vacuum on the network.",
"ip_address": "The static IP address of your vacuum on your local network (optional if autodiscovery is enabled)."
"ip_address": "The static IP address of your vacuum on your local network (optional if autodiscovery is enabled).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "Autodiscovery will automatically update the IP address"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Editar aspiradora",
"data": {
"autodiscovery": "Habilitar autodescubrimiento",
"ip_address": "Dirección IP"
"ip_address": "Dirección IP",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Encontrar automáticamente la aspiradora en la red.",
"ip_address": "La dirección IP estática de su aspiradora en su red local (opcional si el autodescubrimiento está habilitado)."
"ip_address": "La dirección IP estática de su aspiradora en su red local (opcional si el autodescubrimiento está habilitado).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "El autodescubrimiento actualizará automáticamente la dirección IP"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Modifier l'aspirateur",
"data": {
"autodiscovery": "Activer la découverte automatique",
"ip_address": "Adresse IP"
"ip_address": "Adresse IP",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Trouver automatiquement l'aspirateur sur le réseau.",
"ip_address": "L'adresse IP statique de votre aspirateur sur votre réseau local (optionnel si la découverte automatique est activée)."
"ip_address": "L'adresse IP statique de votre aspirateur sur votre réseau local (optionnel si la découverte automatique est activée).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "La découverte automatique mettra à jour automatiquement l'adresse IP"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Modifica aspirapolvere",
"data": {
"autodiscovery": "Abilita rilevamento automatico",
"ip_address": "Indirizzo IP"
"ip_address": "Indirizzo IP",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Trova automaticamente l'aspirapolvere sulla rete.",
"ip_address": "L'indirizzo IP statico del tuo aspirapolvere sulla tua rete locale (opzionale se il rilevamento automatico è abilitato)."
"ip_address": "L'indirizzo IP statico del tuo aspirapolvere sulla tua rete locale (opzionale se il rilevamento automatico è abilitato).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "Il rilevamento automatico aggiornerà automaticamente l'indirizzo IP"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Stofzuiger bewerken",
"data": {
"autodiscovery": "Automatische detectie inschakelen",
"ip_address": "IP-adres"
"ip_address": "IP-adres",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Vind de stofzuiger automatisch op het netwerk.",
"ip_address": "Het statische IP-adres van uw stofzuiger op uw lokale netwerk (optioneel als automatische detectie is ingeschakeld)."
"ip_address": "Het statische IP-adres van uw stofzuiger op uw lokale netwerk (optioneel als automatische detectie is ingeschakeld).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "Automatische detectie zal het IP-adres automatisch bijwerken"
}
Expand Down
8 changes: 6 additions & 2 deletions custom_components/robovac/translations/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
"title": "Editar aspirador",
"data": {
"autodiscovery": "Ativar descoberta automática",
"ip_address": "Endereço IP"
"ip_address": "Endereço IP",
"room_segment_map_id": "Room segment map ID",
"room_segments": "Room segments"
},
"data_description": {
"autodiscovery": "Encontrar automaticamente o aspirador na rede.",
"ip_address": "O endereço IP estático do seu aspirador na sua rede local (opcional se a descoberta automática estiver ativada)."
"ip_address": "O endereço IP estático do seu aspirador na sua rede local (opcional se a descoberta automática estiver ativada).",
"room_segment_map_id": "The map ID used when cleaning configured room segments.",
"room_segments": "Comma-separated room segments in id:name format, for example 1:Kitchen,2:Living Room."
},
"description": "A descoberta automática atualizará automaticamente o endereço IP"
}
Expand Down
104 changes: 103 additions & 1 deletion custom_components/robovac/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from __future__ import annotations
import asyncio
import base64
from dataclasses import dataclass
from datetime import timedelta
from enum import StrEnum
import json
Expand All @@ -27,6 +28,7 @@
from typing import Any, cast

from homeassistant.components.vacuum import (
Segment,
StateVacuumEntity,
VacuumActivity,
VacuumEntityFeature,
Expand All @@ -46,7 +48,15 @@
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import CONF_VACS, DOMAIN, PING_RATE, REFRESH_RATE, TIMEOUT
from .const import (
CONF_ROOM_SEGMENT_MAP_ID,
CONF_ROOM_SEGMENTS,
CONF_VACS,
DOMAIN,
PING_RATE,
REFRESH_RATE,
TIMEOUT,
)
from .errors import getErrorMessage
from .vacuums.base import RobovacCommand, RoboVacEntityFeature, TuyaCodes, TUYA_CONSUMABLES_CODES
from .robovac import ModelNotSupportedException, RoboVac
Expand Down Expand Up @@ -76,6 +86,50 @@
VACUUM_ACTIVITY_VALUES = {activity.value for activity in VacuumActivity}


@dataclass(frozen=True)
class RoomSegment:
"""Cleanable room segment for a RoboVac map."""

id: int
name: str


@dataclass(frozen=True)
class RoomSegmentMap:
"""Cleanable room segments and map id for a RoboVac."""

map_id: int
segments: tuple[RoomSegment, ...]


def _parse_room_segments(raw_segments: str | None) -> tuple[RoomSegment, ...]:
"""Parse configured room segments from 'id:name' comma-separated text."""
if not raw_segments:
return ()

segments: list[RoomSegment] = []
for raw_segment in raw_segments.split(","):
segment = raw_segment.strip()
if not segment:
continue
raw_id, separator, name = segment.partition(":")
if not separator:
_LOGGER.warning("Ignoring room segment without ':' separator: %s", segment)
continue
try:
segment_id = int(raw_id.strip())
except ValueError:
_LOGGER.warning("Ignoring room segment with invalid id: %s", segment)
continue
name = name.strip()
if not name:
_LOGGER.warning("Ignoring room segment without name: %s", segment)
continue
segments.append(RoomSegment(segment_id, name))

return tuple(segments)
Comment on lines +105 to +130


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
Expand Down Expand Up @@ -373,6 +427,11 @@ def __init__(self, item: dict[str, Any]) -> None:
self._attr_model_code = item[CONF_MODEL]
self._attr_ip_address = item[CONF_IP_ADDRESS]
self._attr_access_token = item[CONF_ACCESS_TOKEN]
configured_segments = _parse_room_segments(item.get(CONF_ROOM_SEGMENTS))
self._room_segment_map = RoomSegmentMap(
map_id=int(item.get(CONF_ROOM_SEGMENT_MAP_ID, 1)),
segments=configured_segments,
)
self.vacuum: RoboVac | None = None
self.update_failures = 0
self.tuyastatus: dict[str, Any] | None = None
Expand Down Expand Up @@ -415,6 +474,8 @@ def __init__(self, item: dict[str, Any]) -> None:
if self.vacuum is not None:
# Get the supported features from the vacuum
features = int(self.vacuum.getHomeAssistantFeatures())
if self._room_segment_map.segments:
features |= int(VacuumEntityFeature.CLEAN_AREA)
self._attr_supported_features = VacuumEntityFeature(features)
self._attr_robovac_supported = self.vacuum.getRoboVacFeatures()
self._attr_activity_mapping = self.vacuum.getRoboVacActivityMapping()
Expand Down Expand Up @@ -450,6 +511,41 @@ def __init__(self, item: dict[str, Any]) -> None:
},
)

async def async_get_segments(self) -> list[Segment]:
"""Return cleanable segments for Home Assistant clean-area mapping."""
return [
Segment(id=str(segment.id), name=segment.name)
for segment in self._room_segment_map.segments
]

async def async_clean_segments(self, segment_ids: list[str], **kwargs: Any) -> None:
"""Clean Home Assistant native clean-area segments."""
known_room_ids = {segment.id for segment in self._room_segment_map.segments}
room_ids: list[int] = []
for segment_id in segment_ids:
try:
room_id = int(segment_id)
except (TypeError, ValueError):
_LOGGER.warning("Ignoring invalid segment id for %s: %s", self.name, segment_id)
continue
if room_id not in known_room_ids:
_LOGGER.warning("Ignoring unknown segment id for %s: %s", self.name, segment_id)
continue
room_ids.append(room_id)

if not room_ids:
_LOGGER.warning("No valid segment ids supplied for %s: %s", self.name, segment_ids)
return

await self.async_send_command(
"roomClean",
{
"room_ids": room_ids,
"map_id": self._room_segment_map.map_id,
"count": int(kwargs.get("count", kwargs.get("repeats", 1))),
},
)

async def async_update(self) -> None:
"""Synchronize state from the vacuum.

Expand Down Expand Up @@ -932,7 +1028,13 @@ async def async_send_command(
elif command in ("roomClean", "room_clean") and params is not None and isinstance(params, dict):
room_ids = params.get("roomIds") or params.get("room_ids", [1])
count = params.get("count", 1)
map_id = params.get("mapId") or params.get("map_id")
if not isinstance(room_ids, list):
room_ids = [room_ids]

clean_request = {"roomIds": room_ids, "cleanTimes": count}
if map_id is not None:
clean_request["mapId"] = map_id
method_call = {
"method": "selectRoomsClean",
"data": clean_request,
Expand Down
Loading
Loading