mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-06 18:54:08 +00:00
Remove support for python <3.11 (#1273)
Python 3.11 ships with latest Debian Bookworm. pypy is not that widely used with this library based on statistics. It could be added back when pypy supports python 3.11.
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
|
@@ -5,9 +5,10 @@ from __future__ import annotations
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from contextlib import contextmanager
|
||||
from functools import singledispatch, update_wrapper, wraps
|
||||
from typing import TYPE_CHECKING, Any, Callable, Final
|
||||
from typing import TYPE_CHECKING, Any, Final
|
||||
|
||||
import asyncclick as click
|
||||
|
||||
|
@@ -2,10 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import zoneinfo
|
||||
from datetime import datetime
|
||||
|
||||
import asyncclick as click
|
||||
import zoneinfo
|
||||
|
||||
from kasa import (
|
||||
Device,
|
||||
|
@@ -110,11 +110,9 @@ from abc import ABC, abstractmethod
|
||||
from collections.abc import Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, tzinfo
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, TypeAlias
|
||||
from warnings import warn
|
||||
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from .credentials import Credentials as _Credentials
|
||||
from .device_type import DeviceType
|
||||
from .deviceconfig import (
|
||||
@@ -213,7 +211,7 @@ class Device(ABC):
|
||||
self._last_update: Any = None
|
||||
_LOGGER.debug("Initializing %s of type %s", host, type(self))
|
||||
self._device_type = DeviceType.Unknown
|
||||
# TODO: typing Any is just as using Optional[Dict] would require separate
|
||||
# TODO: typing Any is just as using dict | None would require separate
|
||||
# checks in accessors. the @updated_required decorator does not ensure
|
||||
# mypy that these are not accessed incorrectly.
|
||||
self._discovery_info: dict[str, Any] | None = None
|
||||
|
@@ -27,14 +27,12 @@ Living Room Bulb
|
||||
|
||||
"""
|
||||
|
||||
# Note that this module does not work with from __future__ import annotations
|
||||
# due to it's use of type returned by fields() which becomes a string with the import.
|
||||
# https://bugs.python.org/issue39442
|
||||
# ruff: noqa: FA100
|
||||
# Module cannot use from __future__ import annotations until migrated to mashumaru
|
||||
# as dataclass.fields() will not resolve the type.
|
||||
import logging
|
||||
from dataclasses import asdict, dataclass, field, fields, is_dataclass
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict, Union
|
||||
from typing import TYPE_CHECKING, Any, Optional, TypedDict
|
||||
|
||||
from .credentials import Credentials
|
||||
from .exceptions import KasaException
|
||||
@@ -118,15 +116,15 @@ class DeviceConnectionParameters:
|
||||
|
||||
device_family: DeviceFamily
|
||||
encryption_type: DeviceEncryptionType
|
||||
login_version: Optional[int] = None
|
||||
login_version: int | None = None
|
||||
https: bool = False
|
||||
|
||||
@staticmethod
|
||||
def from_values(
|
||||
device_family: str,
|
||||
encryption_type: str,
|
||||
login_version: Optional[int] = None,
|
||||
https: Optional[bool] = None,
|
||||
login_version: int | None = None,
|
||||
https: bool | None = None,
|
||||
) -> "DeviceConnectionParameters":
|
||||
"""Return connection parameters from string values."""
|
||||
try:
|
||||
@@ -145,7 +143,7 @@ class DeviceConnectionParameters:
|
||||
) from ex
|
||||
|
||||
@staticmethod
|
||||
def from_dict(connection_type_dict: Dict[str, Any]) -> "DeviceConnectionParameters":
|
||||
def from_dict(connection_type_dict: dict[str, Any]) -> "DeviceConnectionParameters":
|
||||
"""Return connection parameters from dict."""
|
||||
if (
|
||||
isinstance(connection_type_dict, dict)
|
||||
@@ -163,9 +161,9 @@ class DeviceConnectionParameters:
|
||||
|
||||
raise KasaException(f"Invalid connection type data for {connection_type_dict}")
|
||||
|
||||
def to_dict(self) -> Dict[str, Union[str, int, bool]]:
|
||||
def to_dict(self) -> dict[str, str | int | bool]:
|
||||
"""Convert connection params to dict."""
|
||||
result: Dict[str, Union[str, int]] = {
|
||||
result: dict[str, str | int] = {
|
||||
"device_family": self.device_family.value,
|
||||
"encryption_type": self.encryption_type.value,
|
||||
"https": self.https,
|
||||
@@ -183,17 +181,17 @@ class DeviceConfig:
|
||||
#: IP address or hostname
|
||||
host: str
|
||||
#: Timeout for querying the device
|
||||
timeout: Optional[int] = DEFAULT_TIMEOUT
|
||||
timeout: int | None = DEFAULT_TIMEOUT
|
||||
#: Override the default 9999 port to support port forwarding
|
||||
port_override: Optional[int] = None
|
||||
port_override: int | None = None
|
||||
#: Credentials for devices requiring authentication
|
||||
credentials: Optional[Credentials] = None
|
||||
credentials: Credentials | None = None
|
||||
#: Credentials hash for devices requiring authentication.
|
||||
#: If credentials are also supplied they take precendence over credentials_hash.
|
||||
#: Credentials hash can be retrieved from :attr:`Device.credentials_hash`
|
||||
credentials_hash: Optional[str] = None
|
||||
credentials_hash: str | None = None
|
||||
#: The protocol specific type of connection. Defaults to the legacy type.
|
||||
batch_size: Optional[int] = None
|
||||
batch_size: int | None = None
|
||||
#: The batch size for protoools supporting multiple request batches.
|
||||
connection_type: DeviceConnectionParameters = field(
|
||||
default_factory=lambda: DeviceConnectionParameters(
|
||||
@@ -208,7 +206,7 @@ class DeviceConfig:
|
||||
#: Set a custom http_client for the device to use.
|
||||
http_client: Optional["ClientSession"] = field(default=None, compare=False)
|
||||
|
||||
aes_keys: Optional[KeyPairDict] = None
|
||||
aes_keys: KeyPairDict | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.connection_type is None:
|
||||
@@ -219,9 +217,9 @@ class DeviceConfig:
|
||||
def to_dict(
|
||||
self,
|
||||
*,
|
||||
credentials_hash: Optional[str] = None,
|
||||
credentials_hash: str | None = None,
|
||||
exclude_credentials: bool = False,
|
||||
) -> Dict[str, Dict[str, str]]:
|
||||
) -> dict[str, dict[str, str]]:
|
||||
"""Convert device config to dict."""
|
||||
if credentials_hash is not None or exclude_credentials:
|
||||
self.credentials = None
|
||||
@@ -230,7 +228,7 @@ class DeviceConfig:
|
||||
return _dataclass_to_dict(self)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(config_dict: Dict[str, Dict[str, str]]) -> "DeviceConfig":
|
||||
def from_dict(config_dict: dict[str, dict[str, str]]) -> "DeviceConfig":
|
||||
"""Return device config from dict."""
|
||||
if isinstance(config_dict, dict):
|
||||
return _dataclass_from_dict(DeviceConfig, config_dict)
|
||||
|
@@ -89,26 +89,19 @@ import logging
|
||||
import secrets
|
||||
import socket
|
||||
import struct
|
||||
from asyncio import timeout as asyncio_timeout
|
||||
from asyncio.transports import DatagramTransport
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass, field
|
||||
from pprint import pformat as pf
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Dict,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Type,
|
||||
cast,
|
||||
)
|
||||
|
||||
from aiohttp import ClientSession
|
||||
|
||||
# When support for cpython older than 3.11 is dropped
|
||||
# async_timeout can be replaced with asyncio.timeout
|
||||
from async_timeout import timeout as asyncio_timeout
|
||||
from mashumaro import field_options
|
||||
from mashumaro.config import BaseConfig
|
||||
|
||||
@@ -156,7 +149,7 @@ class ConnectAttempt(NamedTuple):
|
||||
OnDiscoveredCallable = Callable[[Device], Coroutine]
|
||||
OnUnsupportedCallable = Callable[[UnsupportedDeviceError], Coroutine]
|
||||
OnConnectAttemptCallable = Callable[[ConnectAttempt, bool], None]
|
||||
DeviceDict = Dict[str, Device]
|
||||
DeviceDict = dict[str, Device]
|
||||
|
||||
NEW_DISCOVERY_REDACTORS: dict[str, Callable[[Any], Any] | None] = {
|
||||
"device_id": lambda x: "REDACTED_" + x[9::],
|
||||
@@ -676,7 +669,7 @@ class Discover:
|
||||
data = redact_data(info, IOT_REDACTORS) if Discover._redact_data else info
|
||||
_LOGGER.debug("[DISCOVERY] %s << %s", config.host, pf(data))
|
||||
|
||||
device_class = cast(Type[IotDevice], Discover._get_device_class(info))
|
||||
device_class = cast(type[IotDevice], Discover._get_device_class(info))
|
||||
device = device_class(config.host, config=config)
|
||||
sys_info = info["system"]["get_sysinfo"]
|
||||
if device_type := sys_info.get("mic_type", sys_info.get("type")):
|
||||
@@ -830,9 +823,9 @@ class EncryptionScheme(_DiscoveryBaseMixin):
|
||||
"""Base model for encryption scheme of discovery result."""
|
||||
|
||||
is_support_https: bool
|
||||
encrypt_type: Optional[str] = None # noqa: UP007
|
||||
http_port: Optional[int] = None # noqa: UP007
|
||||
lv: Optional[int] = None # noqa: UP007
|
||||
encrypt_type: str | None = None
|
||||
http_port: int | None = None
|
||||
lv: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -854,18 +847,18 @@ class DiscoveryResult(_DiscoveryBaseMixin):
|
||||
ip: str
|
||||
mac: str
|
||||
mgt_encrypt_schm: EncryptionScheme
|
||||
device_name: Optional[str] = None # noqa: UP007
|
||||
encrypt_info: Optional[EncryptionInfo] = None # noqa: UP007
|
||||
encrypt_type: Optional[list[str]] = None # noqa: UP007
|
||||
decrypted_data: Optional[dict] = None # noqa: UP007
|
||||
is_reset_wifi: Optional[bool] = field( # noqa: UP007
|
||||
device_name: str | None = None
|
||||
encrypt_info: EncryptionInfo | None = None
|
||||
encrypt_type: list[str] | None = None
|
||||
decrypted_data: dict | None = None
|
||||
is_reset_wifi: bool | None = field(
|
||||
metadata=field_options(alias="isResetWiFi"), default=None
|
||||
)
|
||||
|
||||
firmware_version: Optional[str] = None # noqa: UP007
|
||||
hardware_version: Optional[str] = None # noqa: UP007
|
||||
hw_ver: Optional[str] = None # noqa: UP007
|
||||
owner: Optional[str] = None # noqa: UP007
|
||||
is_support_iot_cloud: Optional[bool] = None # noqa: UP007
|
||||
obd_src: Optional[str] = None # noqa: UP007
|
||||
factory_default: Optional[bool] = None # noqa: UP007
|
||||
firmware_version: str | None = None
|
||||
hardware_version: str | None = None
|
||||
hw_ver: str | None = None
|
||||
owner: str | None = None
|
||||
is_support_iot_cloud: bool | None = None
|
||||
obd_src: str | None = None
|
||||
factory_default: bool | None = None
|
||||
|
@@ -39,7 +39,7 @@ class DeviceError(KasaException):
|
||||
"""Base exception for device errors."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
self.error_code: SmartErrorCode | None = kwargs.get("error_code", None)
|
||||
self.error_code: SmartErrorCode | None = kwargs.get("error_code")
|
||||
super().__init__(*args)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
@@ -68,10 +68,11 @@ Type.Choice
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
from functools import cached_property
|
||||
from typing import TYPE_CHECKING, Any, Callable, Coroutine
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .device import Device
|
||||
@@ -244,7 +245,7 @@ class Feature:
|
||||
if self.attribute_setter is None:
|
||||
raise ValueError("Tried to set read-only feature.")
|
||||
if self.type == Feature.Type.Number: # noqa: SIM102
|
||||
if not isinstance(value, (int, float)):
|
||||
if not isinstance(value, int | float):
|
||||
raise ValueError("value must be a number")
|
||||
if value < self.minimum_value or value > self.maximum_value:
|
||||
raise ValueError(
|
||||
|
@@ -6,7 +6,7 @@ import asyncio
|
||||
import logging
|
||||
import ssl
|
||||
import time
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
from yarl import URL
|
||||
@@ -98,7 +98,7 @@ class HttpClient:
|
||||
# This allows the json parameter to be used to pass other
|
||||
# types of data such as async_generator and still have json
|
||||
# returned.
|
||||
if json and not isinstance(json, Dict):
|
||||
if json and not isinstance(json, dict):
|
||||
data = json
|
||||
json = None
|
||||
try:
|
||||
@@ -131,7 +131,7 @@ class HttpClient:
|
||||
raise _ConnectionError(
|
||||
f"Device connection error: {self._config.host}: {ex}", ex
|
||||
) from ex
|
||||
except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as ex:
|
||||
except (aiohttp.ServerTimeoutError, TimeoutError) as ex:
|
||||
raise TimeoutError(
|
||||
"Unable to query the device, "
|
||||
+ f"timed out: {self._config.host}: {ex}",
|
||||
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import re
|
||||
from enum import Enum
|
||||
from typing import Optional, cast
|
||||
from typing import cast
|
||||
|
||||
from pydantic.v1 import BaseModel, Field, root_validator
|
||||
|
||||
@@ -49,7 +49,7 @@ class TurnOnBehavior(BaseModel):
|
||||
"""
|
||||
|
||||
#: Index of preset to use, or ``None`` for the last known state.
|
||||
preset: Optional[int] = Field(alias="index", default=None) # noqa: UP007
|
||||
preset: int | None = Field(alias="index", default=None)
|
||||
#: Wanted behavior
|
||||
mode: BehaviorMode
|
||||
|
||||
|
@@ -17,9 +17,9 @@ from __future__ import annotations
|
||||
import functools
|
||||
import inspect
|
||||
import logging
|
||||
from collections.abc import Mapping, Sequence
|
||||
from collections.abc import Callable, Mapping, Sequence
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
from typing import TYPE_CHECKING, Any, Callable, cast
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from warnings import warn
|
||||
|
||||
from ..device import Device, WifiNetwork, _DeviceInfo
|
||||
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
from typing import cast
|
||||
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from ..cachedzoneinfo import CachedZoneInfo
|
||||
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import asdict
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic.v1 import BaseModel, Field
|
||||
|
||||
@@ -17,22 +17,25 @@ from ..iotmodule import IotModule
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
# type ignore can be removed after migration mashumaro:
|
||||
# error: Signature of "__replace__" incompatible with supertype "LightState"
|
||||
|
||||
class IotLightPreset(BaseModel, LightState):
|
||||
|
||||
class IotLightPreset(BaseModel, LightState): # type: ignore[override]
|
||||
"""Light configuration preset."""
|
||||
|
||||
index: int = Field(kw_only=True)
|
||||
brightness: int = Field(kw_only=True)
|
||||
|
||||
# These are not available for effect mode presets on light strips
|
||||
hue: Optional[int] = Field(kw_only=True, default=None) # noqa: UP007
|
||||
saturation: Optional[int] = Field(kw_only=True, default=None) # noqa: UP007
|
||||
color_temp: Optional[int] = Field(kw_only=True, default=None) # noqa: UP007
|
||||
hue: int | None = Field(kw_only=True, default=None)
|
||||
saturation: int | None = Field(kw_only=True, default=None)
|
||||
color_temp: int | None = Field(kw_only=True, default=None)
|
||||
|
||||
# Variables for effect mode presets
|
||||
custom: Optional[int] = Field(kw_only=True, default=None) # noqa: UP007
|
||||
id: Optional[str] = Field(kw_only=True, default=None) # noqa: UP007
|
||||
mode: Optional[int] = Field(kw_only=True, default=None) # noqa: UP007
|
||||
custom: int | None = Field(kw_only=True, default=None)
|
||||
id: str | None = Field(kw_only=True, default=None)
|
||||
mode: int | None = Field(kw_only=True, default=None)
|
||||
|
||||
|
||||
class LightPreset(IotModule, LightPresetInterface):
|
||||
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from pydantic.v1 import BaseModel
|
||||
|
||||
@@ -35,20 +34,20 @@ class Rule(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
enable: bool
|
||||
wday: List[int] # noqa: UP006
|
||||
wday: list[int]
|
||||
repeat: bool
|
||||
|
||||
# start action
|
||||
sact: Optional[Action] # noqa: UP007
|
||||
sact: Action | None
|
||||
stime_opt: TimeOption
|
||||
smin: int
|
||||
|
||||
eact: Optional[Action] # noqa: UP007
|
||||
eact: Action | None
|
||||
etime_opt: TimeOption
|
||||
emin: int
|
||||
|
||||
# Only on bulbs
|
||||
s_light: Optional[Dict] # noqa: UP006,UP007
|
||||
s_light: dict | None
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone, tzinfo
|
||||
from datetime import UTC, datetime, tzinfo
|
||||
|
||||
from ...exceptions import KasaException
|
||||
from ...interfaces import Time as TimeInterface
|
||||
@@ -13,7 +13,7 @@ from ..iottimezone import get_timezone, get_timezone_index
|
||||
class Time(IotModule, TimeInterface):
|
||||
"""Implements the timezone settings."""
|
||||
|
||||
_timezone: tzinfo = timezone.utc
|
||||
_timezone: tzinfo = UTC
|
||||
|
||||
def query(self) -> dict:
|
||||
"""Request time and timezone."""
|
||||
|
@@ -2,7 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
import orjson
|
||||
|
@@ -29,7 +29,7 @@ If you know or expect the module to exist you can access by index:
|
||||
|
||||
Modules support typing via the Module names in Module:
|
||||
|
||||
>>> from typing_extensions import reveal_type, TYPE_CHECKING
|
||||
>>> from typing import reveal_type, TYPE_CHECKING
|
||||
>>> light_effect = dev.modules.get("LightEffect")
|
||||
>>> light_effect_typed = dev.modules.get(Module.LightEffect)
|
||||
>>> if TYPE_CHECKING:
|
||||
|
@@ -50,9 +50,7 @@ def _test_module_mapping_typing() -> None:
|
||||
|
||||
This is tested during the mypy run and needs to be in this file.
|
||||
"""
|
||||
from typing import Any, NewType, cast
|
||||
|
||||
from typing_extensions import assert_type
|
||||
from typing import Any, NewType, assert_type, cast
|
||||
|
||||
from .iot.iotmodule import IotModule
|
||||
from .module import Module
|
||||
|
@@ -4,8 +4,9 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
from pprint import pformat as pf
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import (
|
||||
|
@@ -17,10 +17,9 @@ import hashlib
|
||||
import logging
|
||||
import struct
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any, TypeVar, cast
|
||||
|
||||
# When support for cpython older than 3.11 is dropped
|
||||
# async_timeout can be replaced with asyncio.timeout
|
||||
from ..deviceconfig import DeviceConfig
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -36,7 +35,7 @@ if TYPE_CHECKING:
|
||||
|
||||
def redact_data(data: _T, redactors: dict[str, Callable[[Any], Any] | None]) -> _T:
|
||||
"""Redact sensitive data for logging."""
|
||||
if not isinstance(data, (dict, list)):
|
||||
if not isinstance(data, dict | list):
|
||||
return data
|
||||
|
||||
if isinstance(data, list):
|
||||
|
@@ -11,8 +11,9 @@ import base64
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
from pprint import pformat as pf
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ..exceptions import (
|
||||
SMART_AUTHENTICATION_ERRORS,
|
||||
|
@@ -4,13 +4,11 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from collections.abc import Coroutine
|
||||
from asyncio import timeout as asyncio_timeout
|
||||
from collections.abc import Callable, Coroutine
|
||||
from datetime import date
|
||||
from typing import TYPE_CHECKING, Callable, Optional
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
# When support for cpython older than 3.11 is dropped
|
||||
# async_timeout can be replaced with asyncio.timeout
|
||||
from async_timeout import timeout as asyncio_timeout
|
||||
from pydantic.v1 import BaseModel, Field, validator
|
||||
|
||||
from ...exceptions import KasaException
|
||||
@@ -41,11 +39,11 @@ class UpdateInfo(BaseModel):
|
||||
"""Update info status object."""
|
||||
|
||||
status: int = Field(alias="type")
|
||||
version: Optional[str] = Field(alias="fw_ver", default=None) # noqa: UP007
|
||||
release_date: Optional[date] = None # noqa: UP007
|
||||
release_notes: Optional[str] = Field(alias="release_note", default=None) # noqa: UP007
|
||||
fw_size: Optional[int] = None # noqa: UP007
|
||||
oem_id: Optional[str] = None # noqa: UP007
|
||||
version: str | None = Field(alias="fw_ver", default=None)
|
||||
release_date: date | None = None
|
||||
release_notes: str | None = Field(alias="release_note", default=None)
|
||||
fw_size: int | None = None
|
||||
oem_id: str | None = None
|
||||
needs_upgrade: bool = Field(alias="need_to_upgrade")
|
||||
|
||||
@validator("release_date", pre=True)
|
||||
@@ -58,9 +56,7 @@ class UpdateInfo(BaseModel):
|
||||
@property
|
||||
def update_available(self) -> bool:
|
||||
"""Return True if update available."""
|
||||
if self.status != 0:
|
||||
return True
|
||||
return False
|
||||
return self.status != 0
|
||||
|
||||
|
||||
class Firmware(SmartModule):
|
||||
|
@@ -2,9 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
from datetime import UTC, datetime, timedelta, timezone, tzinfo
|
||||
from typing import cast
|
||||
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from ...cachedzoneinfo import CachedZoneInfo
|
||||
@@ -19,7 +18,7 @@ class Time(SmartModule, TimeInterface):
|
||||
REQUIRED_COMPONENT = "time"
|
||||
QUERY_GETTER_NAME = "get_device_time"
|
||||
|
||||
_timezone: tzinfo = timezone.utc
|
||||
_timezone: tzinfo = UTC
|
||||
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
|
@@ -6,7 +6,7 @@ import base64
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Mapping, Sequence
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
from datetime import UTC, datetime, timedelta, tzinfo
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from ..device import Device, WifiNetwork, _DeviceInfo
|
||||
@@ -520,7 +520,7 @@ class SmartDevice(Device):
|
||||
return time_mod.time
|
||||
|
||||
# We have no device time, use current local time.
|
||||
return datetime.now(timezone.utc).astimezone().replace(microsecond=0)
|
||||
return datetime.now(UTC).astimezone().replace(microsecond=0)
|
||||
|
||||
@property
|
||||
def on_since(self) -> datetime | None:
|
||||
|
@@ -4,9 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Awaitable, Callable, Coroutine
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from typing_extensions import Concatenate, ParamSpec, TypeVar
|
||||
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar
|
||||
|
||||
from ..exceptions import DeviceError, KasaException, SmartErrorCode
|
||||
from ..module import Module
|
||||
|
@@ -2,9 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone, tzinfo
|
||||
from datetime import UTC, datetime, tzinfo
|
||||
from typing import cast
|
||||
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from ...cachedzoneinfo import CachedZoneInfo
|
||||
@@ -20,7 +19,7 @@ class Time(SmartCameraModule, TimeInterface):
|
||||
QUERY_MODULE_NAME = "system"
|
||||
QUERY_SECTION_NAMES = "basic"
|
||||
|
||||
_timezone: tzinfo = timezone.utc
|
||||
_timezone: tzinfo = UTC
|
||||
_time: datetime
|
||||
|
||||
def _initialize_features(self) -> None:
|
||||
|
@@ -12,7 +12,7 @@ import logging
|
||||
import time
|
||||
from collections.abc import AsyncGenerator
|
||||
from enum import Enum, auto
|
||||
from typing import TYPE_CHECKING, Any, Dict, cast
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from cryptography.hazmat.primitives import hashes, padding, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding as asymmetric_padding
|
||||
@@ -193,7 +193,7 @@ class AesTransport(BaseTransport):
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
resp_dict = cast(Dict[str, Any], resp_dict)
|
||||
resp_dict = cast(dict[str, Any], resp_dict)
|
||||
assert self._encryption_session is not None
|
||||
|
||||
self._handle_response_error_code(
|
||||
@@ -326,7 +326,7 @@ class AesTransport(BaseTransport):
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
resp_dict = cast(Dict[str, Any], resp_dict)
|
||||
resp_dict = cast(dict[str, Any], resp_dict)
|
||||
|
||||
self._handle_response_error_code(resp_dict, "Unable to complete handshake")
|
||||
|
||||
|
@@ -51,7 +51,8 @@ import secrets
|
||||
import struct
|
||||
import time
|
||||
from asyncio import Future
|
||||
from typing import TYPE_CHECKING, Any, Generator, cast
|
||||
from collections.abc import Generator
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
@@ -9,7 +9,7 @@ import logging
|
||||
import secrets
|
||||
import ssl
|
||||
from enum import Enum, auto
|
||||
from typing import TYPE_CHECKING, Any, Dict, cast
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from yarl import URL
|
||||
|
||||
@@ -227,7 +227,7 @@ class SslAesTransport(BaseTransport):
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
resp_dict = cast(Dict[str, Any], resp_dict)
|
||||
resp_dict = cast(dict[str, Any], resp_dict)
|
||||
assert self._encryption_session is not None
|
||||
|
||||
if "result" in resp_dict and "response" in resp_dict["result"]:
|
||||
@@ -393,7 +393,7 @@ class SslAesTransport(BaseTransport):
|
||||
raise AuthenticationError(f"Error trying handshake1: {resp_dict}")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
resp_dict = cast(Dict[str, Any], resp_dict)
|
||||
resp_dict = cast(dict[str, Any], resp_dict)
|
||||
|
||||
server_nonce = resp_dict["result"]["data"]["nonce"]
|
||||
device_confirm = resp_dict["result"]["data"]["device_confirm"]
|
||||
|
@@ -18,12 +18,9 @@ import errno
|
||||
import logging
|
||||
import socket
|
||||
import struct
|
||||
from asyncio import timeout as asyncio_timeout
|
||||
from collections.abc import Generator
|
||||
|
||||
# When support for cpython older than 3.11 is dropped
|
||||
# async_timeout can be replaced with asyncio.timeout
|
||||
from async_timeout import timeout as asyncio_timeout
|
||||
|
||||
from kasa.deviceconfig import DeviceConfig
|
||||
from kasa.exceptions import KasaException, _RetryableError
|
||||
from kasa.json import loads as json_loads
|
||||
|
Reference in New Issue
Block a user