Remove denonavr from mypy ignore list (#74580)

This commit is contained in:
epenet 2022-07-07 12:14:46 +02:00 committed by GitHub
parent 4604694255
commit f19c542d6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 92 deletions

View file

@ -90,14 +90,14 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self) -> None: def __init__(self) -> None:
"""Initialize the Denon AVR flow.""" """Initialize the Denon AVR flow."""
self.host = None self.host: str | None = None
self.serial_number = None self.serial_number: str | None = None
self.model_name = None self.model_name: str | None = None
self.timeout = DEFAULT_TIMEOUT self.timeout = DEFAULT_TIMEOUT
self.show_all_sources = DEFAULT_SHOW_SOURCES self.show_all_sources = DEFAULT_SHOW_SOURCES
self.zone2 = DEFAULT_ZONE2 self.zone2 = DEFAULT_ZONE2
self.zone3 = DEFAULT_ZONE3 self.zone3 = DEFAULT_ZONE3
self.d_receivers = [] self.d_receivers: list[dict[str, Any]] = []
@staticmethod @staticmethod
@callback @callback
@ -138,7 +138,7 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> FlowResult: ) -> FlowResult:
"""Handle multiple receivers found.""" """Handle multiple receivers found."""
errors = {} errors: dict[str, str] = {}
if user_input is not None: if user_input is not None:
self.host = user_input["select_host"] self.host = user_input["select_host"]
return await self.async_step_connect() return await self.async_step_connect()
@ -169,6 +169,7 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> FlowResult: ) -> FlowResult:
"""Connect to the receiver.""" """Connect to the receiver."""
assert self.host
connect_denonavr = ConnectDenonAVR( connect_denonavr = ConnectDenonAVR(
self.host, self.host,
self.timeout, self.timeout,
@ -185,6 +186,7 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if not success: if not success:
return self.async_abort(reason="cannot_connect") return self.async_abort(reason="cannot_connect")
receiver = connect_denonavr.receiver receiver = connect_denonavr.receiver
assert receiver
if not self.serial_number: if not self.serial_number:
self.serial_number = receiver.serial_number self.serial_number = receiver.serial_number
@ -238,6 +240,7 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"*", "" "*", ""
) )
self.serial_number = discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL] self.serial_number = discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL]
assert discovery_info.ssdp_location is not None
self.host = urlparse(discovery_info.ssdp_location).hostname self.host = urlparse(discovery_info.ssdp_location).hostname
if self.model_name in IGNORED_MODELS: if self.model_name in IGNORED_MODELS:
@ -260,6 +263,6 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_confirm() return await self.async_step_confirm()
@staticmethod @staticmethod
def construct_unique_id(model_name: str, serial_number: str) -> str: def construct_unique_id(model_name: str | None, serial_number: str | None) -> str:
"""Construct the unique id from the ssdp discovery or user_step.""" """Construct the unique id from the ssdp discovery or user_step."""
return f"{model_name}-{serial_number}" return f"{model_name}-{serial_number}"

View file

