Skip to content

Commit

Permalink
Update the project's minimum supported version to Python 3.9 (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
mill1000 authored Dec 11, 2024
1 parent f59fa74 commit 12493fa
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 32 deletions.
4 changes: 2 additions & 2 deletions msmart/base_device.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import time
from typing import List, Optional
from typing import Optional

from msmart.const import DeviceType
from msmart.frame import Frame
Expand All @@ -25,7 +25,7 @@ def __init__(self, *, ip: str, port: int, device_id: int, device_type: DeviceTyp
self._supported = False
self._online = False

async def _send_command(self, command: Frame) -> Optional[List[bytes]]:
async def _send_command(self, command: Frame) -> Optional[list[bytes]]:
"""Send a command to the device and return any responses."""

data = command.tobytes()
Expand Down
16 changes: 8 additions & 8 deletions msmart/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from asyncio import Lock
from datetime import datetime, timezone
from secrets import token_hex, token_urlsafe
from typing import Any, Dict, Optional, Tuple
from typing import Any, Optional

import httpx
from Crypto.Cipher import AES
Expand Down Expand Up @@ -92,7 +92,7 @@ def _parse_response(self, response) -> Any:

raise ApiError(body["msg"], code=response_code)

async def _post_request(self, url: str, headers: Dict[str, Any],
async def _post_request(self, url: str, headers: dict[str, Any],
contents: str, retries: int = RETRIES) -> Optional[dict]:
"""Post a request to the API."""

Expand All @@ -114,7 +114,7 @@ async def _post_request(self, url: str, headers: Dict[str, Any],
except httpx.RequestError as e:
raise CloudError("Request failed.") from e

async def _api_request(self, endpoint: str, body: Dict[str, Any]) -> Optional[dict]:
async def _api_request(self, endpoint: str, body: dict[str, Any]) -> Optional[dict]:
"""Make a request to the Midea cloud return the results."""

# Encode body as JSON
Expand All @@ -138,7 +138,7 @@ async def _api_request(self, endpoint: str, body: Dict[str, Any]) -> Optional[di
async with self._api_lock:
return await self._post_request(url, headers, contents)

def _build_request_body(self, data: Dict[str, Any]) -> Dict[str, Any]:
def _build_request_body(self, data: dict[str, Any]) -> dict[str, Any]:
"""Build a request body."""

# Set up the initial body
Expand Down Expand Up @@ -214,7 +214,7 @@ async def login(self, force: bool = False) -> None:
self._access_token = response["mdata"]["accessToken"]
_LOGGER.debug("Received accessToken: %s", self._access_token)

async def get_token(self, udpid: str) -> Tuple[str, str]:
async def get_token(self, udpid: str) -> tuple[str, str]:
"""Get token and key for the provided udpid."""

response = await self._api_request(
Expand All @@ -232,7 +232,7 @@ async def get_token(self, udpid: str) -> Tuple[str, str]:
# No matching udpId in the tokenlist
raise CloudError(f"No token/key found for udpid {udpid}.")

async def get_protocol_lua(self, device_type: DeviceType, sn: str) -> Tuple[str, str]:
async def get_protocol_lua(self, device_type: DeviceType, sn: str) -> tuple[str, str]:
"""Fetch and decode the protocol Lua file."""

response = await self._api_request(
Expand Down Expand Up @@ -264,7 +264,7 @@ async def get_protocol_lua(self, device_type: DeviceType, sn: str) -> Tuple[str,
encrypted_data).decode("UTF-8")
return (file_name, file_data)

async def get_plugin(self, device_type: DeviceType, sn: str) -> Tuple[str, bytes]:
async def get_plugin(self, device_type: DeviceType, sn: str) -> tuple[str, bytes]:
"""Request and download the device plugin."""

response = await self._api_request(
Expand Down Expand Up @@ -364,7 +364,7 @@ def encrypt_iam_password(self, login_id: str, password: str) -> str:

return sha.hexdigest()

def _get_app_key_and_iv(self) -> Tuple[bytes, bytes]:
def _get_app_key_and_iv(self) -> tuple[bytes, bytes]:
hash = hashlib.sha256(self.APP_KEY.encode()).hexdigest()
return (hash[:16].encode(), hash[16:32].encode())

Expand Down
6 changes: 3 additions & 3 deletions msmart/device/AC/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import struct
from collections import namedtuple
from enum import IntEnum
from typing import Any, Callable, Collection, Mapping, Optional, Tuple, Union
from typing import Any, Callable, Collection, Mapping, Optional, Union

import msmart.crc8 as crc8
from msmart.const import DeviceType, FrameType
Expand Down Expand Up @@ -1019,7 +1019,7 @@ def _parse(self, payload: memoryview) -> None:
def decode_bcd(d: int) -> int:
return 10 * (d >> 4) + (d & 0xF)

def parse_energy(d: bytes) -> Tuple[float, float]:
def parse_energy(d: bytes) -> tuple[float, float]:
bcd = (10000 * decode_bcd(d[0]) +
100 * decode_bcd(d[1]) +
1 * decode_bcd(d[2]) +
Expand All @@ -1030,7 +1030,7 @@ def parse_energy(d: bytes) -> Tuple[float, float]:
d[3]) / 10
return bcd, binary

def parse_power(d: bytes) -> Tuple[float, float]:
def parse_power(d: bytes) -> tuple[float, float]:
bcd = (1000 * decode_bcd(d[0]) +
10 * decode_bcd(d[1]) +
0.1 * decode_bcd(d[2]))
Expand Down
18 changes: 9 additions & 9 deletions msmart/device/AC/device.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import logging
from typing import Any, List, Optional, Union, cast
from typing import Any, Optional, Union, cast

from msmart.base_device import Device
from msmart.const import DeviceType
Expand Down Expand Up @@ -120,11 +120,11 @@ def __init__(self, ip: str, device_id: int, port: int, **kwargs) -> None:

# Support all known modes initially
self._supported_op_modes = cast(
List[AirConditioner.OperationalMode], AirConditioner.OperationalMode.list())
list[AirConditioner.OperationalMode], AirConditioner.OperationalMode.list())
self._supported_swing_modes = cast(
List[AirConditioner.SwingMode], AirConditioner.SwingMode.list())
list[AirConditioner.SwingMode], AirConditioner.SwingMode.list())
self._supported_fan_speeds = cast(
List[AirConditioner.FanSpeed], AirConditioner.FanSpeed.list())
list[AirConditioner.FanSpeed], AirConditioner.FanSpeed.list())
self._supports_custom_fan_speed = True
self._supports_eco = True
self._supports_turbo = True
Expand Down Expand Up @@ -364,7 +364,7 @@ def _update_capabilities(self, res: CapabilitiesResponse) -> None:
if res.ieco:
self._supported_properties.add(PropertyId.IECO)

async def _send_command_get_responses(self, command) -> List[Response]:
async def _send_command_get_responses(self, command) -> list[Response]:
"""Send a command and return all valid responses."""

responses = await super()._send_command(command)
Expand Down Expand Up @@ -615,7 +615,7 @@ def outdoor_temperature(self) -> Optional[float]:
return self._outdoor_temperature

@property
def supported_operation_modes(self) -> List[OperationalMode]:
def supported_operation_modes(self) -> list[OperationalMode]:
return self._supported_op_modes

@property
Expand All @@ -627,7 +627,7 @@ def operational_mode(self, mode: OperationalMode) -> None:
self._operational_mode = mode

@property
def supported_fan_speeds(self) -> List[FanSpeed]:
def supported_fan_speeds(self) -> list[FanSpeed]:
return self._supported_fan_speeds

@property
Expand Down Expand Up @@ -698,7 +698,7 @@ def breezeless(self, enable: bool) -> None:
else PropertyId.BREEZELESS)

@property
def supported_swing_modes(self) -> List[SwingMode]:
def supported_swing_modes(self) -> list[SwingMode]:
return self._supported_swing_modes

@property
Expand Down Expand Up @@ -885,7 +885,7 @@ def self_clean_active(self) -> bool:
return self._self_clean_active

@property
def supported_rate_selects(self) -> List[RateSelect]:
def supported_rate_selects(self) -> list[RateSelect]:
return self._supported_rate_selects

@property
Expand Down
6 changes: 3 additions & 3 deletions msmart/discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
import socket
import xml.etree.ElementTree as ET
from typing import Any, Dict, List, Optional, Type, cast
from typing import Any, Optional, Type, cast

from msmart.cloud import Cloud, CloudError
from msmart.const import (DEVICE_INFO_MSG, DISCOVERY_MSG,
Expand Down Expand Up @@ -152,7 +152,7 @@ async def discover(
account=None,
password=None,
auto_connect=True
) -> List[Device]:
) -> list[Device]:
"""Discover devices via broadcast."""

# Create lock if nonexistent within the context of the current loop
Expand Down Expand Up @@ -263,7 +263,7 @@ def _get_device_version(cls, data: bytes) -> int:
raise DiscoverError()

@classmethod
async def _get_device_info(cls, ip: str, version: int, data: bytes) -> Dict[str, Any]:
async def _get_device_info(cls, ip: str, version: int, data: bytes) -> dict[str, Any]:
"""Get device information.
V2/V3 devices return sufficient information in their discovery response.
Expand Down
6 changes: 3 additions & 3 deletions msmart/lan.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import datetime, timedelta, timezone
from enum import IntEnum
from hashlib import md5, sha256
from typing import AsyncGenerator, List, Optional, Tuple, Union, cast
from typing import AsyncGenerator, Optional, Union, cast

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
Expand Down Expand Up @@ -65,7 +65,7 @@ def alive(self) -> bool:

return True

def _format_socket_name(self, sockname: Tuple[str, int]) -> str:
def _format_socket_name(self, sockname: tuple[str, int]) -> str:
return f"{sockname[0]}:{sockname[1]}"

def connection_made(self, transport) -> None:
Expand Down Expand Up @@ -572,7 +572,7 @@ async def _read_available(self) -> AsyncGenerator[bytes, None]:
except asyncio.QueueEmpty:
pass

async def send(self, data: bytes, retries: int = RETRIES) -> List[bytes]:
async def send(self, data: bytes, retries: int = RETRIES) -> list[bytes]:
"""Send data via the LAN protocol. Connecting to the peer if necessary."""

# Connect if protocol doesn't exist or is dead
Expand Down
4 changes: 2 additions & 2 deletions msmart/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import functools
import logging
from enum import IntEnum
from typing import Any, Callable, List, Optional, cast
from typing import Any, Callable, Optional, cast

_LOGGER = logging.getLogger(__name__)

Expand All @@ -13,7 +13,7 @@ class MideaIntEnum(IntEnum):
"""Helper class to convert IntEnums to/from strings."""

@classmethod
def list(cls) -> List[MideaIntEnum]:
def list(cls) -> list[MideaIntEnum]:
return list(cls)

@classmethod
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ readme = "README.md"
authors = [
{name = "Tucker Kern", email = "[email protected]"},
]
requires-python = ">=3.8"
requires-python = ">=3.9"
license = {text = "MIT"}
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Topic :: Home Automation"
Expand Down

0 comments on commit 12493fa

Please sign in to comment.