Files
ha_config/custom_components/gardena_smart_system/vacuum.py
T
2023-11-21 12:23:38 +01:00

223 lines
7.3 KiB
Python

"""Support for Gardena mower."""
import asyncio
import logging
from datetime import timedelta
from homeassistant.core import callback
from homeassistant.components.vacuum import (
StateVacuumEntity,
SUPPORT_BATTERY,
SUPPORT_RETURN_HOME,
SUPPORT_STATE,
SUPPORT_STOP,
SUPPORT_START,
STATE_PAUSED,
STATE_CLEANING,
STATE_DOCKED,
STATE_RETURNING,
STATE_ERROR,
ATTR_BATTERY_LEVEL,
)
from .const import (
ATTR_ACTIVITY,
ATTR_BATTERY_STATE,
ATTR_NAME,
ATTR_OPERATING_HOURS,
ATTR_RF_LINK_LEVEL,
ATTR_RF_LINK_STATE,
ATTR_SERIAL,
ATTR_LAST_ERROR,
ATTR_ERROR,
ATTR_STATE,
CONF_MOWER_DURATION,
DEFAULT_MOWER_DURATION,
DOMAIN,
GARDENA_LOCATION,
)
from .sensor import GardenaSensor
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=1)
SUPPORT_GARDENA = (
SUPPORT_BATTERY | SUPPORT_RETURN_HOME | SUPPORT_STOP | SUPPORT_START | SUPPORT_STATE
)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Gardena smart mower system."""
entities = []
for mower in hass.data[DOMAIN][GARDENA_LOCATION].find_device_by_type("MOWER"):
entities.append(GardenaSmartMower(hass, mower, config_entry.options))
_LOGGER.debug("Adding mower as vacuums: %s", entities)
async_add_entities(entities, True)
class GardenaSmartMower(StateVacuumEntity):
"""Representation of a Gardena Connected Mower."""
def __init__(self, hass, mower, options):
"""Initialize the Gardena Connected Mower."""
self.hass = hass
self._device = mower
self._options = options
self._name = "{}".format(self._device.name)
self._unique_id = f"{self._device.serial}-mower"
self._state = None
self._error_message = ""
async def async_added_to_hass(self):
"""Subscribe to events."""
self._device.add_callback(self.update_callback)
@property
def should_poll(self) -> bool:
"""No polling needed for a vacuum."""
return False
def update_callback(self, device):
"""Call update for Home Assistant when the device is updated."""
self.schedule_update_ha_state(True)
async def async_update(self):
"""Update the states of Gardena devices."""
_LOGGER.debug("Running Gardena update")
# Managing state
state = self._device.state
_LOGGER.debug("Mower has state %s", state)
if state in ["WARNING", "ERROR", "UNAVAILABLE"]:
_LOGGER.debug("Mower has an error")
self._state = STATE_ERROR
self._error_message = self._device.last_error_code
else:
_LOGGER.debug("Getting mower state")
activity = self._device.activity
_LOGGER.debug("Mower has activity %s", activity)
if activity == "PAUSED":
self._state = STATE_PAUSED
elif activity in [
"OK_CUTTING",
"OK_CUTTING_TIMER_OVERRIDDEN",
"OK_LEAVING",
]:
self._state = STATE_CLEANING
elif activity == "OK_SEARCHING":
self._state = STATE_RETURNING
elif activity in [
"OK_CHARGING",
"PARKED_TIMER",
"PARKED_PARK_SELECTED",
"PARKED_AUTOTIMER",
]:
self._state = STATE_DOCKED
elif activity == "NONE":
self._state = None
_LOGGER.debug("Mower has no activity")
@property
def name(self):
"""Return the name of the device."""
return self._device.name
@property
def supported_features(self):
"""Flag lawn mower robot features that are supported."""
return SUPPORT_GARDENA
@property
def battery_level(self):
"""Return the battery level of the lawn mower."""
return self._device.battery_level
@property
def state(self):
"""Return the status of the lawn mower."""
return self._state
@property
def available(self):
"""Return True if the device is available."""
return self._device.state != "UNAVAILABLE"
def error(self):
"""Return the error message."""
if self._state == STATE_ERROR:
return self._error_message
return ""
@property
def extra_state_attributes(self):
"""Return the state attributes of the lawn mower."""
return {
ATTR_ACTIVITY: self._device.activity,
ATTR_BATTERY_LEVEL: self._device.battery_level,
ATTR_BATTERY_STATE: self._device.battery_state,
ATTR_RF_LINK_LEVEL: self._device.rf_link_level,
ATTR_RF_LINK_STATE: self._device.rf_link_state,
ATTR_OPERATING_HOURS: self._device.operating_hours,
ATTR_LAST_ERROR: self._device.last_error_code,
ATTR_ERROR: "NONE" if self._device.activity != "NONE" else self._device.last_error_code,
ATTR_STATE: self._device.activity if self._device.activity != "NONE" else self._device.last_error_code
}
@property
def option_mower_duration(self) -> int:
return self._options.get(CONF_MOWER_DURATION, DEFAULT_MOWER_DURATION)
def start(self):
"""Start the mower using Gardena API command START_SECONDS_TO_OVERRIDE. Duration is read from integration options."""
duration = self.option_mower_duration * 60
_LOGGER.debug("Mower command: vacuum.start => START_SECONDS_TO_OVERRIDE, %s", duration)
return asyncio.run_coroutine_threadsafe(
self._device.start_seconds_to_override(duration), self.hass.loop
).result()
def stop(self, **kwargs):
"""Stop the mower using Gardena API command PARK_UNTIL_FURTHER_NOTICE."""
_LOGGER.debug("Mower command: vacuum.stop => PARK_UNTIL_FURTHER_NOTICE")
asyncio.run_coroutine_threadsafe(
self._device.park_until_further_notice(), self.hass.loop
).result()
def turn_on(self, **kwargs):
"""Start the mower using Gardena API command START_DONT_OVERRIDE."""
_LOGGER.debug("Mower command: vacuum.turn_on => START_DONT_OVERRIDE")
asyncio.run_coroutine_threadsafe(
self._device.start_dont_override(), self.hass.loop
).result()
def turn_off(self, **kwargs):
"""Stop the mower using Gardena API command PARK_UNTIL_FURTHER_NOTICE."""
_LOGGER.debug("Mower command: vacuum.turn_off => PARK_UNTIL_FURTHER_NOTICE")
asyncio.run_coroutine_threadsafe(
self._device.park_until_further_notice(), self.hass.loop
).result()
def return_to_base(self, **kwargs):
"""Stop the mower using Gardena API command PARK_UNTIL_NEXT_TASK."""
_LOGGER.debug("Mower command: vacuum.return_to_base => PARK_UNTIL_NEXT_TASK")
asyncio.run_coroutine_threadsafe(
self._device.park_until_next_task(), self.hass.loop
).result()
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return self._unique_id
@property
def device_info(self):
return {
"identifiers": {
# Serial numbers are unique identifiers within a specific domain
(DOMAIN, self._device.serial)
},
"name": self._device.name,
"manufacturer": "Gardena",
"model": self._device.model_type,
}