@ -1,10 +1,11 @@
"""Support for Denon AVR receivers using their HTTP interface.""" """Support for Denon AVR receivers using their HTTP interface."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Coroutine from collections.abc import Awaitable, Callable, Coroutine
from datetime import timedelta from datetime import timedelta
from functools import wraps from functools import wraps
import logging import logging
from typing import Any, TypeVar
from denonavr import DenonAVR from denonavr import DenonAVR
from denonavr.const import POWER_ON from denonavr.const import POWER_ON
@ -15,6 +16,7 @@ from denonavr.exceptions import (
AvrTimoutError, AvrTimoutError,
DenonAvrError, DenonAvrError,
) )
from typing_extensions import Concatenate, ParamSpec
import voluptuous as vol import voluptuous as vol
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
@ -79,6 +81,10 @@ SERVICE_GET_COMMAND = "get_command"
SERVICE_SET_DYNAMIC_EQ = "set_dynamic_eq" SERVICE_SET_DYNAMIC_EQ = "set_dynamic_eq"
SERVICE_UPDATE_AUDYSSEY = "update_audyssey" SERVICE_UPDATE_AUDYSSEY = "update_audyssey"
_DenonDeviceT = TypeVar("_DenonDeviceT", bound="DenonDevice")
_R = TypeVar("_R")
_P = ParamSpec("_P")
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
@ -131,6 +137,79 @@ async def async_setup_entry(
async_add_entities(entities, update_before_add=True) async_add_entities(entities, update_before_add=True)
def async_log_errors(
func: Callable[Concatenate[_DenonDeviceT, _P], Awaitable[_R]],
) -> Callable[Concatenate[_DenonDeviceT, _P], Coroutine[Any, Any, _R | None]]:
"""
Log errors occurred when calling a Denon AVR receiver.
Decorates methods of DenonDevice class.
Declaration of staticmethod for this method is at the end of this class.
"""
@wraps(func)
async def wrapper(
self: _DenonDeviceT, *args: _P.args, **kwargs: _P.kwargs
) -> _R | None:
# pylint: disable=protected-access
available = True
try:
return await func(self, *args, **kwargs)
except AvrTimoutError:
available = False
if self._available is True:
_LOGGER.warning(
"Timeout connecting to Denon AVR receiver at host %s. "
"Device is unavailable",
self._receiver.host,
)
self._available = False
except AvrNetworkError:
available = False
if self._available is True:
_LOGGER.warning(
"Network error connecting to Denon AVR receiver at host %s. "
"Device is unavailable",
self._receiver.host,
)
self._available = False
except AvrForbiddenError:
available = False
if self._available is True:
_LOGGER.warning(
"Denon AVR receiver at host %s responded with HTTP 403 error. "
"Device is unavailable. Please consider power cycling your "
"receiver",
self._receiver.host,
)
self._available = False
except AvrCommandError as err:
available = False
_LOGGER.error(
"Command %s failed with error: %s",
func.__name__,
err,
)
except DenonAvrError as err:
available = False
_LOGGER.error(
"Error %s occurred in method %s for Denon AVR receiver",
err,
func.__name__,
exc_info=True,
)
finally:
if available is True and self._available is False:
_LOGGER.info(
"Denon AVR receiver at host %s is available again",
self._receiver.host,
)
self._available = True
return None
return wrapper
class DenonDevice(MediaPlayerEntity): class DenonDevice(MediaPlayerEntity):
"""Representation of a Denon Media Player Device.""" """Representation of a Denon Media Player Device."""
@ -144,6 +223,7 @@ class DenonDevice(MediaPlayerEntity):
"""Initialize the device.""" """Initialize the device."""
self._attr_name = receiver.name self._attr_name = receiver.name
self._attr_unique_id = unique_id self._attr_unique_id = unique_id
assert config_entry.unique_id
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
configuration_url=f"http://{config_entry.data[CONF_HOST]}/", configuration_url=f"http://{config_entry.data[CONF_HOST]}/",
identifiers={(DOMAIN, config_entry.unique_id)}, identifiers={(DOMAIN, config_entry.unique_id)},
@ -163,71 +243,6 @@ class DenonDevice(MediaPlayerEntity):
) )
self._available = True self._available = True
def async_log_errors(
func: Coroutine,
) -> Coroutine:
"""
Log errors occurred when calling a Denon AVR receiver.
Decorates methods of DenonDevice class.
Declaration of staticmethod for this method is at the end of this class.
"""
@wraps(func)
async def wrapper(self, *args, **kwargs):
# pylint: disable=protected-access
available = True
try:
return await func(self, *args, **kwargs)
except AvrTimoutError:
available = False
if self._available is True:
_LOGGER.warning(
"Timeout connecting to Denon AVR receiver at host %s. Device is unavailable",
self._receiver.host,
)
self._available = False
except AvrNetworkError:
available = False
if self._available is True:
_LOGGER.warning(
"Network error connecting to Denon AVR receiver at host %s. Device is unavailable",
self._receiver.host,
)
self._available = False
except AvrForbiddenError:
available = False
if self._available is True:
_LOGGER.warning(
"Denon AVR receiver at host %s responded with HTTP 403 error. Device is unavailable. Please consider power cycling your receiver",
self._receiver.host,
)
self._available = False
except AvrCommandError as err:
available = False
_LOGGER.error(
"Command %s failed with error: %s",
func.__name__,
err,
)
except DenonAvrError as err:
available = False
_LOGGER.error(
"Error %s occurred in method %s for Denon AVR receiver",
err,
func.__name__,
exc_info=True,
)
finally:
if available is True and self._available is False:
_LOGGER.info(
"Denon AVR receiver at host %s is available again",
self._receiver.host,
)
self._available = True
return wrapper
@async_log_errors @async_log_errors
async def async_update(self) -> None: async def async_update(self) -> None:
"""Get the latest status information from device.""" """Get the latest status information from device."""
@ -466,8 +481,3 @@ class DenonDevice(MediaPlayerEntity):
if self._update_audyssey: if self._update_audyssey:
await self._receiver.async_update_audyssey() await self._receiver.async_update_audyssey()
# Decorator defined before is a staticmethod
async_log_errors = staticmethod( # pylint: disable=no-staticmethod-decorator
async_log_errors
)

View file

@ -23,12 +23,12 @@ class ConnectDenonAVR:
) -> None: ) -> None:
"""Initialize the class.""" """Initialize the class."""
self._async_client_getter = async_client_getter self._async_client_getter = async_client_getter
self._receiver = None self._receiver: DenonAVR | None = None
self._host = host self._host = host
self._show_all_inputs = show_all_inputs self._show_all_inputs = show_all_inputs
self._timeout = timeout self._timeout = timeout
self._zones = {} self._zones: dict[str, str | None] = {}
if zone2: if zone2:
self._zones["Zone2"] = None self._zones["Zone2"] = None
if zone3: if zone3:
@ -42,6 +42,7 @@ class ConnectDenonAVR:
async def async_connect_receiver(self) -> bool: async def async_connect_receiver(self) -> bool:
"""Connect to the DenonAVR receiver.""" """Connect to the DenonAVR receiver."""
await self.async_init_receiver_class() await self.async_init_receiver_class()
assert self._receiver
if ( if (
self._receiver.manufacturer is None self._receiver.manufacturer is None
@ -70,7 +71,7 @@ class ConnectDenonAVR:
return True return True
async def async_init_receiver_class(self) -> bool: async def async_init_receiver_class(self) -> None:
"""Initialize the DenonAVR class asynchronously.""" """Initialize the DenonAVR class asynchronously."""
receiver = DenonAVR( receiver = DenonAVR(
host=self._host, host=self._host,

View file

@ -2644,15 +2644,6 @@ ignore_errors = true
[mypy-homeassistant.components.conversation.default_agent] [mypy-homeassistant.components.conversation.default_agent]
ignore_errors = true ignore_errors = true
[mypy-homeassistant.components.denonavr.config_flow]
ignore_errors = true
[mypy-homeassistant.components.denonavr.media_player]
ignore_errors = true
[mypy-homeassistant.components.denonavr.receiver]
ignore_errors = true
[mypy-homeassistant.components.evohome] [mypy-homeassistant.components.evohome]
ignore_errors = true ignore_errors = true

View file

@ -23,9 +23,6 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.cloud.http_api", "homeassistant.components.cloud.http_api",
"homeassistant.components.conversation", "homeassistant.components.conversation",
"homeassistant.components.conversation.default_agent", "homeassistant.components.conversation.default_agent",
"homeassistant.components.denonavr.config_flow",
"homeassistant.components.denonavr.media_player",
"homeassistant.components.denonavr.receiver",
"homeassistant.components.evohome", "homeassistant.components.evohome",
"homeassistant.components.evohome.climate", "homeassistant.components.evohome.climate",
"homeassistant.components.evohome.water_heater", "homeassistant.components.evohome.water_heater",