Skip to content

Commit

Permalink
Merge branch 'master' into dsh0416/add-va2b-support
Browse files Browse the repository at this point in the history
  • Loading branch information
dsh0416 authored Jan 1, 2025
2 parents 6cf9755 + edb06c5 commit 469249e
Show file tree
Hide file tree
Showing 92 changed files with 1,243 additions and 1,231 deletions.
40 changes: 7 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,17 @@ jobs:
python-version: ["3.12"]

steps:
- uses: "actions/checkout@v3"
- uses: "actions/setup-python@v4"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v5"
with:
python-version: "${{ matrix.python-version }}"
- name: "Install dependencies"
run: |
python -m pip install --upgrade pip poetry
poetry install --extras docs
- name: "Run pyupgrade"
- name: "Run pre-commit hooks"
run: |
poetry run pre-commit run pyupgrade --all-files
- name: "Code formating (black)"
run: |
poetry run pre-commit run black --all-files
- name: "Code formating (flake8)"
run: |
poetry run pre-commit run flake8 --all-files
- name: "Order of imports (isort)"
run: |
poetry run pre-commit run isort --all-files
# - name: "Docstring formating (docformatter)"
# run: |
# poetry run pre-commit run docformatter --all-files
- name: "Potential security issues (bandit)"
run: |
poetry run pre-commit run bandit --all-files
- name: "Documentation build (sphinx)"
run: |
poetry run sphinx-build docs/ generated_docs
- name: "Typing checks (mypy)"
run: |
poetry run pre-commit run mypy --all-files
poetry run pre-commit run --all-files --verbose
tests:
name: "Python ${{ matrix.python-version}} on ${{ matrix.os }}"
Expand All @@ -59,17 +38,12 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.8"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.9"]
os: [ubuntu-latest, macos-latest, windows-latest]
# Exclude example, in case needed again in the future:
# exclude:
# - python-version: pypy3.8
# os: macos-latest


steps:
- uses: "actions/checkout@v3"
- uses: "actions/setup-python@v4"
- uses: "actions/checkout@v4"
- uses: "actions/setup-python@v5"
with:
python-version: "${{ matrix.python-version }}"
- name: "Install dependencies"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@master

- name: Setup python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.x"

Expand Down
15 changes: 10 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ repos:
- id: debug-statements
- id: check-ast

- repo: /~https://github.com/psf/black
rev: 24.2.0

repos:
- repo: /~https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.7.1
hooks:
- id: black
language_version: python3
# Run the linter.
#- id: ruff
# Run the formatter.
- id: ruff-format

