mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Rename and deprecate exception classes (#739)
# Public # SmartDeviceException -> KasaException UnsupportedDeviceException(SmartDeviceException) -> UnsupportedDeviceError(KasaException) TimeoutException(SmartDeviceException, asyncio.TimeoutError) -> TimeoutError(KasaException, asyncio.TimeoutError) Add new exception for error codes -> DeviceError(KasaException) AuthenticationException(SmartDeviceException) -> AuthenticationError(DeviceError) # Internal # RetryableException(SmartDeviceException) -> _RetryableError(DeviceError) ConnectionException(SmartDeviceException) -> _ConnectionError(KasaException)
This commit is contained in:
parent
4beff228c9
commit
8c39e81a40
@ -22,12 +22,12 @@ import asyncclick as click
|
||||
|
||||
from devtools.helpers.smartrequests import SmartRequest, get_component_requests
|
||||
from kasa import (
|
||||
AuthenticationException,
|
||||
AuthenticationError,
|
||||
Credentials,
|
||||
Device,
|
||||
Discover,
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
)
|
||||
from kasa.discover import DiscoveryResult
|
||||
from kasa.exceptions import SmartErrorCode
|
||||
@ -303,19 +303,16 @@ async def _make_requests_or_exit(
|
||||
for method, result in responses.items():
|
||||
final[method] = result
|
||||
return final
|
||||
except AuthenticationException as ex:
|
||||
except AuthenticationError as ex:
|
||||
_echo_error(
|
||||
f"Unable to query the device due to an authentication error: {ex}",
|
||||
)
|
||||
exit(1)
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
_echo_error(
|
||||
f"Unable to query {name} at once: {ex}",
|
||||
)
|
||||
if (
|
||||
isinstance(ex, TimeoutException)
|
||||
or ex.error_code == SmartErrorCode.SESSION_TIMEOUT_ERROR
|
||||
):
|
||||
if isinstance(ex, TimeoutError):
|
||||
_echo_error(
|
||||
"Timeout, try reducing the batch size via --batch-size option.",
|
||||
)
|
||||
@ -400,7 +397,7 @@ async def get_smart_fixture(device: SmartDevice, batch_size: int):
|
||||
response = await device.protocol.query(
|
||||
SmartRequest._create_request_dict(test_call.request)
|
||||
)
|
||||
except AuthenticationException as ex:
|
||||
except AuthenticationError as ex:
|
||||
_echo_error(
|
||||
f"Unable to query the device due to an authentication error: {ex}",
|
||||
)
|
||||
|
@ -100,6 +100,17 @@ The classes providing this functionality are:
|
||||
- :class:`KlapTransport <kasa.klaptransport.KlapTransport>`
|
||||
- :class:`KlapTransportV2 <kasa.klaptransport.KlapTransportV2>`
|
||||
|
||||
Errors and Exceptions
|
||||
*********************
|
||||
|
||||
The base exception for all library errors is :class:`KasaException <kasa.exceptions.KasaException>`.
|
||||
|
||||
- If the device returns an error the library raises a :class:`DeviceError <kasa.exceptions.DeviceError>` which will usually contain an ``error_code`` with the detail.
|
||||
- If the device fails to authenticate the library raises an :class:`AuthenticationError <kasa.exceptions.AuthenticationError>` which is derived
|
||||
from :class:`DeviceError <kasa.exceptions.DeviceError>` and could contain an ``error_code`` depending on the type of failure.
|
||||
- If the library encounters and unsupported deviceit raises an :class:`UnsupportedDeviceError <kasa.exceptions.UnsupportedDeviceError>`.
|
||||
- If the device fails to respond within a timeout the library raises a :class:`TimeoutError <kasa.exceptions.TimeoutError>`.
|
||||
- All other failures will raise the base :class:`KasaException <kasa.exceptions.KasaException>` class.
|
||||
|
||||
API documentation for modules
|
||||
*****************************
|
||||
@ -154,3 +165,26 @@ API documentation for protocols and transports
|
||||
:members:
|
||||
:inherited-members:
|
||||
:undoc-members:
|
||||
|
||||
API documentation for errors and exceptions
|
||||
*******************************************
|
||||
|
||||
.. autoclass:: kasa.exceptions.KasaException
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: kasa.exceptions.DeviceError
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: kasa.exceptions.AuthenticationError
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: kasa.exceptions.UnsupportedDeviceError
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: kasa.exceptions.TimeoutError
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
@ -24,7 +24,7 @@ Methods changing the state of the device do not invalidate the cache (i.e., ther
|
||||
You can assume that the operation has succeeded if no exception is raised.
|
||||
These methods will return the device response, which can be useful for some use cases.
|
||||
|
||||
Errors are raised as :class:`SmartDeviceException` instances for the library user to handle.
|
||||
Errors are raised as :class:`KasaException` instances for the library user to handle.
|
||||
|
||||
Simple example script showing some functionality for legacy devices:
|
||||
|
||||
@ -154,15 +154,3 @@ API documentation
|
||||
.. autoclass:: Credentials
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: SmartDeviceException
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: AuthenticationException
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: UnsupportedDeviceException
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
@ -8,7 +8,7 @@ All common, shared functionalities are available through `SmartDevice` class::
|
||||
For device type specific actions `SmartBulb`, `SmartPlug`, or `SmartStrip`
|
||||
should be used instead.
|
||||
|
||||
Module-specific errors are raised as `SmartDeviceException` and are expected
|
||||
Module-specific errors are raised as `KasaException` and are expected
|
||||
to be handled by the user of the library.
|
||||
"""
|
||||
from importlib.metadata import version
|
||||
@ -28,10 +28,11 @@ from kasa.deviceconfig import (
|
||||
from kasa.discover import Discover
|
||||
from kasa.emeterstatus import EmeterStatus
|
||||
from kasa.exceptions import (
|
||||
AuthenticationException,
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
UnsupportedDeviceException,
|
||||
AuthenticationError,
|
||||
DeviceError,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
UnsupportedDeviceError,
|
||||
)
|
||||
from kasa.feature import Feature, FeatureType
|
||||
from kasa.iot.iotbulb import BulbPreset, TurnOnBehavior, TurnOnBehaviors
|
||||
@ -61,10 +62,11 @@ __all__ = [
|
||||
"Device",
|
||||
"Bulb",
|
||||
"Plug",
|
||||
"SmartDeviceException",
|
||||
"AuthenticationException",
|
||||
"UnsupportedDeviceException",
|
||||
"TimeoutException",
|
||||
"KasaException",
|
||||
"AuthenticationError",
|
||||
"DeviceError",
|
||||
"UnsupportedDeviceError",
|
||||
"TimeoutError",
|
||||
"Credentials",
|
||||
"DeviceConfig",
|
||||
"ConnectionType",
|
||||
@ -84,6 +86,12 @@ deprecated_smart_devices = {
|
||||
"SmartDimmer": iot.IotDimmer,
|
||||
"SmartBulbPreset": BulbPreset,
|
||||
}
|
||||
deprecated_exceptions = {
|
||||
"SmartDeviceException": KasaException,
|
||||
"UnsupportedDeviceException": UnsupportedDeviceError,
|
||||
"AuthenticationException": AuthenticationError,
|
||||
"TimeoutException": TimeoutError,
|
||||
}
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
@ -101,6 +109,11 @@ def __getattr__(name):
|
||||
stacklevel=1,
|
||||
)
|
||||
return new_class
|
||||
if name in deprecated_exceptions:
|
||||
new_class = deprecated_exceptions[name]
|
||||
msg = f"{name} is deprecated, use {new_class.__name__} instead"
|
||||
warn(msg, DeprecationWarning, stacklevel=1)
|
||||
return new_class
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
|
||||
@ -112,6 +125,11 @@ if TYPE_CHECKING:
|
||||
SmartStrip = iot.IotStrip
|
||||
SmartDimmer = iot.IotDimmer
|
||||
SmartBulbPreset = BulbPreset
|
||||
|
||||
SmartDeviceException = KasaException
|
||||
UnsupportedDeviceException = UnsupportedDeviceError
|
||||
AuthenticationException = AuthenticationError
|
||||
TimeoutException = TimeoutError
|
||||
# Instanstiate all classes so the type checkers catch abstract issues
|
||||
from . import smart
|
||||
|
||||
|
@ -22,12 +22,11 @@ from .deviceconfig import DeviceConfig
|
||||
from .exceptions import (
|
||||
SMART_AUTHENTICATION_ERRORS,
|
||||
SMART_RETRYABLE_ERRORS,
|
||||
SMART_TIMEOUT_ERRORS,
|
||||
AuthenticationException,
|
||||
RetryableException,
|
||||
SmartDeviceException,
|
||||
AuthenticationError,
|
||||
DeviceError,
|
||||
KasaException,
|
||||
SmartErrorCode,
|
||||
TimeoutException,
|
||||
_RetryableError,
|
||||
)
|
||||
from .httpclient import HttpClient
|
||||
from .json import dumps as json_dumps
|
||||
@ -141,14 +140,12 @@ class AesTransport(BaseTransport):
|
||||
if error_code == SmartErrorCode.SUCCESS:
|
||||
return
|
||||
msg = f"{msg}: {self._host}: {error_code.name}({error_code.value})"
|
||||
if error_code in SMART_TIMEOUT_ERRORS:
|
||||
raise TimeoutException(msg, error_code=error_code)
|
||||
if error_code in SMART_RETRYABLE_ERRORS:
|
||||
raise RetryableException(msg, error_code=error_code)
|
||||
raise _RetryableError(msg, error_code=error_code)
|
||||
if error_code in SMART_AUTHENTICATION_ERRORS:
|
||||
self._state = TransportState.HANDSHAKE_REQUIRED
|
||||
raise AuthenticationException(msg, error_code=error_code)
|
||||
raise SmartDeviceException(msg, error_code=error_code)
|
||||
raise AuthenticationError(msg, error_code=error_code)
|
||||
raise DeviceError(msg, error_code=error_code)
|
||||
|
||||
async def send_secure_passthrough(self, request: str) -> Dict[str, Any]:
|
||||
"""Send encrypted message as passthrough."""
|
||||
@ -171,7 +168,7 @@ class AesTransport(BaseTransport):
|
||||
# _LOGGER.debug(f"secure_passthrough response is {status_code}: {resp_dict}")
|
||||
|
||||
if status_code != 200:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"{self._host} responded with an unexpected "
|
||||
+ f"status code {status_code} to passthrough"
|
||||
)
|
||||
@ -197,7 +194,7 @@ class AesTransport(BaseTransport):
|
||||
self._host,
|
||||
)
|
||||
except Exception:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Unable to decrypt response from {self._host}, "
|
||||
+ f"error: {ex}, response: {raw_response}",
|
||||
ex,
|
||||
@ -208,7 +205,7 @@ class AesTransport(BaseTransport):
|
||||
"""Login to the device."""
|
||||
try:
|
||||
await self.try_login(self._login_params)
|
||||
except AuthenticationException as aex:
|
||||
except AuthenticationError as aex:
|
||||
try:
|
||||
if aex.error_code is not SmartErrorCode.LOGIN_ERROR:
|
||||
raise aex
|
||||
@ -223,10 +220,10 @@ class AesTransport(BaseTransport):
|
||||
"%s: logged in with default credentials",
|
||||
self._host,
|
||||
)
|
||||
except AuthenticationException:
|
||||
except AuthenticationError:
|
||||
raise
|
||||
except Exception as ex:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
"Unable to login and trying default "
|
||||
+ f"login raised another exception: {ex}",
|
||||
ex,
|
||||
@ -292,7 +289,7 @@ class AesTransport(BaseTransport):
|
||||
_LOGGER.debug("Device responded with: %s", resp_dict)
|
||||
|
||||
if status_code != 200:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"{self._host} responded with an unexpected "
|
||||
+ f"status code {status_code} to handshake"
|
||||
)
|
||||
@ -347,7 +344,7 @@ class AesTransport(BaseTransport):
|
||||
await self.perform_login()
|
||||
# After a login failure handshake needs to
|
||||
# be redone or a 9999 error is received.
|
||||
except AuthenticationException as ex:
|
||||
except AuthenticationError as ex:
|
||||
self._state = TransportState.HANDSHAKE_REQUIRED
|
||||
raise ex
|
||||
|
||||
|
12
kasa/cli.py
12
kasa/cli.py
@ -13,7 +13,7 @@ from typing import Any, Dict, cast
|
||||
import asyncclick as click
|
||||
|
||||
from kasa import (
|
||||
AuthenticationException,
|
||||
AuthenticationError,
|
||||
Bulb,
|
||||
ConnectionType,
|
||||
Credentials,
|
||||
@ -22,8 +22,8 @@ from kasa import (
|
||||
DeviceFamilyType,
|
||||
Discover,
|
||||
EncryptType,
|
||||
SmartDeviceException,
|
||||
UnsupportedDeviceException,
|
||||
KasaException,
|
||||
UnsupportedDeviceError,
|
||||
)
|
||||
from kasa.discover import DiscoveryResult
|
||||
from kasa.iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip
|
||||
@ -458,7 +458,7 @@ async def discover(ctx):
|
||||
unsupported = []
|
||||
auth_failed = []
|
||||
|
||||
async def print_unsupported(unsupported_exception: UnsupportedDeviceException):
|
||||
async def print_unsupported(unsupported_exception: UnsupportedDeviceError):
|
||||
unsupported.append(unsupported_exception)
|
||||
async with sem:
|
||||
if unsupported_exception.discovery_result:
|
||||
@ -476,7 +476,7 @@ async def discover(ctx):
|
||||
async with sem:
|
||||
try:
|
||||
await dev.update()
|
||||
except AuthenticationException:
|
||||
except AuthenticationError:
|
||||
auth_failed.append(dev._discovery_info)
|
||||
echo("== Authentication failed for device ==")
|
||||
_echo_discovery_info(dev._discovery_info)
|
||||
@ -677,7 +677,7 @@ async def cmd_command(dev: Device, module, command, parameters):
|
||||
elif isinstance(dev, SmartDevice):
|
||||
res = await dev._query_helper(command, parameters)
|
||||
else:
|
||||
raise SmartDeviceException("Unexpected device type %s.", dev)
|
||||
raise KasaException("Unexpected device type %s.", dev)
|
||||
echo(json.dumps(res))
|
||||
return res
|
||||
|
||||
|
@ -9,7 +9,7 @@ from .credentials import Credentials
|
||||
from .device_type import DeviceType
|
||||
from .deviceconfig import DeviceConfig
|
||||
from .emeterstatus import EmeterStatus
|
||||
from .exceptions import SmartDeviceException
|
||||
from .exceptions import KasaException
|
||||
from .feature import Feature
|
||||
from .iotprotocol import IotProtocol
|
||||
from .protocol import BaseProtocol
|
||||
@ -242,12 +242,12 @@ class Device(ABC):
|
||||
if p.alias == name:
|
||||
return p
|
||||
|
||||
raise SmartDeviceException(f"Device has no child with {name}")
|
||||
raise KasaException(f"Device has no child with {name}")
|
||||
|
||||
def get_plug_by_index(self, index: int) -> "Device":
|
||||
"""Return child device for the given index."""
|
||||
if index + 1 > len(self.children) or index < 0:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Invalid index {index}, device has {len(self.children)} plugs"
|
||||
)
|
||||
return self.children[index]
|
||||
@ -306,7 +306,7 @@ class Device(ABC):
|
||||
"""Add a new feature to the device."""
|
||||
desc_name = feature.name.lower().replace(" ", "_")
|
||||
if desc_name in self._features:
|
||||
raise SmartDeviceException("Duplicate feature name %s" % desc_name)
|
||||
raise KasaException("Duplicate feature name %s" % desc_name)
|
||||
self._features[desc_name] = feature
|
||||
|
||||
@property
|
||||
|
@ -6,7 +6,7 @@ from typing import Any, Dict, Optional, Tuple, Type
|
||||
from .aestransport import AesTransport
|
||||
from .device import Device
|
||||
from .deviceconfig import DeviceConfig
|
||||
from .exceptions import SmartDeviceException, UnsupportedDeviceException
|
||||
from .exceptions import KasaException, UnsupportedDeviceError
|
||||
from .iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip
|
||||
from .iotprotocol import IotProtocol
|
||||
from .klaptransport import KlapTransport, KlapTransportV2
|
||||
@ -45,12 +45,12 @@ async def connect(*, host: Optional[str] = None, config: DeviceConfig) -> "Devic
|
||||
:return: Object for querying/controlling found device.
|
||||
"""
|
||||
if host and config or (not host and not config):
|
||||
raise SmartDeviceException("One of host or config must be provded and not both")
|
||||
raise KasaException("One of host or config must be provded and not both")
|
||||
if host:
|
||||
config = DeviceConfig(host=host)
|
||||
|
||||
if (protocol := get_protocol(config=config)) is None:
|
||||
raise UnsupportedDeviceException(
|
||||
raise UnsupportedDeviceError(
|
||||
f"Unsupported device for {config.host}: "
|
||||
+ f"{config.connection_type.device_family.value}"
|
||||
)
|
||||
@ -99,7 +99,7 @@ async def _connect(config: DeviceConfig, protocol: BaseProtocol) -> "Device":
|
||||
_perf_log(True, "update")
|
||||
return device
|
||||
else:
|
||||
raise UnsupportedDeviceException(
|
||||
raise UnsupportedDeviceError(
|
||||
f"Unsupported device for {config.host}: "
|
||||
+ f"{config.connection_type.device_family.value}"
|
||||
)
|
||||
@ -108,12 +108,12 @@ async def _connect(config: DeviceConfig, protocol: BaseProtocol) -> "Device":
|
||||
def get_device_class_from_sys_info(info: Dict[str, Any]) -> Type[IotDevice]:
|
||||
"""Find SmartDevice subclass for device described by passed data."""
|
||||
if "system" not in info or "get_sysinfo" not in info["system"]:
|
||||
raise SmartDeviceException("No 'system' or 'get_sysinfo' in response")
|
||||
raise KasaException("No 'system' or 'get_sysinfo' in response")
|
||||
|
||||
sysinfo: Dict[str, Any] = info["system"]["get_sysinfo"]
|
||||
type_: Optional[str] = sysinfo.get("type", sysinfo.get("mic_type"))
|
||||
if type_ is None:
|
||||
raise SmartDeviceException("Unable to find the device type field!")
|
||||
raise KasaException("Unable to find the device type field!")
|
||||
|
||||
if "dev_name" in sysinfo and "Dimmer" in sysinfo["dev_name"]:
|
||||
return IotDimmer
|
||||
@ -129,7 +129,7 @@ def get_device_class_from_sys_info(info: Dict[str, Any]) -> Type[IotDevice]:
|
||||
return IotLightStrip
|
||||
|
||||
return IotBulb
|
||||
raise UnsupportedDeviceException("Unknown device type: %s" % type_)
|
||||
raise UnsupportedDeviceError("Unknown device type: %s" % type_)
|
||||
|
||||
|
||||
def get_device_class_from_family(device_type: str) -> Optional[Type[Device]]:
|
||||
|
@ -5,7 +5,7 @@ from enum import Enum
|
||||
from typing import TYPE_CHECKING, Dict, Optional, Union
|
||||
|
||||
from .credentials import Credentials
|
||||
from .exceptions import SmartDeviceException
|
||||
from .exceptions import KasaException
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from aiohttp import ClientSession
|
||||
@ -46,7 +46,7 @@ def _dataclass_from_dict(klass, in_val):
|
||||
fieldtypes[dict_key], in_val[dict_key]
|
||||
)
|
||||
else:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Cannot create dataclass from dict, unknown key: {dict_key}"
|
||||
)
|
||||
return klass(**val)
|
||||
@ -92,7 +92,7 @@ class ConnectionType:
|
||||
login_version,
|
||||
)
|
||||
except (ValueError, TypeError) as ex:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Invalid connection parameters for {device_family}."
|
||||
+ f"{encryption_type}.{login_version}"
|
||||
) from ex
|
||||
@ -113,9 +113,7 @@ class ConnectionType:
|
||||
login_version, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
raise SmartDeviceException(
|
||||
f"Invalid connection type data for {connection_type_dict}"
|
||||
)
|
||||
raise KasaException(f"Invalid connection type data for {connection_type_dict}")
|
||||
|
||||
def to_dict(self) -> Dict[str, Union[str, int]]:
|
||||
"""Convert connection params to dict."""
|
||||
@ -185,4 +183,4 @@ class DeviceConfig:
|
||||
"""Return device config from dict."""
|
||||
if isinstance(config_dict, dict):
|
||||
return _dataclass_from_dict(DeviceConfig, config_dict)
|
||||
raise SmartDeviceException(f"Invalid device config data: {config_dict}")
|
||||
raise KasaException(f"Invalid device config data: {config_dict}")
|
||||
|
@ -24,9 +24,9 @@ from kasa.device_factory import (
|
||||
)
|
||||
from kasa.deviceconfig import ConnectionType, DeviceConfig, EncryptType
|
||||
from kasa.exceptions import (
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
UnsupportedDeviceException,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
UnsupportedDeviceError,
|
||||
)
|
||||
from kasa.iot.iotdevice import IotDevice
|
||||
from kasa.json import dumps as json_dumps
|
||||
@ -59,7 +59,7 @@ class _DiscoverProtocol(asyncio.DatagramProtocol):
|
||||
discovery_timeout: int = 5,
|
||||
interface: Optional[str] = None,
|
||||
on_unsupported: Optional[
|
||||
Callable[[UnsupportedDeviceException], Awaitable[None]]
|
||||
Callable[[UnsupportedDeviceError], Awaitable[None]]
|
||||
] = None,
|
||||
port: Optional[int] = None,
|
||||
credentials: Optional[Credentials] = None,
|
||||
@ -162,14 +162,14 @@ class _DiscoverProtocol(asyncio.DatagramProtocol):
|
||||
device = Discover._get_device_instance(data, config)
|
||||
else:
|
||||
return
|
||||
except UnsupportedDeviceException as udex:
|
||||
except UnsupportedDeviceError as udex:
|
||||
_LOGGER.debug("Unsupported device found at %s << %s", ip, udex)
|
||||
self.unsupported_device_exceptions[ip] = udex
|
||||
if self.on_unsupported is not None:
|
||||
self._run_callback_task(self.on_unsupported(udex))
|
||||
self._handle_discovered_event()
|
||||
return
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
_LOGGER.debug(f"[DISCOVERY] Unable to find device type for {ip}: {ex}")
|
||||
self.invalid_device_exceptions[ip] = ex
|
||||
self._handle_discovered_event()
|
||||
@ -311,7 +311,7 @@ class Discover:
|
||||
try:
|
||||
_LOGGER.debug("Waiting %s seconds for responses...", discovery_timeout)
|
||||
await protocol.wait_for_discovery_to_complete()
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
for device in protocol.discovered_devices.values():
|
||||
await device.protocol.close()
|
||||
raise ex
|
||||
@ -368,9 +368,7 @@ class Discover:
|
||||
# https://docs.python.org/3/library/socket.html#socket.getaddrinfo
|
||||
ip = adrrinfo[0][4][0]
|
||||
except socket.gaierror as gex:
|
||||
raise SmartDeviceException(
|
||||
f"Could not resolve hostname {host}"
|
||||
) from gex
|
||||
raise KasaException(f"Could not resolve hostname {host}") from gex
|
||||
|
||||
transport, protocol = await loop.create_datagram_endpoint(
|
||||
lambda: _DiscoverProtocol(
|
||||
@ -401,7 +399,7 @@ class Discover:
|
||||
elif ip in protocol.invalid_device_exceptions:
|
||||
raise protocol.invalid_device_exceptions[ip]
|
||||
else:
|
||||
raise TimeoutException(f"Timed out getting discovery response for {host}")
|
||||
raise TimeoutError(f"Timed out getting discovery response for {host}")
|
||||
|
||||
@staticmethod
|
||||
def _get_device_class(info: dict) -> Type[Device]:
|
||||
@ -410,7 +408,7 @@ class Discover:
|
||||
discovery_result = DiscoveryResult(**info["result"])
|
||||
dev_class = get_device_class_from_family(discovery_result.device_type)
|
||||
if not dev_class:
|
||||
raise UnsupportedDeviceException(
|
||||
raise UnsupportedDeviceError(
|
||||
"Unknown device type: %s" % discovery_result.device_type,
|
||||
discovery_result=info,
|
||||
)
|
||||
@ -424,7 +422,7 @@ class Discover:
|
||||
try:
|
||||
info = json_loads(XorEncryption.decrypt(data))
|
||||
except Exception as ex:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Unable to read response from device: {config.host}: {ex}"
|
||||
) from ex
|
||||
|
||||
@ -451,7 +449,7 @@ class Discover:
|
||||
info = json_loads(data[16:])
|
||||
except Exception as ex:
|
||||
_LOGGER.debug("Got invalid response from device %s: %s", config.host, data)
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Unable to read response from device: {config.host}: {ex}"
|
||||
) from ex
|
||||
try:
|
||||
@ -460,7 +458,7 @@ class Discover:
|
||||
_LOGGER.debug(
|
||||
"Unable to parse discovery from device %s: %s", config.host, info
|
||||
)
|
||||
raise UnsupportedDeviceException(
|
||||
raise UnsupportedDeviceError(
|
||||
f"Unable to parse discovery from device: {config.host}: {ex}"
|
||||
) from ex
|
||||
|
||||
@ -472,15 +470,15 @@ class Discover:
|
||||
discovery_result.mgt_encrypt_schm.encrypt_type,
|
||||
discovery_result.mgt_encrypt_schm.lv,
|
||||
)
|
||||
except SmartDeviceException as ex:
|
||||
raise UnsupportedDeviceException(
|
||||
except KasaException as ex:
|
||||
raise UnsupportedDeviceError(
|
||||
f"Unsupported device {config.host} of type {type_} "
|
||||
+ f"with encrypt_type {discovery_result.mgt_encrypt_schm.encrypt_type}",
|
||||
discovery_result=discovery_result.get_dict(),
|
||||
) from ex
|
||||
if (device_class := get_device_class_from_family(type_)) is None:
|
||||
_LOGGER.warning("Got unsupported device type: %s", type_)
|
||||
raise UnsupportedDeviceException(
|
||||
raise UnsupportedDeviceError(
|
||||
f"Unsupported device {config.host} of type {type_}: {info}",
|
||||
discovery_result=discovery_result.get_dict(),
|
||||
)
|
||||
@ -488,7 +486,7 @@ class Discover:
|
||||
_LOGGER.warning(
|
||||
"Got unsupported connection type: %s", config.connection_type.to_dict()
|
||||
)
|
||||
raise UnsupportedDeviceException(
|
||||
raise UnsupportedDeviceError(
|
||||
f"Unsupported encryption scheme {config.host} of "
|
||||
+ f"type {config.connection_type.to_dict()}: {info}",
|
||||
discovery_result=discovery_result.get_dict(),
|
||||
|
@ -1,47 +1,59 @@
|
||||
"""python-kasa exceptions."""
|
||||
from asyncio import TimeoutError
|
||||
from asyncio import TimeoutError as _asyncioTimeoutError
|
||||
from enum import IntEnum
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class SmartDeviceException(Exception):
|
||||
class KasaException(Exception):
|
||||
"""Base exception for library errors."""
|
||||
|
||||
|
||||
class TimeoutError(KasaException, _asyncioTimeoutError):
|
||||
"""Timeout exception for device errors."""
|
||||
|
||||
def __repr__(self):
|
||||
return KasaException.__repr__(self)
|
||||
|
||||
def __str__(self):
|
||||
return KasaException.__str__(self)
|
||||
|
||||
|
||||
class _ConnectionError(KasaException):
|
||||
"""Connection exception for device errors."""
|
||||
|
||||
|
||||
class UnsupportedDeviceError(KasaException):
|
||||
"""Exception for trying to connect to unsupported devices."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
self.discovery_result = kwargs.get("discovery_result")
|
||||
super().__init__(*args)
|
||||
|
||||
|
||||
class DeviceError(KasaException):
|
||||
"""Base exception for device errors."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
self.error_code: Optional["SmartErrorCode"] = kwargs.get("error_code", None)
|
||||
super().__init__(*args)
|
||||
|
||||
def __repr__(self):
|
||||
err_code = self.error_code.__repr__() if self.error_code else ""
|
||||
return f"{self.__class__.__name__}({err_code})"
|
||||
|
||||
class UnsupportedDeviceException(SmartDeviceException):
|
||||
"""Exception for trying to connect to unsupported devices."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
self.discovery_result = kwargs.get("discovery_result")
|
||||
super().__init__(*args, **kwargs)
|
||||
def __str__(self):
|
||||
err_code = f" (error_code={self.error_code.name})" if self.error_code else ""
|
||||
return super().__str__() + err_code
|
||||
|
||||
|
||||
class AuthenticationException(SmartDeviceException):
|
||||
class AuthenticationError(DeviceError):
|
||||
"""Base exception for device authentication errors."""
|
||||
|
||||
|
||||
class RetryableException(SmartDeviceException):
|
||||
class _RetryableError(DeviceError):
|
||||
"""Retryable exception for device errors."""
|
||||
|
||||
|
||||
class TimeoutException(SmartDeviceException, TimeoutError):
|
||||
"""Timeout exception for device errors."""
|
||||
|
||||
def __repr__(self):
|
||||
return SmartDeviceException.__repr__(self)
|
||||
|
||||
def __str__(self):
|
||||
return SmartDeviceException.__str__(self)
|
||||
|
||||
|
||||
class ConnectionException(SmartDeviceException):
|
||||
"""Connection exception for device errors."""
|
||||
|
||||
|
||||
class SmartErrorCode(IntEnum):
|
||||
"""Enum for SMART Error Codes."""
|
||||
|
||||
@ -109,6 +121,7 @@ SMART_RETRYABLE_ERRORS = [
|
||||
SmartErrorCode.TRANSPORT_NOT_AVAILABLE_ERROR,
|
||||
SmartErrorCode.HTTP_TRANSPORT_FAILED_ERROR,
|
||||
SmartErrorCode.UNSPECIFIC_ERROR,
|
||||
SmartErrorCode.SESSION_TIMEOUT_ERROR,
|
||||
]
|
||||
|
||||
SMART_AUTHENTICATION_ERRORS = [
|
||||
@ -118,7 +131,3 @@ SMART_AUTHENTICATION_ERRORS = [
|
||||
SmartErrorCode.HAND_SHAKE_FAILED_ERROR,
|
||||
SmartErrorCode.TRANSPORT_UNKNOWN_CREDENTIALS_ERROR,
|
||||
]
|
||||
|
||||
SMART_TIMEOUT_ERRORS = [
|
||||
SmartErrorCode.SESSION_TIMEOUT_ERROR,
|
||||
]
|
||||
|
@ -8,9 +8,9 @@ from yarl import URL
|
||||
|
||||
from .deviceconfig import DeviceConfig
|
||||
from .exceptions import (
|
||||
ConnectionException,
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
_ConnectionError,
|
||||
)
|
||||
from .json import loads as json_loads
|
||||
|
||||
@ -86,17 +86,17 @@ class HttpClient:
|
||||
response_data = json_loads(response_data.decode())
|
||||
|
||||
except (aiohttp.ServerDisconnectedError, aiohttp.ClientOSError) as ex:
|
||||
raise ConnectionException(
|
||||
raise _ConnectionError(
|
||||
f"Device connection error: {self._config.host}: {ex}", ex
|
||||
) from ex
|
||||
except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as ex:
|
||||
raise TimeoutException(
|
||||
raise TimeoutError(
|
||||
"Unable to query the device, "
|
||||
+ f"timed out: {self._config.host}: {ex}",
|
||||
ex,
|
||||
) from ex
|
||||
except Exception as ex:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Unable to query the device: {self._config.host}: {ex}", ex
|
||||
) from ex
|
||||
|
||||
|
@ -13,7 +13,7 @@ from ..bulb import HSV, Bulb, BulbPreset, ColorTempRange
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..protocol import BaseProtocol
|
||||
from .iotdevice import IotDevice, SmartDeviceException, requires_update
|
||||
from .iotdevice import IotDevice, KasaException, requires_update
|
||||
from .modules import Antitheft, Cloud, Countdown, Emeter, Schedule, Time, Usage
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
so you must await :func:`update()` to fetch updates values from the device.
|
||||
|
||||
Errors reported by the device are raised as
|
||||
:class:`SmartDeviceExceptions <kasa.exceptions.SmartDeviceException>`,
|
||||
:class:`KasaException <kasa.exceptions.KasaException>`,
|
||||
and should be handled by the user of the library.
|
||||
|
||||
Examples:
|
||||
@ -233,7 +233,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
:return: White temperature range in Kelvin (minimum, maximum)
|
||||
"""
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Color temperature not supported")
|
||||
raise KasaException("Color temperature not supported")
|
||||
|
||||
for model, temp_range in TPLINK_KELVIN.items():
|
||||
sys_info = self.sys_info
|
||||
@ -249,7 +249,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
"""Query the light state."""
|
||||
light_state = self.sys_info["light_state"]
|
||||
if light_state is None:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
"The device has no light_state or you have not called update()"
|
||||
)
|
||||
|
||||
@ -333,7 +333,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
:return: hue, saturation and value (degrees, %, %)
|
||||
"""
|
||||
if not self.is_color:
|
||||
raise SmartDeviceException("Bulb does not support color.")
|
||||
raise KasaException("Bulb does not support color.")
|
||||
|
||||
light_state = cast(dict, self.light_state)
|
||||
|
||||
@ -360,7 +360,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
if not self.is_color:
|
||||
raise SmartDeviceException("Bulb does not support color.")
|
||||
raise KasaException("Bulb does not support color.")
|
||||
|
||||
if not isinstance(hue, int) or not (0 <= hue <= 360):
|
||||
raise ValueError(f"Invalid hue value: {hue} (valid range: 0-360)")
|
||||
@ -387,7 +387,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
def color_temp(self) -> int:
|
||||
"""Return color temperature of the device in kelvin."""
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Bulb does not support colortemp.")
|
||||
raise KasaException("Bulb does not support colortemp.")
|
||||
|
||||
light_state = self.light_state
|
||||
return int(light_state["color_temp"])
|
||||
@ -402,7 +402,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Bulb does not support colortemp.")
|
||||
raise KasaException("Bulb does not support colortemp.")
|
||||
|
||||
valid_temperature_range = self.valid_temperature_range
|
||||
if temp < valid_temperature_range[0] or temp > valid_temperature_range[1]:
|
||||
@ -423,7 +423,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
def brightness(self) -> int:
|
||||
"""Return the current brightness in percentage."""
|
||||
if not self.is_dimmable: # pragma: no cover
|
||||
raise SmartDeviceException("Bulb is not dimmable.")
|
||||
raise KasaException("Bulb is not dimmable.")
|
||||
|
||||
light_state = self.light_state
|
||||
return int(light_state["brightness"])
|
||||
@ -438,7 +438,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
if not self.is_dimmable: # pragma: no cover
|
||||
raise SmartDeviceException("Bulb is not dimmable.")
|
||||
raise KasaException("Bulb is not dimmable.")
|
||||
|
||||
self._raise_for_invalid_brightness(brightness)
|
||||
|
||||
@ -511,10 +511,10 @@ class IotBulb(IotDevice, Bulb):
|
||||
obtained using :func:`presets`.
|
||||
"""
|
||||
if len(self.presets) == 0:
|
||||
raise SmartDeviceException("Device does not supported saving presets")
|
||||
raise KasaException("Device does not supported saving presets")
|
||||
|
||||
if preset.index >= len(self.presets):
|
||||
raise SmartDeviceException("Invalid preset index")
|
||||
raise KasaException("Invalid preset index")
|
||||
|
||||
return await self._query_helper(
|
||||
self.LIGHT_SERVICE, "set_preferred_state", preset.dict(exclude_none=True)
|
||||
|
@ -21,7 +21,7 @@ from typing import Any, Dict, List, Optional, Sequence, Set
|
||||
from ..device import Device, WifiNetwork
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..emeterstatus import EmeterStatus
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..exceptions import KasaException
|
||||
from ..feature import Feature
|
||||
from ..protocol import BaseProtocol
|
||||
from .iotmodule import IotModule
|
||||
@ -48,9 +48,7 @@ def requires_update(f):
|
||||
async def wrapped(*args, **kwargs):
|
||||
self = args[0]
|
||||
if self._last_update is None and f.__name__ not in self._sys_info:
|
||||
raise SmartDeviceException(
|
||||
"You need to await update() to access the data"
|
||||
)
|
||||
raise KasaException("You need to await update() to access the data")
|
||||
return await f(*args, **kwargs)
|
||||
|
||||
else:
|
||||
@ -59,9 +57,7 @@ def requires_update(f):
|
||||
def wrapped(*args, **kwargs):
|
||||
self = args[0]
|
||||
if self._last_update is None and f.__name__ not in self._sys_info:
|
||||
raise SmartDeviceException(
|
||||
"You need to await update() to access the data"
|
||||
)
|
||||
raise KasaException("You need to await update() to access the data")
|
||||
return f(*args, **kwargs)
|
||||
|
||||
f.requires_update = True
|
||||
@ -92,7 +88,8 @@ class IotDevice(Device):
|
||||
All changes to the device are done using awaitable methods,
|
||||
which will not change the cached values, but you must await update() separately.
|
||||
|
||||
Errors reported by the device are raised as SmartDeviceExceptions,
|
||||
Errors reported by the device are raised as
|
||||
:class:`KasaException <kasa.exceptions.KasaException>`,
|
||||
and should be handled by the user of the library.
|
||||
|
||||
Examples:
|
||||
@ -221,9 +218,9 @@ class IotDevice(Device):
|
||||
def _verify_emeter(self) -> None:
|
||||
"""Raise an exception if there is no emeter."""
|
||||
if not self.has_emeter:
|
||||
raise SmartDeviceException("Device has no emeter")
|
||||
raise KasaException("Device has no emeter")
|
||||
if self.emeter_type not in self._last_update:
|
||||
raise SmartDeviceException("update() required prior accessing emeter")
|
||||
raise KasaException("update() required prior accessing emeter")
|
||||
|
||||
async def _query_helper(
|
||||
self, target: str, cmd: str, arg: Optional[Dict] = None, child_ids=None
|
||||
@ -241,20 +238,20 @@ class IotDevice(Device):
|
||||
try:
|
||||
response = await self._raw_query(request=request)
|
||||
except Exception as ex:
|
||||
raise SmartDeviceException(f"Communication error on {target}:{cmd}") from ex
|
||||
raise KasaException(f"Communication error on {target}:{cmd}") from ex
|
||||
|
||||
if target not in response:
|
||||
raise SmartDeviceException(f"No required {target} in response: {response}")
|
||||
raise KasaException(f"No required {target} in response: {response}")
|
||||
|
||||
result = response[target]
|
||||
if "err_code" in result and result["err_code"] != 0:
|
||||
raise SmartDeviceException(f"Error on {target}.{cmd}: {result}")
|
||||
raise KasaException(f"Error on {target}.{cmd}: {result}")
|
||||
|
||||
if cmd not in result:
|
||||
raise SmartDeviceException(f"No command in response: {response}")
|
||||
raise KasaException(f"No command in response: {response}")
|
||||
result = result[cmd]
|
||||
if "err_code" in result and result["err_code"] != 0:
|
||||
raise SmartDeviceException(f"Error on {target} {cmd}: {result}")
|
||||
raise KasaException(f"Error on {target} {cmd}: {result}")
|
||||
|
||||
if "err_code" in result:
|
||||
del result["err_code"]
|
||||
@ -513,7 +510,7 @@ class IotDevice(Device):
|
||||
sys_info = self._sys_info
|
||||
mac = sys_info.get("mac", sys_info.get("mic_mac"))
|
||||
if not mac:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
"Unknown mac, please submit a bug report with sys_info output."
|
||||
)
|
||||
mac = mac.replace("-", ":")
|
||||
@ -656,14 +653,14 @@ class IotDevice(Device):
|
||||
|
||||
try:
|
||||
info = await _scan("netif")
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
_LOGGER.debug(
|
||||
"Unable to scan using 'netif', retrying with 'softaponboarding': %s", ex
|
||||
)
|
||||
info = await _scan("smartlife.iot.common.softaponboarding")
|
||||
|
||||
if "ap_list" not in info:
|
||||
raise SmartDeviceException("Invalid response for wifi scan: %s" % info)
|
||||
raise KasaException("Invalid response for wifi scan: %s" % info)
|
||||
|
||||
return [WifiNetwork(**x) for x in info["ap_list"]]
|
||||
|
||||
@ -679,7 +676,7 @@ class IotDevice(Device):
|
||||
payload = {"ssid": ssid, "password": password, "key_type": int(keytype)}
|
||||
try:
|
||||
return await _join("netif", payload)
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
_LOGGER.debug(
|
||||
"Unable to join using 'netif', retrying with 'softaponboarding': %s", ex
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ from typing import Any, Dict, Optional
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..protocol import BaseProtocol
|
||||
from .iotdevice import SmartDeviceException, requires_update
|
||||
from .iotdevice import KasaException, requires_update
|
||||
from .iotplug import IotPlug
|
||||
from .modules import AmbientLight, Motion
|
||||
|
||||
@ -46,7 +46,7 @@ class IotDimmer(IotPlug):
|
||||
which will not change the cached values,
|
||||
but you must await :func:`update()` separately.
|
||||
|
||||
Errors reported by the device are raised as :class:`SmartDeviceException`\s,
|
||||
Errors reported by the device are raised as :class:`KasaException`\s,
|
||||
and should be handled by the user of the library.
|
||||
|
||||
Examples:
|
||||
@ -88,7 +88,7 @@ class IotDimmer(IotPlug):
|
||||
Will return a range between 0 - 100.
|
||||
"""
|
||||
if not self.is_dimmable:
|
||||
raise SmartDeviceException("Device is not dimmable.")
|
||||
raise KasaException("Device is not dimmable.")
|
||||
|
||||
sys_info = self.sys_info
|
||||
return int(sys_info["brightness"])
|
||||
@ -103,7 +103,7 @@ class IotDimmer(IotPlug):
|
||||
Using a transition will cause the dimmer to turn on.
|
||||
"""
|
||||
if not self.is_dimmable:
|
||||
raise SmartDeviceException("Device is not dimmable.")
|
||||
raise KasaException("Device is not dimmable.")
|
||||
|
||||
if not isinstance(brightness, int):
|
||||
raise ValueError(
|
||||
|
@ -6,7 +6,7 @@ from ..deviceconfig import DeviceConfig
|
||||
from ..effects import EFFECT_MAPPING_V1, EFFECT_NAMES_V1
|
||||
from ..protocol import BaseProtocol
|
||||
from .iotbulb import IotBulb
|
||||
from .iotdevice import SmartDeviceException, requires_update
|
||||
from .iotdevice import KasaException, requires_update
|
||||
|
||||
|
||||
class IotLightStrip(IotBulb):
|
||||
@ -117,7 +117,7 @@ class IotLightStrip(IotBulb):
|
||||
:param int transition: The wanted transition time
|
||||
"""
|
||||
if effect not in EFFECT_MAPPING_V1:
|
||||
raise SmartDeviceException(f"The effect {effect} is not a built in effect.")
|
||||
raise KasaException(f"The effect {effect} is not a built in effect.")
|
||||
effect_dict = EFFECT_MAPPING_V1[effect]
|
||||
if brightness is not None:
|
||||
effect_dict["brightness"] = brightness
|
||||
@ -136,7 +136,7 @@ class IotLightStrip(IotBulb):
|
||||
:param str effect_dict: The custom effect dict to set
|
||||
"""
|
||||
if not self.has_effects:
|
||||
raise SmartDeviceException("Bulb does not support effects.")
|
||||
raise KasaException("Bulb does not support effects.")
|
||||
await self._query_helper(
|
||||
"smartlife.iot.lighting_effect",
|
||||
"set_lighting_effect",
|
||||
|
@ -2,7 +2,7 @@
|
||||
import collections
|
||||
import logging
|
||||
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..exceptions import KasaException
|
||||
from ..module import Module
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -43,7 +43,7 @@ class IotModule(Module):
|
||||
def data(self):
|
||||
"""Return the module specific raw data from the last update."""
|
||||
if self._module not in self._device._last_update:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"You need to call update() prior accessing module data"
|
||||
f" for '{self._module}'"
|
||||
)
|
||||
|
@ -22,7 +22,7 @@ class IotPlug(IotDevice):
|
||||
which will not change the cached values,
|
||||
but you must await :func:`update()` separately.
|
||||
|
||||
Errors reported by the device are raised as :class:`SmartDeviceException`\s,
|
||||
Errors reported by the device are raised as :class:`KasaException`\s,
|
||||
and should be handled by the user of the library.
|
||||
|
||||
Examples:
|
||||
|
@ -6,7 +6,7 @@ from typing import Any, DefaultDict, Dict, Optional
|
||||
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..exceptions import KasaException
|
||||
from ..protocol import BaseProtocol
|
||||
from .iotdevice import (
|
||||
EmeterStatus,
|
||||
@ -43,7 +43,7 @@ class IotStrip(IotDevice):
|
||||
which will not change the cached values,
|
||||
but you must await :func:`update()` separately.
|
||||
|
||||
Errors reported by the device are raised as :class:`SmartDeviceException`\s,
|
||||
Errors reported by the device are raised as :class:`KasaException`\s,
|
||||
and should be handled by the user of the library.
|
||||
|
||||
Examples:
|
||||
@ -375,4 +375,4 @@ class IotStripPlug(IotPlug):
|
||||
if plug["id"] == self.child_id:
|
||||
return plug
|
||||
|
||||
raise SmartDeviceException(f"Unable to find children {self.child_id}")
|
||||
raise KasaException(f"Unable to find children {self.child_id}")
|
||||
|
@ -2,7 +2,7 @@
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from ...exceptions import SmartDeviceException
|
||||
from ...exceptions import KasaException
|
||||
from ..iotmodule import IotModule
|
||||
|
||||
|
||||
@ -54,9 +54,7 @@ class Motion(IotModule):
|
||||
elif range is not None:
|
||||
payload = {"index": range.value}
|
||||
else:
|
||||
raise SmartDeviceException(
|
||||
"Either range or custom_range need to be defined"
|
||||
)
|
||||
raise KasaException("Either range or custom_range need to be defined")
|
||||
|
||||
return await self.call("set_trigger_sens", payload)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Provides the current time and timezone information."""
|
||||
from datetime import datetime
|
||||
|
||||
from ...exceptions import SmartDeviceException
|
||||
from ...exceptions import KasaException
|
||||
from ..iotmodule import IotModule, merge
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ class Time(IotModule):
|
||||
res["min"],
|
||||
res["sec"],
|
||||
)
|
||||
except SmartDeviceException:
|
||||
except KasaException:
|
||||
return None
|
||||
|
||||
async def get_timezone(self):
|
||||
|
@ -5,11 +5,11 @@ from typing import Dict, Optional, Union
|
||||
|
||||
from .deviceconfig import DeviceConfig
|
||||
from .exceptions import (
|
||||
AuthenticationException,
|
||||
ConnectionException,
|
||||
RetryableException,
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
AuthenticationError,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
_ConnectionError,
|
||||
_RetryableError,
|
||||
)
|
||||
from .json import dumps as json_dumps
|
||||
from .protocol import BaseProtocol, BaseTransport
|
||||
@ -46,31 +46,31 @@ class IotProtocol(BaseProtocol):
|
||||
for retry in range(retry_count + 1):
|
||||
try:
|
||||
return await self._execute_query(request, retry)
|
||||
except ConnectionException as sdex:
|
||||
except _ConnectionError as sdex:
|
||||
if retry >= retry_count:
|
||||
_LOGGER.debug("Giving up on %s after %s retries", self._host, retry)
|
||||
raise sdex
|
||||
continue
|
||||
except AuthenticationException as auex:
|
||||
except AuthenticationError as auex:
|
||||
await self._transport.reset()
|
||||
_LOGGER.debug(
|
||||
"Unable to authenticate with %s, not retrying", self._host
|
||||
)
|
||||
raise auex
|
||||
except RetryableException as ex:
|
||||
except _RetryableError as ex:
|
||||
await self._transport.reset()
|
||||
if retry >= retry_count:
|
||||
_LOGGER.debug("Giving up on %s after %s retries", self._host, retry)
|
||||
raise ex
|
||||
continue
|
||||
except TimeoutException as ex:
|
||||
except TimeoutError as ex:
|
||||
await self._transport.reset()
|
||||
if retry >= retry_count:
|
||||
_LOGGER.debug("Giving up on %s after %s retries", self._host, retry)
|
||||
raise ex
|
||||
await asyncio.sleep(self.BACKOFF_SECONDS_AFTER_TIMEOUT)
|
||||
continue
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
await self._transport.reset()
|
||||
_LOGGER.debug(
|
||||
"Unable to query the device: %s, not retrying: %s",
|
||||
@ -80,7 +80,7 @@ class IotProtocol(BaseProtocol):
|
||||
raise ex
|
||||
|
||||
# make mypy happy, this should never be reached..
|
||||
raise SmartDeviceException("Query reached somehow to unreachable")
|
||||
raise KasaException("Query reached somehow to unreachable")
|
||||
|
||||
async def _execute_query(self, request: str, retry_count: int) -> Dict:
|
||||
return await self._transport.send(request)
|
||||
@ -101,7 +101,7 @@ class _deprecated_TPLinkSmartHomeProtocol(IotProtocol):
|
||||
) -> None:
|
||||
"""Create a protocol object."""
|
||||
if not host and not transport:
|
||||
raise SmartDeviceException("host or transport must be supplied")
|
||||
raise KasaException("host or transport must be supplied")
|
||||
if not transport:
|
||||
config = DeviceConfig(
|
||||
host=host, # type: ignore[arg-type]
|
||||
|
@ -57,7 +57,7 @@ from yarl import URL
|
||||
|
||||
from .credentials import Credentials
|
||||
from .deviceconfig import DeviceConfig
|
||||
from .exceptions import AuthenticationException, SmartDeviceException
|
||||
from .exceptions import AuthenticationError, KasaException
|
||||
from .httpclient import HttpClient
|
||||
from .json import loads as json_loads
|
||||
from .protocol import DEFAULT_CREDENTIALS, BaseTransport, get_default_credentials, md5
|
||||
@ -159,7 +159,7 @@ class KlapTransport(BaseTransport):
|
||||
)
|
||||
|
||||
if response_status != 200:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Device {self._host} responded with {response_status} to handshake1"
|
||||
)
|
||||
|
||||
@ -168,7 +168,7 @@ class KlapTransport(BaseTransport):
|
||||
server_hash = response_data[16:]
|
||||
|
||||
if len(server_hash) != 32:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Device {self._host} responded with unexpected klap response "
|
||||
+ f"{response_data!r} to handshake1"
|
||||
)
|
||||
@ -236,7 +236,7 @@ class KlapTransport(BaseTransport):
|
||||
|
||||
msg = f"Server response doesn't match our challenge on ip {self._host}"
|
||||
_LOGGER.debug(msg)
|
||||
raise AuthenticationException(msg)
|
||||
raise AuthenticationError(msg)
|
||||
|
||||
async def perform_handshake2(
|
||||
self, local_seed, remote_seed, auth_hash
|
||||
@ -267,8 +267,8 @@ class KlapTransport(BaseTransport):
|
||||
|
||||
if response_status != 200:
|
||||
# This shouldn't be caused by incorrect
|
||||
# credentials so don't raise AuthenticationException
|
||||
raise SmartDeviceException(
|
||||
# credentials so don't raise AuthenticationError
|
||||
raise KasaException(
|
||||
f"Device {self._host} responded with {response_status} to handshake2"
|
||||
)
|
||||
|
||||
@ -337,12 +337,12 @@ class KlapTransport(BaseTransport):
|
||||
# If we failed with a security error, force a new handshake next time.
|
||||
if response_status == 403:
|
||||
self._handshake_done = False
|
||||
raise AuthenticationException(
|
||||
raise AuthenticationError(
|
||||
f"Got a security error from {self._host} after handshake "
|
||||
+ "completed"
|
||||
)
|
||||
else:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Device {self._host} responded with {response_status} to"
|
||||
+ f"request with seq {seq}"
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
||||
from typing import Dict
|
||||
|
||||
from .device import Device
|
||||
from .exceptions import SmartDeviceException
|
||||
from .exceptions import KasaException
|
||||
from .feature import Feature
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -39,7 +39,7 @@ class Module(ABC):
|
||||
"""Add module feature."""
|
||||
feat_name = f"{self._module}_{feature.name}"
|
||||
if feat_name in self._module_features:
|
||||
raise SmartDeviceException("Duplicate name detected %s" % feat_name)
|
||||
raise KasaException("Duplicate name detected %s" % feat_name)
|
||||
self._module_features[feat_name] = feature
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional
|
||||
from ..bulb import Bulb
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..exceptions import KasaException
|
||||
from ..iot.iotbulb import HSV, BulbPreset, ColorTempRange
|
||||
from ..smartprotocol import SmartProtocol
|
||||
from .smartdevice import SmartDevice
|
||||
@ -57,7 +57,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
:return: White temperature range in Kelvin (minimum, maximum)
|
||||
"""
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Color temperature not supported")
|
||||
raise KasaException("Color temperature not supported")
|
||||
|
||||
ct_range = self._info.get("color_temp_range", [0, 0])
|
||||
return ColorTempRange(min=ct_range[0], max=ct_range[1])
|
||||
@ -107,7 +107,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
:return: hue, saturation and value (degrees, %, %)
|
||||
"""
|
||||
if not self.is_color:
|
||||
raise SmartDeviceException("Bulb does not support color.")
|
||||
raise KasaException("Bulb does not support color.")
|
||||
|
||||
h, s, v = (
|
||||
self._info.get("hue", 0),
|
||||
@ -121,7 +121,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
def color_temp(self) -> int:
|
||||
"""Whether the bulb supports color temperature changes."""
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Bulb does not support colortemp.")
|
||||
raise KasaException("Bulb does not support colortemp.")
|
||||
|
||||
return self._info.get("color_temp", -1)
|
||||
|
||||
@ -129,7 +129,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
def brightness(self) -> int:
|
||||
"""Return the current brightness in percentage."""
|
||||
if not self.is_dimmable: # pragma: no cover
|
||||
raise SmartDeviceException("Bulb is not dimmable.")
|
||||
raise KasaException("Bulb is not dimmable.")
|
||||
|
||||
return self._info.get("brightness", -1)
|
||||
|
||||
@ -151,7 +151,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
if not self.is_color:
|
||||
raise SmartDeviceException("Bulb does not support color.")
|
||||
raise KasaException("Bulb does not support color.")
|
||||
|
||||
if not isinstance(hue, int) or not (0 <= hue <= 360):
|
||||
raise ValueError(f"Invalid hue value: {hue} (valid range: 0-360)")
|
||||
@ -188,7 +188,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
# TODO: Note, trying to set brightness at the same time
|
||||
# with color_temp causes error -1008
|
||||
if not self.is_variable_color_temp:
|
||||
raise SmartDeviceException("Bulb does not support colortemp.")
|
||||
raise KasaException("Bulb does not support colortemp.")
|
||||
|
||||
valid_temperature_range = self.valid_temperature_range
|
||||
if temp < valid_temperature_range[0] or temp > valid_temperature_range[1]:
|
||||
@ -211,7 +211,7 @@ class SmartBulb(SmartDevice, Bulb):
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
if not self.is_dimmable: # pragma: no cover
|
||||
raise SmartDeviceException("Bulb is not dimmable.")
|
||||
raise KasaException("Bulb is not dimmable.")
|
||||
|
||||
return await self.protocol.query(
|
||||
{"set_device_info": {"brightness": brightness}}
|
||||
|
@ -9,7 +9,7 @@ from ..device import Device, WifiNetwork
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..emeterstatus import EmeterStatus
|
||||
from ..exceptions import AuthenticationException, SmartDeviceException, SmartErrorCode
|
||||
from ..exceptions import AuthenticationError, DeviceError, KasaException, SmartErrorCode
|
||||
from ..feature import Feature, FeatureType
|
||||
from ..smartprotocol import SmartProtocol
|
||||
from .modules import ( # noqa: F401
|
||||
@ -85,7 +85,7 @@ class SmartDevice(Device):
|
||||
return response
|
||||
if default is not None:
|
||||
return default
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"{request} not found in {responses} for device {self.host}"
|
||||
)
|
||||
|
||||
@ -100,7 +100,7 @@ class SmartDevice(Device):
|
||||
async def update(self, update_children: bool = True):
|
||||
"""Update the device."""
|
||||
if self.credentials is None and self.credentials_hash is None:
|
||||
raise AuthenticationException("Tapo plug requires authentication.")
|
||||
raise AuthenticationError("Tapo plug requires authentication.")
|
||||
|
||||
if self._components_raw is None:
|
||||
await self._negotiate()
|
||||
@ -341,7 +341,7 @@ class SmartDevice(Device):
|
||||
"""Retrieve current energy readings."""
|
||||
_LOGGER.warning("Deprecated, use `emeter_realtime`.")
|
||||
if not self.has_emeter:
|
||||
raise SmartDeviceException("Device has no emeter")
|
||||
raise KasaException("Device has no emeter")
|
||||
return self.emeter_realtime
|
||||
|
||||
@property
|
||||
@ -421,7 +421,7 @@ class SmartDevice(Device):
|
||||
after some delay.
|
||||
"""
|
||||
if not self.credentials:
|
||||
raise AuthenticationException("Device requires authentication.")
|
||||
raise AuthenticationError("Device requires authentication.")
|
||||
|
||||
payload = {
|
||||
"account": {
|
||||
@ -445,10 +445,9 @@ class SmartDevice(Device):
|
||||
# Thus, We limit retries and suppress the raised exception as useless.
|
||||
try:
|
||||
return await self.protocol.query({"set_qs_info": payload}, retry_count=0)
|
||||
except SmartDeviceException as ex:
|
||||
if ex.error_code: # Re-raise on device-reported errors
|
||||
raise
|
||||
|
||||
except DeviceError:
|
||||
raise # Re-raise on device-reported errors
|
||||
except KasaException:
|
||||
_LOGGER.debug("Received an expected for wifi join, but this is expected")
|
||||
|
||||
async def update_credentials(self, username: str, password: str):
|
||||
|
@ -2,7 +2,7 @@
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Dict, Type
|
||||
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..exceptions import KasaException
|
||||
from ..module import Module
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -59,7 +59,7 @@ class SmartModule(Module):
|
||||
q_keys = list(q.keys())
|
||||
# TODO: hacky way to check if update has been called.
|
||||
if q_keys[0] not in self._device._last_update:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"You need to call update() prior accessing module data"
|
||||
f" for '{self._module}'"
|
||||
)
|
||||
|
@ -15,13 +15,13 @@ from typing import Any, Dict, Union
|
||||
from .exceptions import (
|
||||
SMART_AUTHENTICATION_ERRORS,
|
||||
SMART_RETRYABLE_ERRORS,
|
||||
SMART_TIMEOUT_ERRORS,
|
||||
AuthenticationException,
|
||||
ConnectionException,
|
||||
RetryableException,
|
||||
SmartDeviceException,
|
||||
AuthenticationError,
|
||||
DeviceError,
|
||||
KasaException,
|
||||
SmartErrorCode,
|
||||
TimeoutException,
|
||||
TimeoutError,
|
||||
_ConnectionError,
|
||||
_RetryableError,
|
||||
)
|
||||
from .json import dumps as json_dumps
|
||||
from .protocol import BaseProtocol, BaseTransport, md5
|
||||
@ -66,32 +66,32 @@ class SmartProtocol(BaseProtocol):
|
||||
for retry in range(retry_count + 1):
|
||||
try:
|
||||
return await self._execute_query(request, retry)
|
||||
except ConnectionException as sdex:
|
||||
except _ConnectionError as sdex:
|
||||
if retry >= retry_count:
|
||||
_LOGGER.debug("Giving up on %s after %s retries", self._host, retry)
|
||||
raise sdex
|
||||
continue
|
||||
except AuthenticationException as auex:
|
||||
except AuthenticationError as auex:
|
||||
await self._transport.reset()
|
||||
_LOGGER.debug(
|
||||
"Unable to authenticate with %s, not retrying", self._host
|
||||
)
|
||||
raise auex
|
||||
except RetryableException as ex:
|
||||
except _RetryableError as ex:
|
||||
await self._transport.reset()
|
||||
if retry >= retry_count:
|
||||
_LOGGER.debug("Giving up on %s after %s retries", self._host, retry)
|
||||
raise ex
|
||||
await asyncio.sleep(self.BACKOFF_SECONDS_AFTER_TIMEOUT)
|
||||
continue
|
||||
except TimeoutException as ex:
|
||||
except TimeoutError as ex:
|
||||
await self._transport.reset()
|
||||
if retry >= retry_count:
|
||||
_LOGGER.debug("Giving up on %s after %s retries", self._host, retry)
|
||||
raise ex
|
||||
await asyncio.sleep(self.BACKOFF_SECONDS_AFTER_TIMEOUT)
|
||||
continue
|
||||
except SmartDeviceException as ex:
|
||||
except KasaException as ex:
|
||||
await self._transport.reset()
|
||||
_LOGGER.debug(
|
||||
"Unable to query the device: %s, not retrying: %s",
|
||||
@ -101,7 +101,7 @@ class SmartProtocol(BaseProtocol):
|
||||
raise ex
|
||||
|
||||
# make mypy happy, this should never be reached..
|
||||
raise SmartDeviceException("Query reached somehow to unreachable")
|
||||
raise KasaException("Query reached somehow to unreachable")
|
||||
|
||||
async def _execute_multiple_query(self, request: Dict, retry_count: int) -> Dict:
|
||||
debug_enabled = _LOGGER.isEnabledFor(logging.DEBUG)
|
||||
@ -193,13 +193,11 @@ class SmartProtocol(BaseProtocol):
|
||||
+ f"{error_code.name}({error_code.value})"
|
||||
+ f" for method: {method}"
|
||||
)
|
||||
if error_code in SMART_TIMEOUT_ERRORS:
|
||||
raise TimeoutException(msg, error_code=error_code)
|
||||
if error_code in SMART_RETRYABLE_ERRORS:
|
||||
raise RetryableException(msg, error_code=error_code)
|
||||
raise _RetryableError(msg, error_code=error_code)
|
||||
if error_code in SMART_AUTHENTICATION_ERRORS:
|
||||
raise AuthenticationException(msg, error_code=error_code)
|
||||
raise SmartDeviceException(msg, error_code=error_code)
|
||||
raise AuthenticationError(msg, error_code=error_code)
|
||||
raise DeviceError(msg, error_code=error_code)
|
||||
|
||||
async def close(self) -> None:
|
||||
"""Close the underlying transport."""
|
||||
|
@ -1,7 +1,7 @@
|
||||
import warnings
|
||||
from json import loads as json_loads
|
||||
|
||||
from kasa import Credentials, DeviceConfig, SmartDeviceException, SmartProtocol
|
||||
from kasa import Credentials, DeviceConfig, KasaException, SmartProtocol
|
||||
from kasa.protocol import BaseTransport
|
||||
|
||||
|
||||
@ -144,7 +144,7 @@ class FakeSmartTransport(BaseTransport):
|
||||
)
|
||||
return {"result": missing_result[1], "error_code": 0}
|
||||
else:
|
||||
raise SmartDeviceException(f"Fixture doesn't support {method}")
|
||||
raise KasaException(f"Fixture doesn't support {method}")
|
||||
elif method == "set_qs_info":
|
||||
return {"error_code": 0}
|
||||
elif method[:4] == "set_":
|
||||
|
@ -19,8 +19,8 @@ from ..aestransport import AesEncyptionSession, AesTransport, TransportState
|
||||
from ..credentials import Credentials
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import (
|
||||
AuthenticationException,
|
||||
SmartDeviceException,
|
||||
AuthenticationError,
|
||||
KasaException,
|
||||
SmartErrorCode,
|
||||
)
|
||||
from ..httpclient import HttpClient
|
||||
@ -49,8 +49,8 @@ status_parameters = pytest.mark.parametrize(
|
||||
"status_code, error_code, inner_error_code, expectation",
|
||||
[
|
||||
(200, 0, 0, does_not_raise()),
|
||||
(400, 0, 0, pytest.raises(SmartDeviceException)),
|
||||
(200, -1, 0, pytest.raises(SmartDeviceException)),
|
||||
(400, 0, 0, pytest.raises(KasaException)),
|
||||
(200, -1, 0, pytest.raises(KasaException)),
|
||||
],
|
||||
ids=("success", "status_code", "error_code"),
|
||||
)
|
||||
@ -101,17 +101,17 @@ async def test_login(mocker, status_code, error_code, inner_error_code, expectat
|
||||
([SmartErrorCode.LOGIN_ERROR, 0, 0, 0], does_not_raise(), 4),
|
||||
(
|
||||
[SmartErrorCode.LOGIN_ERROR, SmartErrorCode.LOGIN_ERROR],
|
||||
pytest.raises(AuthenticationException),
|
||||
pytest.raises(AuthenticationError),
|
||||
3,
|
||||
),
|
||||
(
|
||||
[SmartErrorCode.LOGIN_FAILED_ERROR],
|
||||
pytest.raises(AuthenticationException),
|
||||
pytest.raises(AuthenticationError),
|
||||
1,
|
||||
),
|
||||
(
|
||||
[SmartErrorCode.LOGIN_ERROR, SmartErrorCode.SESSION_TIMEOUT_ERROR],
|
||||
pytest.raises(SmartDeviceException),
|
||||
pytest.raises(KasaException),
|
||||
3,
|
||||
),
|
||||
],
|
||||
@ -238,7 +238,7 @@ async def test_unencrypted_response_invalid_json(mocker, caplog):
|
||||
}
|
||||
caplog.set_level(logging.DEBUG)
|
||||
msg = f"Unable to decrypt response from {host}, error: Incorrect padding, response: Foobar"
|
||||
with pytest.raises(SmartDeviceException, match=msg):
|
||||
with pytest.raises(KasaException, match=msg):
|
||||
await transport.send(json_dumps(request))
|
||||
|
||||
|
||||
@ -267,7 +267,7 @@ async def test_passthrough_errors(mocker, error_code):
|
||||
"requestID": 1,
|
||||
"terminal_uuid": "foobar",
|
||||
}
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await transport.send(json_dumps(request))
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ from voluptuous import (
|
||||
Schema,
|
||||
)
|
||||
|
||||
from kasa import Bulb, BulbPreset, DeviceType, SmartDeviceException
|
||||
from kasa import Bulb, BulbPreset, DeviceType, KasaException
|
||||
from kasa.iot import IotBulb
|
||||
|
||||
from .conftest import (
|
||||
@ -51,7 +51,7 @@ async def test_state_attributes(dev: Bulb):
|
||||
|
||||
@bulb_iot
|
||||
async def test_light_state_without_update(dev: IotBulb, monkeypatch):
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
monkeypatch.setitem(
|
||||
dev._last_update["system"]["get_sysinfo"], "light_state", None
|
||||
)
|
||||
@ -123,9 +123,9 @@ async def test_color_state_information(dev: Bulb):
|
||||
async def test_hsv_on_non_color(dev: Bulb):
|
||||
assert not dev.is_color
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.set_hsv(0, 0, 0)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
print(dev.hsv)
|
||||
|
||||
|
||||
@ -175,13 +175,13 @@ async def test_out_of_range_temperature(dev: Bulb):
|
||||
|
||||
@non_variable_temp
|
||||
async def test_non_variable_temp(dev: Bulb):
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.set_color_temp(2700)
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
print(dev.valid_temperature_range)
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
print(dev.color_temp)
|
||||
|
||||
|
||||
@ -238,9 +238,9 @@ async def test_invalid_brightness(dev: Bulb):
|
||||
async def test_non_dimmable(dev: Bulb):
|
||||
assert not dev.is_dimmable
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
assert dev.brightness == 0
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.set_brightness(100)
|
||||
|
||||
|
||||
@ -296,7 +296,7 @@ async def test_modify_preset(dev: IotBulb, mocker):
|
||||
await dev.save_preset(preset)
|
||||
assert dev.presets[0].brightness == 10
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.save_preset(
|
||||
BulbPreset(index=5, hue=0, brightness=0, saturation=0, color_temp=0)
|
||||
)
|
||||
|
@ -6,12 +6,13 @@ import pytest
|
||||
from asyncclick.testing import CliRunner
|
||||
|
||||
from kasa import (
|
||||
AuthenticationException,
|
||||
AuthenticationError,
|
||||
Credentials,
|
||||
Device,
|
||||
DeviceError,
|
||||
EmeterStatus,
|
||||
SmartDeviceException,
|
||||
UnsupportedDeviceException,
|
||||
KasaException,
|
||||
UnsupportedDeviceError,
|
||||
)
|
||||
from kasa.cli import (
|
||||
TYPE_TO_CLASS,
|
||||
@ -188,15 +189,13 @@ async def test_wifi_join_no_creds(dev):
|
||||
)
|
||||
|
||||
assert res.exit_code != 0
|
||||
assert isinstance(res.exception, AuthenticationException)
|
||||
assert isinstance(res.exception, AuthenticationError)
|
||||
|
||||
|
||||
@device_smart
|
||||
async def test_wifi_join_exception(dev, mocker):
|
||||
runner = CliRunner()
|
||||
mocker.patch.object(
|
||||
dev.protocol, "query", side_effect=SmartDeviceException(error_code=9999)
|
||||
)
|
||||
mocker.patch.object(dev.protocol, "query", side_effect=DeviceError(error_code=9999))
|
||||
res = await runner.invoke(
|
||||
wifi,
|
||||
["join", "FOOBAR", "--keytype", "wpa_psk", "--password", "foobar"],
|
||||
@ -204,7 +203,7 @@ async def test_wifi_join_exception(dev, mocker):
|
||||
)
|
||||
|
||||
assert res.exit_code != 0
|
||||
assert isinstance(res.exception, SmartDeviceException)
|
||||
assert isinstance(res.exception, KasaException)
|
||||
|
||||
|
||||
@device_smart
|
||||
@ -509,7 +508,7 @@ async def test_host_unsupported(unsupported_device_info):
|
||||
)
|
||||
|
||||
assert res.exit_code != 0
|
||||
assert isinstance(res.exception, UnsupportedDeviceException)
|
||||
assert isinstance(res.exception, UnsupportedDeviceError)
|
||||
|
||||
|
||||
@new_discovery
|
||||
@ -522,7 +521,7 @@ async def test_discover_auth_failed(discovery_mock, mocker):
|
||||
mocker.patch.object(
|
||||
device_class,
|
||||
"update",
|
||||
side_effect=AuthenticationException("Failed to authenticate"),
|
||||
side_effect=AuthenticationError("Failed to authenticate"),
|
||||
)
|
||||
res = await runner.invoke(
|
||||
cli,
|
||||
@ -553,7 +552,7 @@ async def test_host_auth_failed(discovery_mock, mocker):
|
||||
mocker.patch.object(
|
||||
device_class,
|
||||
"update",
|
||||
side_effect=AuthenticationException("Failed to authenticate"),
|
||||
side_effect=AuthenticationError("Failed to authenticate"),
|
||||
)
|
||||
res = await runner.invoke(
|
||||
cli,
|
||||
@ -569,7 +568,7 @@ async def test_host_auth_failed(discovery_mock, mocker):
|
||||
)
|
||||
|
||||
assert res.exit_code != 0
|
||||
assert isinstance(res.exception, AuthenticationException)
|
||||
assert isinstance(res.exception, AuthenticationError)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_type", list(TYPE_TO_CLASS))
|
||||
@ -616,7 +615,7 @@ async def test_shell(dev: Device, mocker):
|
||||
|
||||
async def test_errors(mocker):
|
||||
runner = CliRunner()
|
||||
err = SmartDeviceException("Foobar")
|
||||
err = KasaException("Foobar")
|
||||
|
||||
# Test masking
|
||||
mocker.patch("kasa.Discover.discover", side_effect=err)
|
||||
|
@ -8,7 +8,7 @@ from kasa import (
|
||||
Credentials,
|
||||
Device,
|
||||
Discover,
|
||||
SmartDeviceException,
|
||||
KasaException,
|
||||
)
|
||||
from kasa.device_factory import connect, get_protocol
|
||||
from kasa.deviceconfig import (
|
||||
@ -110,8 +110,8 @@ async def test_connect_logs_connect_time(
|
||||
async def test_connect_query_fails(all_fixture_data: dict, mocker):
|
||||
"""Make sure that connect fails when query fails."""
|
||||
host = "127.0.0.1"
|
||||
mocker.patch("kasa.IotProtocol.query", side_effect=SmartDeviceException)
|
||||
mocker.patch("kasa.SmartProtocol.query", side_effect=SmartDeviceException)
|
||||
mocker.patch("kasa.IotProtocol.query", side_effect=KasaException)
|
||||
mocker.patch("kasa.SmartProtocol.query", side_effect=KasaException)
|
||||
|
||||
ctype, _ = _get_connection_type_device_class(all_fixture_data)
|
||||
config = DeviceConfig(
|
||||
@ -120,7 +120,7 @@ async def test_connect_query_fails(all_fixture_data: dict, mocker):
|
||||
protocol_class = get_protocol(config).__class__
|
||||
close_mock = mocker.patch.object(protocol_class, "close")
|
||||
assert close_mock.call_count == 0
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await connect(config=config)
|
||||
assert close_mock.call_count == 1
|
||||
|
||||
|
@ -8,7 +8,7 @@ from kasa.credentials import Credentials
|
||||
from kasa.deviceconfig import (
|
||||
DeviceConfig,
|
||||
)
|
||||
from kasa.exceptions import SmartDeviceException
|
||||
from kasa.exceptions import KasaException
|
||||
|
||||
|
||||
async def test_serialization():
|
||||
@ -29,7 +29,7 @@ async def test_serialization():
|
||||
ids=["invalid-dict", "not-dict"],
|
||||
)
|
||||
def test_deserialization_errors(input_value, expected_msg):
|
||||
with pytest.raises(SmartDeviceException, match=expected_msg):
|
||||
with pytest.raises(KasaException, match=expected_msg):
|
||||
DeviceConfig.from_dict(input_value)
|
||||
|
||||
|
||||
|
@ -13,14 +13,14 @@ from kasa import (
|
||||
Device,
|
||||
DeviceType,
|
||||
Discover,
|
||||
SmartDeviceException,
|
||||
KasaException,
|
||||
)
|
||||
from kasa.deviceconfig import (
|
||||
ConnectionType,
|
||||
DeviceConfig,
|
||||
)
|
||||
from kasa.discover import DiscoveryResult, _DiscoverProtocol, json_dumps
|
||||
from kasa.exceptions import AuthenticationException, UnsupportedDeviceException
|
||||
from kasa.exceptions import AuthenticationError, UnsupportedDeviceError
|
||||
from kasa.iot import IotDevice
|
||||
from kasa.xortransport import XorEncryption
|
||||
|
||||
@ -94,7 +94,7 @@ async def test_type_detection_lightstrip(dev: Device):
|
||||
|
||||
async def test_type_unknown():
|
||||
invalid_info = {"system": {"get_sysinfo": {"type": "nosuchtype"}}}
|
||||
with pytest.raises(UnsupportedDeviceException):
|
||||
with pytest.raises(UnsupportedDeviceError):
|
||||
Discover._get_device_class(invalid_info)
|
||||
|
||||
|
||||
@ -151,7 +151,7 @@ async def test_discover_single_hostname(discovery_mock, mocker):
|
||||
assert update_mock.call_count == 0
|
||||
|
||||
mocker.patch("socket.getaddrinfo", side_effect=socket.gaierror())
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
x = await Discover.discover_single(host, credentials=Credentials())
|
||||
|
||||
|
||||
@ -161,7 +161,7 @@ async def test_discover_single_unsupported(unsupported_device_info, mocker):
|
||||
|
||||
# Test with a valid unsupported response
|
||||
with pytest.raises(
|
||||
UnsupportedDeviceException,
|
||||
UnsupportedDeviceError,
|
||||
):
|
||||
await Discover.discover_single(host)
|
||||
|
||||
@ -171,7 +171,7 @@ async def test_discover_single_no_response(mocker):
|
||||
host = "127.0.0.1"
|
||||
mocker.patch.object(_DiscoverProtocol, "do_discover")
|
||||
with pytest.raises(
|
||||
SmartDeviceException, match=f"Timed out getting discovery response for {host}"
|
||||
KasaException, match=f"Timed out getting discovery response for {host}"
|
||||
):
|
||||
await Discover.discover_single(host, discovery_timeout=0)
|
||||
|
||||
@ -198,7 +198,7 @@ async def test_discover_invalid_info(msg, data, mocker):
|
||||
|
||||
mocker.patch.object(_DiscoverProtocol, "do_discover", mock_discover)
|
||||
|
||||
with pytest.raises(SmartDeviceException, match=msg):
|
||||
with pytest.raises(KasaException, match=msg):
|
||||
await Discover.discover_single(host)
|
||||
|
||||
|
||||
@ -280,11 +280,11 @@ async def test_discover_single_authentication(discovery_mock, mocker):
|
||||
mocker.patch.object(
|
||||
device_class,
|
||||
"update",
|
||||
side_effect=AuthenticationException("Failed to authenticate"),
|
||||
side_effect=AuthenticationError("Failed to authenticate"),
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
AuthenticationException,
|
||||
AuthenticationError,
|
||||
match="Failed to authenticate",
|
||||
):
|
||||
device = await Discover.discover_single(
|
||||
@ -315,7 +315,7 @@ async def test_device_update_from_new_discovery_info(discovery_data):
|
||||
# TODO implement requires_update for SmartDevice
|
||||
if isinstance(device, IotDevice):
|
||||
with pytest.raises(
|
||||
SmartDeviceException,
|
||||
KasaException,
|
||||
match=re.escape("You need to await update() to access the data"),
|
||||
):
|
||||
assert device.supported_modules
|
||||
@ -456,9 +456,9 @@ async def test_discover_propogates_task_exceptions(discovery_mock):
|
||||
discovery_timeout = 0
|
||||
|
||||
async def on_discovered(dev):
|
||||
raise SmartDeviceException("Dummy exception")
|
||||
raise KasaException("Dummy exception")
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await Discover.discover(
|
||||
discovery_timeout=discovery_timeout, on_discovered=on_discovered
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ from voluptuous import (
|
||||
Schema,
|
||||
)
|
||||
|
||||
from kasa import EmeterStatus, SmartDeviceException
|
||||
from kasa import EmeterStatus, KasaException
|
||||
from kasa.iot import IotDevice
|
||||
from kasa.iot.modules.emeter import Emeter
|
||||
|
||||
@ -38,16 +38,16 @@ CURRENT_CONSUMPTION_SCHEMA = Schema(
|
||||
async def test_no_emeter(dev):
|
||||
assert not dev.has_emeter
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.get_emeter_realtime()
|
||||
# Only iot devices support the historical stats so other
|
||||
# devices will not implement the methods below
|
||||
if isinstance(dev, IotDevice):
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.get_emeter_daily()
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.get_emeter_monthly()
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.erase_emeter_stats()
|
||||
|
||||
|
||||
|
@ -6,9 +6,9 @@ import pytest
|
||||
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import (
|
||||
ConnectionException,
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
_ConnectionError,
|
||||
)
|
||||
from ..httpclient import HttpClient
|
||||
|
||||
@ -18,28 +18,28 @@ from ..httpclient import HttpClient
|
||||
[
|
||||
(
|
||||
aiohttp.ServerDisconnectedError(),
|
||||
ConnectionException,
|
||||
_ConnectionError,
|
||||
"Device connection error: ",
|
||||
),
|
||||
(
|
||||
aiohttp.ClientOSError(),
|
||||
ConnectionException,
|
||||
_ConnectionError,
|
||||
"Device connection error: ",
|
||||
),
|
||||
(
|
||||
aiohttp.ServerTimeoutError(),
|
||||
TimeoutException,
|
||||
TimeoutError,
|
||||
"Unable to query the device, timed out: ",
|
||||
),
|
||||
(
|
||||
asyncio.TimeoutError(),
|
||||
TimeoutException,
|
||||
TimeoutError,
|
||||
"Unable to query the device, timed out: ",
|
||||
),
|
||||
(Exception(), SmartDeviceException, "Unable to query the device: "),
|
||||
(Exception(), KasaException, "Unable to query the device: "),
|
||||
(
|
||||
aiohttp.ServerFingerprintMismatch("exp", "got", "host", 1),
|
||||
SmartDeviceException,
|
||||
KasaException,
|
||||
"Unable to query the device: ",
|
||||
),
|
||||
],
|
||||
|
@ -12,11 +12,11 @@ from ..aestransport import AesTransport
|
||||
from ..credentials import Credentials
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import (
|
||||
AuthenticationException,
|
||||
ConnectionException,
|
||||
RetryableException,
|
||||
SmartDeviceException,
|
||||
TimeoutException,
|
||||
AuthenticationError,
|
||||
KasaException,
|
||||
TimeoutError,
|
||||
_ConnectionError,
|
||||
_RetryableError,
|
||||
)
|
||||
from ..httpclient import HttpClient
|
||||
from ..iotprotocol import IotProtocol
|
||||
@ -68,7 +68,7 @@ async def test_protocol_retries_via_client_session(
|
||||
mocker.patch.object(protocol_class, "BACKOFF_SECONDS_AFTER_TIMEOUT", 0)
|
||||
|
||||
config = DeviceConfig(host)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
DUMMY_QUERY, retry_count=retry_count
|
||||
)
|
||||
@ -80,11 +80,11 @@ async def test_protocol_retries_via_client_session(
|
||||
@pytest.mark.parametrize(
|
||||
"error, retry_expectation",
|
||||
[
|
||||
(SmartDeviceException("dummy exception"), False),
|
||||
(RetryableException("dummy exception"), True),
|
||||
(TimeoutException("dummy exception"), True),
|
||||
(KasaException("dummy exception"), False),
|
||||
(_RetryableError("dummy exception"), True),
|
||||
(TimeoutError("dummy exception"), True),
|
||||
],
|
||||
ids=("SmartDeviceException", "RetryableException", "TimeoutException"),
|
||||
ids=("KasaException", "_RetryableError", "TimeoutError"),
|
||||
)
|
||||
@pytest.mark.parametrize("transport_class", [AesTransport, KlapTransport])
|
||||
@pytest.mark.parametrize("protocol_class", [IotProtocol, SmartProtocol])
|
||||
@ -97,7 +97,7 @@ async def test_protocol_retries_via_httpclient(
|
||||
mocker.patch.object(protocol_class, "BACKOFF_SECONDS_AFTER_TIMEOUT", 0)
|
||||
|
||||
config = DeviceConfig(host)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
DUMMY_QUERY, retry_count=retry_count
|
||||
)
|
||||
@ -115,11 +115,11 @@ async def test_protocol_no_retry_on_connection_error(
|
||||
conn = mocker.patch.object(
|
||||
aiohttp.ClientSession,
|
||||
"post",
|
||||
side_effect=AuthenticationException("foo"),
|
||||
side_effect=AuthenticationError("foo"),
|
||||
)
|
||||
mocker.patch.object(protocol_class, "BACKOFF_SECONDS_AFTER_TIMEOUT", 0)
|
||||
config = DeviceConfig(host)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
DUMMY_QUERY, retry_count=5
|
||||
)
|
||||
@ -139,7 +139,7 @@ async def test_protocol_retry_recoverable_error(
|
||||
side_effect=aiohttp.ClientOSError("foo"),
|
||||
)
|
||||
config = DeviceConfig(host)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
DUMMY_QUERY, retry_count=5
|
||||
)
|
||||
@ -159,7 +159,7 @@ async def test_protocol_reconnect(mocker, retry_count, protocol_class, transport
|
||||
nonlocal remaining
|
||||
remaining -= 1
|
||||
if remaining:
|
||||
raise ConnectionException("Simulated connection failure")
|
||||
raise _ConnectionError("Simulated connection failure")
|
||||
|
||||
return mock_response
|
||||
|
||||
@ -249,7 +249,7 @@ def test_encrypt_unicode():
|
||||
),
|
||||
(
|
||||
Credentials("shouldfail", "shouldfail"),
|
||||
pytest.raises(AuthenticationException),
|
||||
pytest.raises(AuthenticationError),
|
||||
),
|
||||
],
|
||||
ids=("client", "blank", "kasa_setup", "shouldfail"),
|
||||
@ -350,7 +350,7 @@ async def test_handshake(
|
||||
assert protocol._transport._handshake_done is True
|
||||
|
||||
response_status = 403
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol._transport.perform_handshake()
|
||||
assert protocol._transport._handshake_done is False
|
||||
await protocol.close()
|
||||
@ -405,37 +405,37 @@ async def test_query(mocker):
|
||||
pytest.param(
|
||||
(403, 403, 403),
|
||||
True,
|
||||
pytest.raises(SmartDeviceException),
|
||||
pytest.raises(KasaException),
|
||||
id="handshake1-403-status",
|
||||
),
|
||||
pytest.param(
|
||||
(200, 403, 403),
|
||||
True,
|
||||
pytest.raises(SmartDeviceException),
|
||||
pytest.raises(KasaException),
|
||||
id="handshake2-403-status",
|
||||
),
|
||||
pytest.param(
|
||||
(200, 200, 403),
|
||||
True,
|
||||
pytest.raises(AuthenticationException),
|
||||
pytest.raises(AuthenticationError),
|
||||
id="request-403-status",
|
||||
),
|
||||
pytest.param(
|
||||
(200, 200, 400),
|
||||
True,
|
||||
pytest.raises(SmartDeviceException),
|
||||
pytest.raises(KasaException),
|
||||
id="request-400-status",
|
||||
),
|
||||
pytest.param(
|
||||
(200, 200, 200),
|
||||
False,
|
||||
pytest.raises(AuthenticationException),
|
||||
pytest.raises(AuthenticationError),
|
||||
id="handshake1-wrong-auth",
|
||||
),
|
||||
pytest.param(
|
||||
(200, 200, 200),
|
||||
secrets.token_bytes(16),
|
||||
pytest.raises(SmartDeviceException),
|
||||
pytest.raises(KasaException),
|
||||
id="handshake1-bad-auth-length",
|
||||
),
|
||||
],
|
||||
|
@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from kasa import DeviceType
|
||||
from kasa.exceptions import SmartDeviceException
|
||||
from kasa.exceptions import KasaException
|
||||
from kasa.iot import IotLightStrip
|
||||
|
||||
from .conftest import lightstrip
|
||||
@ -23,7 +23,7 @@ async def test_lightstrip_effect(dev: IotLightStrip):
|
||||
|
||||
@lightstrip
|
||||
async def test_effects_lightstrip_set_effect(dev: IotLightStrip):
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev.set_effect("Not real")
|
||||
|
||||
await dev.set_effect("Candy Cane")
|
||||
|
@ -14,7 +14,7 @@ import pytest
|
||||
from ..aestransport import AesTransport
|
||||
from ..credentials import Credentials
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..exceptions import KasaException
|
||||
from ..iotprotocol import IotProtocol, _deprecated_TPLinkSmartHomeProtocol
|
||||
from ..klaptransport import KlapTransport, KlapTransportV2
|
||||
from ..protocol import (
|
||||
@ -46,7 +46,7 @@ async def test_protocol_retries(mocker, retry_count, protocol_class, transport_c
|
||||
|
||||
conn = mocker.patch("asyncio.open_connection", side_effect=aio_mock_writer)
|
||||
config = DeviceConfig("127.0.0.1")
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
{}, retry_count=retry_count
|
||||
)
|
||||
@ -70,7 +70,7 @@ async def test_protocol_no_retry_on_unreachable(
|
||||
side_effect=OSError(errno.EHOSTUNREACH, "No route to host"),
|
||||
)
|
||||
config = DeviceConfig("127.0.0.1")
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
{}, retry_count=5
|
||||
)
|
||||
@ -94,7 +94,7 @@ async def test_protocol_no_retry_connection_refused(
|
||||
side_effect=ConnectionRefusedError,
|
||||
)
|
||||
config = DeviceConfig("127.0.0.1")
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
{}, retry_count=5
|
||||
)
|
||||
@ -118,7 +118,7 @@ async def test_protocol_retry_recoverable_error(
|
||||
side_effect=OSError(errno.ECONNRESET, "Connection reset by peer"),
|
||||
)
|
||||
config = DeviceConfig("127.0.0.1")
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
{}, retry_count=5
|
||||
)
|
||||
@ -553,7 +553,7 @@ async def test_protocol_will_retry_on_connect(
|
||||
retry_count = 2
|
||||
conn = mocker.patch("asyncio.open_connection", side_effect=error)
|
||||
config = DeviceConfig("127.0.0.1")
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
{}, retry_count=retry_count
|
||||
)
|
||||
@ -595,7 +595,7 @@ async def test_protocol_will_retry_on_write(
|
||||
conn = mocker.patch("asyncio.open_connection", side_effect=aio_mock_writer)
|
||||
write_mock = mocker.patch("asyncio.StreamWriter.write", side_effect=error)
|
||||
config = DeviceConfig("127.0.0.1")
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await protocol_class(transport=transport_class(config=config)).query(
|
||||
{}, retry_count=retry_count
|
||||
)
|
||||
@ -609,9 +609,7 @@ def test_deprecated_protocol():
|
||||
with pytest.deprecated_call():
|
||||
from kasa import TPLinkSmartHomeProtocol
|
||||
|
||||
with pytest.raises(
|
||||
SmartDeviceException, match="host or transport must be supplied"
|
||||
):
|
||||
with pytest.raises(KasaException, match="host or transport must be supplied"):
|
||||
proto = TPLinkSmartHomeProtocol()
|
||||
host = "127.0.0.1"
|
||||
proto = TPLinkSmartHomeProtocol(host=host)
|
||||
|
@ -21,7 +21,7 @@ from voluptuous import (
|
||||
)
|
||||
|
||||
import kasa
|
||||
from kasa import Credentials, Device, DeviceConfig, SmartDeviceException
|
||||
from kasa import Credentials, Device, DeviceConfig, KasaException
|
||||
from kasa.exceptions import SmartErrorCode
|
||||
from kasa.iot import IotDevice
|
||||
from kasa.smart import SmartChildDevice, SmartDevice
|
||||
@ -67,8 +67,8 @@ async def test_state_info(dev):
|
||||
@device_iot
|
||||
async def test_invalid_connection(dev):
|
||||
with patch.object(
|
||||
FakeIotProtocol, "query", side_effect=SmartDeviceException
|
||||
), pytest.raises(SmartDeviceException):
|
||||
FakeIotProtocol, "query", side_effect=KasaException
|
||||
), pytest.raises(KasaException):
|
||||
await dev.update()
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ async def test_initial_update_no_emeter(dev, mocker):
|
||||
|
||||
@device_iot
|
||||
async def test_query_helper(dev):
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dev._query_helper("test", "testcmd", {})
|
||||
# TODO check for unwrapping?
|
||||
|
||||
@ -328,7 +328,7 @@ async def test_update_no_device_info(dev: SmartDevice):
|
||||
}
|
||||
msg = f"get_device_info not found in {mock_response} for device 127.0.0.123"
|
||||
with patch.object(dev.protocol, "query", return_value=mock_response), pytest.raises(
|
||||
SmartDeviceException, match=msg
|
||||
KasaException, match=msg
|
||||
):
|
||||
await dev.update()
|
||||
|
||||
@ -348,6 +348,16 @@ def test_deprecated_devices(device_class, use_class):
|
||||
getattr(module, use_class.__name__)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exceptions_class, use_class", kasa.deprecated_exceptions.items()
|
||||
)
|
||||
def test_deprecated_exceptions(exceptions_class, use_class):
|
||||
msg = f"{exceptions_class} is deprecated, use {use_class.__name__} instead"
|
||||
with pytest.deprecated_call(match=msg):
|
||||
getattr(kasa, exceptions_class)
|
||||
getattr(kasa, use_class.__name__)
|
||||
|
||||
|
||||
def check_mac(x):
|
||||
if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", x.lower()):
|
||||
return x
|
||||
|
@ -1,13 +1,10 @@
|
||||
from itertools import chain
|
||||
|
||||
import pytest
|
||||
|
||||
from ..credentials import Credentials
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import (
|
||||
SMART_RETRYABLE_ERRORS,
|
||||
SMART_TIMEOUT_ERRORS,
|
||||
SmartDeviceException,
|
||||
KasaException,
|
||||
SmartErrorCode,
|
||||
)
|
||||
from ..smartprotocol import _ChildProtocolWrapper
|
||||
@ -28,13 +25,10 @@ async def test_smart_device_errors(dummy_protocol, mocker, error_code):
|
||||
dummy_protocol._transport, "send", return_value=mock_response
|
||||
)
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dummy_protocol.query(DUMMY_QUERY, retry_count=2)
|
||||
|
||||
if error_code in chain(SMART_TIMEOUT_ERRORS, SMART_RETRYABLE_ERRORS):
|
||||
expected_calls = 3
|
||||
else:
|
||||
expected_calls = 1
|
||||
expected_calls = 3 if error_code in SMART_RETRYABLE_ERRORS else 1
|
||||
assert send_mock.call_count == expected_calls
|
||||
|
||||
|
||||
@ -124,7 +118,7 @@ async def test_childdevicewrapper_error(dummy_protocol, mocker):
|
||||
mock_response = {"error_code": 0, "result": {"responseData": {"error_code": -1001}}}
|
||||
|
||||
mocker.patch.object(wrapped_protocol._transport, "send", return_value=mock_response)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await wrapped_protocol.query(DUMMY_QUERY)
|
||||
|
||||
|
||||
@ -180,5 +174,5 @@ async def test_childdevicewrapper_multiplerequest_error(dummy_protocol, mocker):
|
||||
}
|
||||
|
||||
mocker.patch.object(dummy_protocol._transport, "send", return_value=mock_response)
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
await dummy_protocol.query(DUMMY_QUERY)
|
||||
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from kasa import SmartDeviceException
|
||||
from kasa import KasaException
|
||||
from kasa.iot import IotStrip
|
||||
|
||||
from .conftest import handle_turn_on, strip, turn_on
|
||||
@ -73,7 +73,7 @@ async def test_get_plug_by_name(dev: IotStrip):
|
||||
name = dev.children[0].alias
|
||||
assert dev.get_plug_by_name(name) == dev.children[0] # type: ignore[arg-type]
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
dev.get_plug_by_name("NONEXISTING NAME")
|
||||
|
||||
|
||||
@ -81,10 +81,10 @@ async def test_get_plug_by_name(dev: IotStrip):
|
||||
async def test_get_plug_by_index(dev: IotStrip):
|
||||
assert dev.get_plug_by_index(0) == dev.children[0]
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
dev.get_plug_by_index(-1)
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
with pytest.raises(KasaException):
|
||||
dev.get_plug_by_index(len(dev.children))
|
||||
|
||||
|
||||
|
@ -23,7 +23,7 @@ from typing import Dict, Generator, Optional
|
||||
from async_timeout import timeout as asyncio_timeout
|
||||
|
||||
from .deviceconfig import DeviceConfig
|
||||
from .exceptions import RetryableException, SmartDeviceException
|
||||
from .exceptions import KasaException, _RetryableError
|
||||
from .json import loads as json_loads
|
||||
from .protocol import BaseTransport
|
||||
|
||||
@ -129,24 +129,24 @@ class XorTransport(BaseTransport):
|
||||
await self._connect(self._timeout)
|
||||
except ConnectionRefusedError as ex:
|
||||
await self.reset()
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Unable to connect to the device: {self._host}:{self._port}: {ex}"
|
||||
) from ex
|
||||
except OSError as ex:
|
||||
await self.reset()
|
||||
if ex.errno in _NO_RETRY_ERRORS:
|
||||
raise SmartDeviceException(
|
||||
raise KasaException(
|
||||
f"Unable to connect to the device:"
|
||||
f" {self._host}:{self._port}: {ex}"
|
||||
) from ex
|
||||
else:
|
||||
raise RetryableException(
|
||||
raise _RetryableError(
|
||||
f"Unable to connect to the device:"
|
||||
f" {self._host}:{self._port}: {ex}"
|
||||
) from ex
|
||||
except Exception as ex:
|
||||
await self.reset()
|
||||
raise RetryableException(
|
||||
raise _RetryableError(
|
||||
f"Unable to connect to the device:" f" {self._host}:{self._port}: {ex}"
|
||||
) from ex
|
||||
except BaseException:
|
||||
@ -162,7 +162,7 @@ class XorTransport(BaseTransport):
|
||||
return await self._execute_send(request)
|
||||
except Exception as ex:
|
||||
await self.reset()
|
||||
raise RetryableException(
|
||||
raise _RetryableError(
|
||||
f"Unable to query the device {self._host}:{self._port}: {ex}"
|
||||
) from ex
|
||||
except BaseException:
|
||||
|
Loading…
Reference in New Issue
Block a user