Strict typing for Neato (#53633)

* Strict typing

* Rebase

* Tweak import

* Cleanup

* Rebase + typing hub

* Flake8

* Update homeassistant/components/neato/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/neato/vacuum.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/neato/camera.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Address review comments

* Black

* Update homeassistant/components/neato/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Specific dict definition

* Annotations

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Simone Chemelli 2021-08-08 15:02:37 +02:00 committed by GitHub
parent 3da61b77a9
commit 18a0fcf931
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 213 additions and 133 deletions

View File

@ -63,6 +63,7 @@ homeassistant.components.mailbox.*
homeassistant.components.media_player.*
homeassistant.components.mysensors.*
homeassistant.components.nam.*
homeassistant.components.neato.*
homeassistant.components.nest.*
homeassistant.components.netatmo.*
homeassistant.components.network.*

View File

@ -1,5 +1,8 @@
"""API for Neato Botvac bound to Home Assistant OAuth."""
from __future__ import annotations
from asyncio import run_coroutine_threadsafe
from typing import Any
import pybotvac
@ -7,7 +10,7 @@ from homeassistant import config_entries, core
from homeassistant.helpers import config_entry_oauth2_flow
class ConfigEntryAuth(pybotvac.OAuthSession):
class ConfigEntryAuth(pybotvac.OAuthSession): # type: ignore[misc]
"""Provide Neato Botvac authentication tied to an OAuth2 based config entry."""
def __init__(
@ -29,7 +32,7 @@ class ConfigEntryAuth(pybotvac.OAuthSession):
self.session.async_ensure_token_valid(), self.hass.loop
).result()
return self.session.token["access_token"]
return self.session.token["access_token"] # type: ignore[no-any-return]
class NeatoImplementation(config_entry_oauth2_flow.LocalOAuth2Implementation):
@ -39,7 +42,7 @@ class NeatoImplementation(config_entry_oauth2_flow.LocalOAuth2Implementation):
"""
@property
def extra_authorize_data(self) -> dict:
def extra_authorize_data(self) -> dict[str, Any]:
"""Extra data that needs to be appended to the authorize url."""
return {"client_secret": self.client_secret}

View File

@ -1,10 +1,20 @@
"""Support for loading picture from Neato."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from pybotvac.exceptions import NeatoRobotException
from pybotvac.robot import Robot
from urllib3.response import HTTPResponse
from homeassistant.components.camera import Camera
from homeassistant.components.neato import NeatoHub
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
NEATO_DOMAIN,
@ -20,11 +30,13 @@ SCAN_INTERVAL = timedelta(minutes=SCAN_INTERVAL_MINUTES)
ATTR_GENERATED_AT = "generated_at"
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Neato camera with config entry."""
dev = []
neato = hass.data.get(NEATO_LOGIN)
mapdata = hass.data.get(NEATO_MAP_DATA)
neato: NeatoHub = hass.data[NEATO_LOGIN]
mapdata: dict[str, Any] | None = hass.data.get(NEATO_MAP_DATA)
for robot in hass.data[NEATO_ROBOTS]:
if "maps" in robot.traits:
dev.append(NeatoCleaningMap(neato, robot, mapdata))
@ -39,7 +51,9 @@ async def async_setup_entry(hass, entry, async_add_entities):
class NeatoCleaningMap(Camera):
"""Neato cleaning map for last clean."""
def __init__(self, neato, robot, mapdata):
def __init__(
self, neato: NeatoHub, robot: Robot, mapdata: dict[str, Any] | None
) -> None:
"""Initialize Neato cleaning map."""
super().__init__()
self.robot = robot
@ -47,24 +61,18 @@ class NeatoCleaningMap(Camera):
self._mapdata = mapdata
self._available = neato is not None
self._robot_name = f"{self.robot.name} Cleaning Map"
self._robot_serial = self.robot.serial
self._generated_at = None
self._image_url = None
self._image = None
self._robot_serial: str = self.robot.serial
self._generated_at: str | None = None
self._image_url: str | None = None
self._image: bytes | None = None
def camera_image(self):
def camera_image(self) -> bytes | None:
"""Return image response."""
self.update()
return self._image
def update(self):
def update(self) -> None:
"""Check the contents of the map list."""
if self.neato is None:
_LOGGER.error("Error while updating '%s'", self.entity_id)
self._image = None
self._image_url = None
self._available = False
return
_LOGGER.debug("Running camera update for '%s'", self.entity_id)
try:
@ -80,7 +88,8 @@ class NeatoCleaningMap(Camera):
return
image_url = None
map_data = self._mapdata[self._robot_serial]["maps"][0]
if self._mapdata:
map_data: dict[str, Any] = self._mapdata[self._robot_serial]["maps"][0]
image_url = map_data["url"]
if image_url == self._image_url:
_LOGGER.debug(
@ -89,7 +98,7 @@ class NeatoCleaningMap(Camera):
return
try:
image = self.neato.download_map(image_url)
image: HTTPResponse = self.neato.download_map(image_url)
except NeatoRobotException as ex:
if self._available: # Print only once when available
_LOGGER.error(
@ -102,33 +111,33 @@ class NeatoCleaningMap(Camera):
self._image = image.read()
self._image_url = image_url
self._generated_at = map_data["generated_at"]
self._generated_at = map_data.get("generated_at")
self._available = True
@property
def name(self):
def name(self) -> str:
"""Return the name of this camera."""
return self._robot_name
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return unique ID."""
return self._robot_serial
@property
def available(self):
def available(self) -> bool:
"""Return if the robot is available."""
return self._available
@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Device info for neato robot."""
return {"identifiers": {(NEATO_DOMAIN, self._robot_serial)}}
@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the vacuum cleaner."""
data = {}
data: dict[str, Any] = {}
if self._generated_at is not None:
data[ATTR_GENERATED_AT] = self._generated_at

View File

@ -2,10 +2,13 @@
from __future__ import annotations
import logging
from types import MappingProxyType
from typing import Any
import voluptuous as vol
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_entry_oauth2_flow
from .const import NEATO_DOMAIN
@ -23,7 +26,9 @@ class OAuth2FlowHandler(
"""Return logger."""
return logging.getLogger(__name__)
async def async_step_user(self, user_input: dict | None = None) -> dict:
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Create an entry for the flow."""
current_entries = self._async_current_entries()
if self.source != SOURCE_REAUTH and current_entries:
@ -32,11 +37,13 @@ class OAuth2FlowHandler(
return await super().async_step_user(user_input=user_input)
async def async_step_reauth(self, data) -> dict:
async def async_step_reauth(self, data: MappingProxyType[str, Any]) -> FlowResult:
"""Perform reauth upon migration of old entries."""
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(self, user_input: dict | None = None) -> dict:
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Confirm reauth upon migration of old entries."""
if user_input is None:
return self.async_show_form(
@ -44,7 +51,7 @@ class OAuth2FlowHandler(
)
return await self.async_step_user()
async def async_oauth_create_entry(self, data: dict) -> dict:
async def async_oauth_create_entry(self, data: dict[str, Any]) -> FlowResult:
"""Create an entry for the flow. Update an entry if one already exist."""
current_entries = self._async_current_entries()
if self.source == SOURCE_REAUTH and current_entries:

View File

@ -3,7 +3,9 @@ from datetime import timedelta
import logging
from pybotvac import Account
from urllib3.response import HTTPResponse
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.util import Throttle
@ -21,23 +23,23 @@ class NeatoHub:
self.my_neato: Account = neato
@Throttle(timedelta(minutes=1))
def update_robots(self):
def update_robots(self) -> None:
"""Update the robot states."""
_LOGGER.debug("Running HUB.update_robots %s", self._hass.data.get(NEATO_ROBOTS))
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
self._hass.data[NEATO_PERSISTENT_MAPS] = self.my_neato.persistent_maps
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
def download_map(self, url):
def download_map(self, url: str) -> HTTPResponse:
"""Download a new map image."""
map_image_data = self.my_neato.get_map_image(url)
return map_image_data
async def async_update_entry_unique_id(self, entry) -> str:
async def async_update_entry_unique_id(self, entry: ConfigEntry) -> str:
"""Update entry for unique_id."""
await self._hass.async_add_executor_job(self.my_neato.refresh_userdata)
unique_id = self.my_neato.unique_id
unique_id: str = self.my_neato.unique_id
if entry.unique_id == unique_id:
return unique_id

View File

@ -1,11 +1,20 @@
"""Support for Neato sensors."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from pybotvac.exceptions import NeatoRobotException
from pybotvac.robot import Robot
from homeassistant.components.neato import NeatoHub
from homeassistant.components.sensor import DEVICE_CLASS_BATTERY, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import NEATO_DOMAIN, NEATO_LOGIN, NEATO_ROBOTS, SCAN_INTERVAL_MINUTES
@ -16,10 +25,12 @@ SCAN_INTERVAL = timedelta(minutes=SCAN_INTERVAL_MINUTES)
BATTERY = "Battery"
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the Neato sensor using config entry."""
dev = []
neato = hass.data.get(NEATO_LOGIN)
neato: NeatoHub = hass.data[NEATO_LOGIN]
for robot in hass.data[NEATO_ROBOTS]:
dev.append(NeatoSensor(neato, robot))
@ -33,15 +44,15 @@ async def async_setup_entry(hass, entry, async_add_entities):
class NeatoSensor(SensorEntity):
"""Neato sensor."""
def __init__(self, neato, robot):
def __init__(self, neato: NeatoHub, robot: Robot) -> None:
"""Initialize Neato sensor."""
self.robot = robot
self._available = False
self._robot_name = f"{self.robot.name} {BATTERY}"
self._robot_serial = self.robot.serial
self._state = None
self._available: bool = False
self._robot_name: str = f"{self.robot.name} {BATTERY}"
self._robot_serial: str = self.robot.serial
self._state: dict[str, Any] | None = None
def update(self):
def update(self) -> None:
"""Update Neato Sensor."""
try:
self._state = self.robot.state
@ -58,36 +69,38 @@ class NeatoSensor(SensorEntity):
_LOGGER.debug("self._state=%s", self._state)
@property
def name(self):
def name(self) -> str:
"""Return the name of this sensor."""
return self._robot_name
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return unique ID."""
return self._robot_serial
@property
def device_class(self):
def device_class(self) -> str:
"""Return the device class."""
return DEVICE_CLASS_BATTERY
@property
def available(self):
def available(self) -> bool:
"""Return availability."""
return self._available
@property
def state(self):
def state(self) -> str | None:
"""Return the state."""
return self._state["details"]["charge"] if self._state else None
if self._state is not None:
return str(self._state["details"]["charge"])
return None
@property
def unit_of_measurement(self):
def unit_of_measurement(self) -> str:
"""Return unit of measurement."""
return PERCENTAGE
@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Device info for neato robot."""
return {"identifiers": {(NEATO_DOMAIN, self._robot_serial)}}

View File

@ -1,11 +1,19 @@
"""Support for Neato Connected Vacuums switches."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from pybotvac.exceptions import NeatoRobotException
from pybotvac.robot import Robot
from homeassistant.components.neato import NeatoHub
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo, ToggleEntity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import NEATO_DOMAIN, NEATO_LOGIN, NEATO_ROBOTS, SCAN_INTERVAL_MINUTES
@ -18,10 +26,13 @@ SWITCH_TYPE_SCHEDULE = "schedule"
SWITCH_TYPES = {SWITCH_TYPE_SCHEDULE: ["Schedule"]}
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Neato switch with config entry."""
dev = []
neato = hass.data.get(NEATO_LOGIN)
neato: NeatoHub = hass.data[NEATO_LOGIN]
for robot in hass.data[NEATO_ROBOTS]:
for type_name in SWITCH_TYPES:
dev.append(NeatoConnectedSwitch(neato, robot, type_name))
@ -36,18 +47,18 @@ async def async_setup_entry(hass, entry, async_add_entities):
class NeatoConnectedSwitch(ToggleEntity):
"""Neato Connected Switches."""
def __init__(self, neato, robot, switch_type):
def __init__(self, neato: NeatoHub, robot: Robot, switch_type: str) -> None:
"""Initialize the Neato Connected switches."""
self.type = switch_type
self.robot = robot
self._available = False
self._robot_name = f"{self.robot.name} {SWITCH_TYPES[self.type][0]}"
self._state = None
self._schedule_state = None
self._state: dict[str, Any] | None = None
self._schedule_state: str | None = None
self._clean_state = None
self._robot_serial = self.robot.serial
self._robot_serial: str = self.robot.serial
def update(self):
def update(self) -> None:
"""Update the states of Neato switches."""
_LOGGER.debug("Running Neato switch update for '%s'", self.entity_id)
try:
@ -65,7 +76,7 @@ class NeatoConnectedSwitch(ToggleEntity):
_LOGGER.debug("self._state=%s", self._state)
if self.type == SWITCH_TYPE_SCHEDULE:
_LOGGER.debug("State: %s", self._state)
if self._state["details"]["isScheduleEnabled"]:
if self._state is not None and self._state["details"]["isScheduleEnabled"]:
self._schedule_state = STATE_ON
else:
self._schedule_state = STATE_OFF
@ -74,34 +85,33 @@ class NeatoConnectedSwitch(ToggleEntity):
)
@property
def name(self):
def name(self) -> str:
"""Return the name of the switch."""
return self._robot_name
@property
def available(self):
def available(self) -> bool:
"""Return True if entity is available."""
return self._available
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return a unique ID."""
return self._robot_serial
@property
def is_on(self):
def is_on(self) -> bool:
"""Return true if switch is on."""
if self.type == SWITCH_TYPE_SCHEDULE:
if self._schedule_state == STATE_ON:
return True
return False
return bool(
self.type == SWITCH_TYPE_SCHEDULE and self._schedule_state == STATE_ON
)
@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Device info for neato robot."""
return {"identifiers": {(NEATO_DOMAIN, self._robot_serial)}}
def turn_on(self, **kwargs):
def turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
if self.type == SWITCH_TYPE_SCHEDULE:
try:
@ -111,7 +121,7 @@ class NeatoConnectedSwitch(ToggleEntity):
"Neato switch connection error '%s': %s", self.entity_id, ex
)
def turn_off(self, **kwargs):
def turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
if self.type == SWITCH_TYPE_SCHEDULE:
try:

View File

@ -1,7 +1,11 @@
"""Support for Neato Connected Vacuums."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from pybotvac import Robot
from pybotvac.exceptions import NeatoRobotException
import voluptuous as vol
@ -24,9 +28,14 @@ from homeassistant.components.vacuum import (
SUPPORT_STOP,
StateVacuumEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_MODE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import NeatoHub
from .const import (
ACTION,
ALERTS,
@ -72,12 +81,14 @@ ATTR_CATEGORY = "category"
ATTR_ZONE = "zone"
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Neato vacuum with config entry."""
dev = []
neato = hass.data.get(NEATO_LOGIN)
mapdata = hass.data.get(NEATO_MAP_DATA)
persistent_maps = hass.data.get(NEATO_PERSISTENT_MAPS)
neato: NeatoHub = hass.data[NEATO_LOGIN]
mapdata: dict[str, Any] | None = hass.data.get(NEATO_MAP_DATA)
persistent_maps: dict[str, Any] | None = hass.data.get(NEATO_PERSISTENT_MAPS)
for robot in hass.data[NEATO_ROBOTS]:
dev.append(NeatoConnectedVacuum(neato, robot, mapdata, persistent_maps))
@ -105,33 +116,39 @@ async def async_setup_entry(hass, entry, async_add_entities):
class NeatoConnectedVacuum(StateVacuumEntity):
"""Representation of a Neato Connected Vacuum."""
def __init__(self, neato, robot, mapdata, persistent_maps):
def __init__(
self,
neato: NeatoHub,
robot: Robot,
mapdata: dict[str, Any] | None,
persistent_maps: dict[str, Any] | None,
) -> None:
"""Initialize the Neato Connected Vacuum."""
self.robot = robot
self._available = neato is not None
self._available: bool = neato is not None
self._mapdata = mapdata
self._name = f"{self.robot.name}"
self._robot_has_map = self.robot.has_persistent_maps
self._name: str = f"{self.robot.name}"
self._robot_has_map: bool = self.robot.has_persistent_maps
self._robot_maps = persistent_maps
self._robot_serial = self.robot.serial
self._status_state = None
self._clean_state = None
self._state = None
self._clean_time_start = None
self._clean_time_stop = None
self._clean_area = None
self._clean_battery_start = None
self._clean_battery_end = None
self._clean_susp_charge_count = None
self._clean_susp_time = None
self._clean_pause_time = None
self._clean_error_time = None
self._launched_from = None
self._battery_level = None
self._robot_boundaries = []
self._robot_stats = None
self._robot_serial: str = self.robot.serial
self._status_state: str | None = None
self._clean_state: str | None = None
self._state: dict[str, Any] | None = None
self._clean_time_start: str | None = None
self._clean_time_stop: str | None = None
self._clean_area: float | None = None
self._clean_battery_start: int | None = None
self._clean_battery_end: int | None = None
self._clean_susp_charge_count: int | None = None
self._clean_susp_time: int | None = None
self._clean_pause_time: int | None = None
self._clean_error_time: int | None = None
self._launched_from: str | None = None
self._battery_level: int | None = None
self._robot_boundaries: list = []
self._robot_stats: dict[str, Any] | None = None
def update(self):
def update(self) -> None:
"""Update the states of Neato Vacuums."""
_LOGGER.debug("Running Neato Vacuums update for '%s'", self.entity_id)
try:
@ -151,6 +168,8 @@ class NeatoConnectedVacuum(StateVacuumEntity):
self._available = False
return
if self._state is None:
return
self._available = True
_LOGGER.debug("self._state=%s", self._state)
if "alert" in self._state:
@ -198,10 +217,12 @@ class NeatoConnectedVacuum(StateVacuumEntity):
self._battery_level = self._state["details"]["charge"]
if not self._mapdata.get(self._robot_serial, {}).get("maps", []):
if self._mapdata is None or not self._mapdata.get(self._robot_serial, {}).get(
"maps", []
):
return
mapdata = self._mapdata[self._robot_serial]["maps"][0]
mapdata: dict[str, Any] = self._mapdata[self._robot_serial]["maps"][0]
self._clean_time_start = mapdata["start_at"]
self._clean_time_stop = mapdata["end_at"]
self._clean_area = mapdata["cleaned_area"]
@ -215,10 +236,11 @@ class NeatoConnectedVacuum(StateVacuumEntity):
if (
self._robot_has_map
and self._state
and self._state["availableServices"]["maps"] != "basic-1"
and self._robot_maps[self._robot_serial]
and self._robot_maps
):
allmaps = self._robot_maps[self._robot_serial]
allmaps: dict = self._robot_maps[self._robot_serial]
_LOGGER.debug(
"Found the following maps for '%s': %s", self.entity_id, allmaps
)
@ -249,44 +271,44 @@ class NeatoConnectedVacuum(StateVacuumEntity):
)
@property
def name(self):
def name(self) -> str:
"""Return the name of the device."""
return self._name
@property
def supported_features(self):
def supported_features(self) -> int:
"""Flag vacuum cleaner robot features that are supported."""
return SUPPORT_NEATO
@property
def battery_level(self):
def battery_level(self) -> int | None:
"""Return the battery level of the vacuum cleaner."""
return self._battery_level
@property
def available(self):
def available(self) -> bool:
"""Return if the robot is available."""
return self._available
@property
def icon(self):
def icon(self) -> str:
"""Return neato specific icon."""
return "mdi:robot-vacuum-variant"
@property
def state(self):
def state(self) -> str | None:
"""Return the status of the vacuum cleaner."""
return self._clean_state
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return a unique ID."""
return self._robot_serial
@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the vacuum cleaner."""
data = {}
data: dict[str, Any] = {}
if self._status_state is not None:
data[ATTR_STATUS] = self._status_state
@ -314,28 +336,32 @@ class NeatoConnectedVacuum(StateVacuumEntity):
return data
@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Device info for neato robot."""
info = {"identifiers": {(NEATO_DOMAIN, self._robot_serial)}, "name": self._name}
info: DeviceInfo = {
"identifiers": {(NEATO_DOMAIN, self._robot_serial)},
"name": self._name,
}
if self._robot_stats:
info["manufacturer"] = self._robot_stats["battery"]["vendor"]
info["model"] = self._robot_stats["model"]
info["sw_version"] = self._robot_stats["firmware"]
return info
def start(self):
def start(self) -> None:
"""Start cleaning or resume cleaning."""
try:
if self._state["state"] == 1:
self.robot.start_cleaning()
elif self._state["state"] == 3:
self.robot.resume_cleaning()
except NeatoRobotException as ex:
_LOGGER.error(
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
if self._state:
try:
if self._state["state"] == 1:
self.robot.start_cleaning()
elif self._state["state"] == 3:
self.robot.resume_cleaning()
except NeatoRobotException as ex:
_LOGGER.error(
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
def pause(self):
def pause(self) -> None:
"""Pause the vacuum."""
try:
self.robot.pause_cleaning()
@ -344,7 +370,7 @@ class NeatoConnectedVacuum(StateVacuumEntity):
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
def return_to_base(self, **kwargs):
def return_to_base(self, **kwargs: Any) -> None:
"""Set the vacuum cleaner to return to the dock."""
try:
if self._clean_state == STATE_CLEANING:
@ -356,7 +382,7 @@ class NeatoConnectedVacuum(StateVacuumEntity):
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
def stop(self, **kwargs):
def stop(self, **kwargs: Any) -> None:
"""Stop the vacuum cleaner."""
try:
self.robot.stop_cleaning()
@ -365,7 +391,7 @@ class NeatoConnectedVacuum(StateVacuumEntity):
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
def locate(self, **kwargs):
def locate(self, **kwargs: Any) -> None:
"""Locate the robot by making it emit a sound."""
try:
self.robot.locate()
@ -374,7 +400,7 @@ class NeatoConnectedVacuum(StateVacuumEntity):
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
def clean_spot(self, **kwargs):
def clean_spot(self, **kwargs: Any) -> None:
"""Run a spot cleaning starting from the base."""
try:
self.robot.start_spot_cleaning()
@ -383,7 +409,9 @@ class NeatoConnectedVacuum(StateVacuumEntity):
"Neato vacuum connection error for '%s': %s", self.entity_id, ex
)
def neato_custom_cleaning(self, mode, navigation, category, zone=None):
def neato_custom_cleaning(
self, mode: str, navigation: str, category: str, zone: str | None = None
) -> None:
"""Zone cleaning service call."""
boundary_id = None
if zone is not None:

View File

@ -704,6 +704,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.neato.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.nest.*]
check_untyped_defs = true
disallow_incomplete_defs = true
@ -1515,9 +1526,6 @@ ignore_errors = true
[mypy-homeassistant.components.mullvad.*]
ignore_errors = true
[mypy-homeassistant.components.neato.*]
ignore_errors = true
[mypy-homeassistant.components.ness_alarm.*]
ignore_errors = true

View File

@ -101,7 +101,6 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.mobile_app.*",
"homeassistant.components.motion_blinds.*",
"homeassistant.components.mullvad.*",
"homeassistant.components.neato.*",
"homeassistant.components.ness_alarm.*",
"homeassistant.components.nest.legacy.*",
"homeassistant.components.netio.*",