- repo: /~https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
Expand Down Expand Up @@ -58,4 +63,4 @@ repos:
rev: v3.15.0
hooks:
- id: pyupgrade
args: ['--py38-plus']
args: ['--py39-plus']
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Status](/~https://github.com/rytilahti/python-miio/actions/workflows/ci.yml/badge.
[![Coverage
Status](https://codecov.io/gh/rytilahti/python-miio/branch/master/graph/badge.svg?token=lYKWubxkLU)](https://codecov.io/gh/rytilahti/python-miio)
[![Documentation status](https://readthedocs.org/projects/python-miio/badge/?version=latest)](https://python-miio.readthedocs.io/en/latest/?badge=latest)
[![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](/~https://github.com/psf/black)

This library (and its accompanying cli tool, `miiocli`) can be used to control devices using Xiaomi's
[miIO](/~https://github.com/OpenMiHome/mihome-binary-protocol/blob/master/doc/PROTOCOL.md)
Expand Down Expand Up @@ -50,7 +49,7 @@ Alternatively, you can install the latest development version from GitHub:
pip install git+/~https://github.com/rytilahti/python-miio.git

**This project is currently ongoing [a major refactoring effort](/~https://github.com/rytilahti/python-miio/issues/1114).
If you are interested in controlling modern (MIoT) devices, you want to use the git version until version 0.6.0 is released.**
If you are interested in controlling modern (MIoT) devices, you want to use the git version (or pre-releases, `pip install --pre python-miio`) until version 0.6.0 is released.**

## Getting started

Expand Down
24 changes: 12 additions & 12 deletions devtools/containers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from dataclasses import dataclass, field
from operator import attrgetter
from typing import Any, Dict, List, Optional
from typing import Any, Optional

from dataclasses_json import DataClassJsonMixin, config

Expand Down Expand Up @@ -45,7 +45,7 @@ def filename(self) -> str:

@dataclass
class ModelMapping(DataClassJsonMixin):
instances: List[InstanceInfo]
instances: list[InstanceInfo]

def info_for_model(self, model: str, *, status_filter="released") -> InstanceInfo:
matches = [inst for inst in self.instances if inst.model == model]
Expand Down Expand Up @@ -76,12 +76,12 @@ class Property(DataClassJsonMixin):
type: str
description: str
format: str
access: List[str]
access: list[str]

value_list: Optional[List[Dict[str, Any]]] = field(
value_list: Optional[list[dict[str, Any]]] = field(
default_factory=list, metadata=config(field_name="value-list")
) # type: ignore
value_range: Optional[List[int]] = field(
value_range: Optional[list[int]] = field(
default=None, metadata=config(field_name="value-range")
)

Expand Down Expand Up @@ -156,8 +156,8 @@ class Action(DataClassJsonMixin):
iid: int
type: str
description: str
out: List[Any] = field(default_factory=list)
in_: List[Any] = field(default_factory=list, metadata=config(field_name="in"))
out: list[Any] = field(default_factory=list)
in_: list[Any] = field(default_factory=list, metadata=config(field_name="in"))

def __repr__(self):
return f"aiid {self.iid} {self.description}: in: {self.in_} -> out: {self.out}"
Expand All @@ -178,7 +178,7 @@ class Event(DataClassJsonMixin):
iid: int
type: str
description: str
arguments: List[int]
arguments: list[int]

def __repr__(self):
return f"eiid {self.iid} ({self.description}): (args: {self.arguments})"
Expand All @@ -189,9 +189,9 @@ class Service(DataClassJsonMixin):
iid: int
type: str
description: str
properties: List[Property] = field(default_factory=list)
actions: List[Action] = field(default_factory=list)
events: List[Event] = field(default_factory=list)
properties: list[Property] = field(default_factory=list)
actions: list[Action] = field(default_factory=list)
events: list[Event] = field(default_factory=list)

def __repr__(self):
return f"siid {self.iid}: ({self.description}): {len(self.properties)} props, {len(self.actions)} actions"
Expand Down Expand Up @@ -220,7 +220,7 @@ def as_code(self):
class Device(DataClassJsonMixin):
type: str
description: str
services: List[Service] = field(default_factory=list)
services: list[Service] = field(default_factory=list)

def as_code(self):
s = ""
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
)
]

intersphinx_mapping = {"python": ("https://docs.python.org/3.8", None)}
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}

apidoc_module_dir = "../miio"
apidoc_output_dir = "api"
Expand Down
6 changes: 3 additions & 3 deletions miio/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Any, Dict
from typing import Any

import click

Expand Down Expand Up @@ -30,7 +30,7 @@
@click.version_option(package_name="python-miio")
@click.pass_context
def cli(ctx, debug: int, output: str):
logging_config: Dict[str, Any] = {
logging_config: dict[str, Any] = {
"level": logging.DEBUG if debug > 0 else logging.INFO
}
try:
Expand All @@ -56,7 +56,7 @@ def cli(ctx, debug: int, output: str):


for device_class in DeviceGroupMeta._device_classes:
cli.add_command(device_class.get_device_group())
cli.add_command(device_class.get_device_group()) # type: ignore[attr-defined]


@click.command()
Expand Down
10 changes: 5 additions & 5 deletions miio/click_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import logging
import re
from functools import partial, wraps
from typing import Any, Callable, ClassVar, Dict, List, Optional, Set, Type, Union
from typing import Any, Callable, ClassVar, Optional, Union

import click

Expand Down Expand Up @@ -111,9 +111,9 @@ def __init__(self, debug: int = 0, output: Optional[Callable] = None):


class DeviceGroupMeta(type):
_device_classes: Set[Type] = set()
_supported_models: ClassVar[List[str]]
_mappings: ClassVar[Dict[str, Any]]
_device_classes: set[type] = set()
_supported_models: ClassVar[list[str]]
_mappings: ClassVar[dict[str, Any]]

def __new__(mcs, name, bases, namespace):
commands = {}
Expand Down Expand Up @@ -150,7 +150,7 @@ def get_device_group(dcls):
return cls

@property
def supported_models(cls) -> List[str]:
def supported_models(cls) -> list[str]:
"""Return list of supported models."""
return list(cls._mappings.keys()) or cls._supported_models

Expand Down
8 changes: 4 additions & 4 deletions miio/cloud.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import logging
from typing import TYPE_CHECKING, Dict, Optional
from typing import TYPE_CHECKING, Optional

import click

Expand Down Expand Up @@ -127,14 +127,14 @@ def _parse_device_list(self, data, locale):
return devs

@classmethod
def available_locales(cls) -> Dict[str, str]:
def available_locales(cls) -> dict[str, str]:
"""Return available locales.
The value is the human-readable name of the locale.
"""
return AVAILABLE_LOCALES

def get_devices(self, locale: Optional[str] = None) -> Dict[str, CloudDeviceInfo]:
def get_devices(self, locale: Optional[str] = None) -> dict[str, CloudDeviceInfo]:
"""Return a list of available devices keyed with a device id.
If no locale is given, all known locales are browsed. If a device id is already
Expand All @@ -147,7 +147,7 @@ def get_devices(self, locale: Optional[str] = None) -> Dict[str, CloudDeviceInfo
self._micloud.get_devices(country=locale), locale=locale
)

all_devices: Dict[str, CloudDeviceInfo] = {}
all_devices: dict[str, CloudDeviceInfo] = {}
for loc in AVAILABLE_LOCALES:
if loc == "all":
continue
Expand Down
8 changes: 4 additions & 4 deletions miio/descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"""

from enum import Enum, Flag, auto
from typing import Any, Callable, Dict, List, Optional, Type
from typing import Any, Callable, Optional

import attr

Expand Down Expand Up @@ -55,7 +55,7 @@ class Descriptor:
#: Name of the attribute in the status container that contains the value, if applicable.
status_attribute: Optional[str] = None
#: Additional data related to this descriptor.
extras: Dict = attr.ib(factory=dict, repr=False)
extras: dict = attr.ib(factory=dict, repr=False)
#: Access flags (read, write, execute) for the described item.
access: AccessFlags = attr.ib(default=AccessFlags(0))

Expand Down Expand Up @@ -84,7 +84,7 @@ class ActionDescriptor(Descriptor):
method: Optional[Callable] = attr.ib(default=None, repr=False)
#: Name of the method in the device class that can be used to execute the action.
method_name: Optional[str] = attr.ib(default=None, repr=False)
inputs: Optional[List[Any]] = attr.ib(default=None, repr=True)
inputs: Optional[list[Any]] = attr.ib(default=None, repr=True)

access: AccessFlags = attr.ib(default=AccessFlags.Execute)

Expand Down Expand Up @@ -153,7 +153,7 @@ class EnumDescriptor(PropertyDescriptor):
#: Name of the attribute in the device class that returns the choices.
choices_attribute: Optional[str] = attr.ib(default=None, repr=False)
#: Enum class containing the available choices.
choices: Optional[Type[Enum]] = attr.ib(default=None, repr=False)
choices: Optional[type[Enum]] = attr.ib(default=None, repr=False)

@property
def __cli_output__(self) -> str:
Expand Down
6 changes: 3 additions & 3 deletions miio/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class Device(metaclass=DeviceGroupMeta):

retry_count = 3
timeout = 5
_mappings: Dict[str, Any] = {}
_supported_models: List[str] = []
_mappings: dict[str, Any] = {}
_supported_models: list[str] = []

def __init_subclass__(cls, **kwargs):
"""Overridden to register all integrations to the factory."""
Expand Down Expand Up @@ -182,7 +182,7 @@ def raw_id(self) -> int:
return self._protocol.raw_id

@property
def supported_models(self) -> List[str]:
def supported_models(self) -> list[str]:
"""Return a list of supported models."""
return list(self._mappings.keys()) or self._supported_models

Expand Down
12 changes: 6 additions & 6 deletions miio/devicefactory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Dict, List, Optional, Type
from typing import Optional

import click

Expand All @@ -22,11 +22,11 @@ class DeviceFactory:
dev = DeviceFactory.create("127.0.0.1", 32*"0")
"""

_integration_classes: List[Type[Device]] = []
_supported_models: Dict[str, Type[Device]] = {}
_integration_classes: list[type[Device]] = []
_supported_models: dict[str, type[Device]] = {}

@classmethod
def register(cls, integration_cls: Type[Device]):
def register(cls, integration_cls: type[Device]):
"""Register class for to the registry."""
cls._integration_classes.append(integration_cls)
_LOGGER.debug("Registering %s", integration_cls.__name__)
Expand All @@ -44,13 +44,13 @@ def register(cls, integration_cls: Type[Device]):
cls._supported_models[model] = integration_cls

@classmethod
def supported_models(cls) -> Dict[str, Type[Device]]:
def supported_models(cls) -> dict[str, type[Device]]:
"""Return a dictionary of models and their corresponding implementation
classes."""
return cls._supported_models

@classmethod
def integrations(cls) -> List[Type[Device]]:
def integrations(cls) -> list[type[Device]]:
"""Return the list of integration classes."""
return cls._integration_classes

Expand Down
Loading

0 comments on commit 469249e

Please sign in to comment.