Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
poetry install --extras docs
- name: "Run pre-commit hooks"
run: |
poetry run pre-commit run --all-files --verbose
poetry run pre-commit run --all-files --verbose

tests:
name: "Python ${{ matrix.python-version}} on ${{ matrix.os }}"
Expand All @@ -38,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13"]
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
Expand Down
7 changes: 3 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ repos:
- id: debug-statements
- id: check-ast


repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.7.1
hooks:
# Run the linter.
#- id: ruff
- id: ruff
args: [--fix]
# Run the formatter.
- id: ruff-format

Expand Down Expand Up @@ -63,4 +62,4 @@ repos:
rev: v3.15.0
hooks:
- id: pyupgrade
args: ['--py39-plus']
args: ['--py311-plus']
4 changes: 2 additions & 2 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version: 2

build:
os: ubuntu-20.04
os: ubuntu-22.04
tools:
python: "3.9"
python: "3.12"

python:
install:
Expand Down
8 changes: 4 additions & 4 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, Optional
from typing import Any

from dataclasses_json import DataClassJsonMixin, config

Expand Down Expand Up @@ -78,14 +78,14 @@ class Property(DataClassJsonMixin):
format: str
access: list[str]

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

unit: Optional[str] = None
unit: str | None = None

def __repr__(self):
return f"piid: {self.iid} ({self.description}): ({self.format}, unit: {self.unit}) (acc: {self.access})"
Expand Down
9 changes: 3 additions & 6 deletions devtools/miottemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ def _print(file):
@cli.command()
def download_mapping():
"""Download model<->urn mapping."""
click.echo(
"Downloading and saving model<->urn mapping to %s" % MIOTSPEC_MAPPING.name
)
click.echo(f"Downloading and saving model<->urn mapping to {MIOTSPEC_MAPPING.name}")
url = "http://miot-spec.org/miot-spec-v2/instances?status=all"
res = requests.get(url, timeout=5)

Expand Down Expand Up @@ -132,16 +130,15 @@ def download(ctx, urn, model):

if not MIOTSPEC_MAPPING.exists():
click.echo(
"miotspec mapping doesn't exist, downloading to %s"
% MIOTSPEC_MAPPING.name
f"miotspec mapping doesn't exist, downloading to {MIOTSPEC_MAPPING.name}"
)
ctx.invoke(download_mapping)

mapping = get_mapping()
model = mapping.info_for_model(model)

url = f"https://miot-spec.org/miot-spec-v2/instance?type={model.type}"
click.echo("Going to download %s" % url)
click.echo(f"Going to download {url}")
content = requests.get(url, timeout=5)
save_to = model.filename
click.echo(f"Saving data to {save_to}")
Expand Down
17 changes: 9 additions & 8 deletions miio/click_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import json
import logging
import re
from collections.abc import Callable
from functools import partial, wraps
from typing import Any, Callable, ClassVar, Optional, Union
from typing import Any, ClassVar

import click

Expand All @@ -30,15 +31,15 @@ def validate_ip(ctx, param, value):
ipaddress.ip_address(value)
return value
except ValueError as ex:
raise click.BadParameter("Invalid IP: %s" % ex)
raise click.BadParameter(f"Invalid IP: {ex}")


def validate_token(ctx, param, value):
if value is None:
return None
token_len = len(value)
if token_len != 32:
raise click.BadParameter("Token length != 32 chars: %s" % token_len)
raise click.BadParameter(f"Token length != 32 chars: {token_len}")
return value


Expand Down Expand Up @@ -101,11 +102,11 @@ def convert(self, value, param, ctx):
try:
return ast.literal_eval(value)
except ValueError:
self.fail("%s is not a valid literal" % value, param, ctx)
self.fail(f"{value} is not a valid literal", param, ctx)


class GlobalContextObject:
def __init__(self, debug: int = 0, output: Optional[Callable] = None):
def __init__(self, debug: int = 0, output: Callable | None = None):
self.debug = debug
self.output = output

Expand Down Expand Up @@ -272,7 +273,7 @@ def command_callback(self, miio_command, miio_device, *args, **kwargs):

def get_command(self, ctx, cmd_name):
if cmd_name not in self.commands:
ctx.fail("Unknown command (%s)" % cmd_name)
ctx.fail(f"Unknown command ({cmd_name})")

cmd = self.commands[cmd_name]
return self.commands[cmd_name].wrap(
Expand All @@ -290,8 +291,8 @@ def command(*decorators, name=None, default_output=None, **kwargs):


def format_output(
msg_fmt: Union[str, Callable] = "",
result_msg_fmt: Union[str, Callable] = "{result}",
msg_fmt: str | Callable = "",
result_msg_fmt: str | Callable = "{result}",
):
def decorator(func):
@wraps(func)
Expand Down
6 changes: 3 additions & 3 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, Optional
from typing import TYPE_CHECKING

import click

Expand Down Expand Up @@ -134,7 +134,7 @@ def available_locales(cls) -> dict[str, str]:
"""
return AVAILABLE_LOCALES

def get_devices(self, locale: Optional[str] = None) -> dict[str, CloudDeviceInfo]:
def get_devices(self, locale: str | None = 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 Down Expand Up @@ -178,7 +178,7 @@ def cloud(ctx: click.Context, username, password):
@click.pass_context
@click.option("--locale", prompt=True, type=click.Choice(AVAILABLE_LOCALES.keys()))
@click.option("--raw", is_flag=True, default=False)
def cloud_list(ctx: click.Context, locale: Optional[str], raw: bool):
def cloud_list(ctx: click.Context, locale: str | None, raw: bool):
"""List devices connected to the cloud account."""

ci = ctx.obj
Expand Down
27 changes: 15 additions & 12 deletions miio/descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
:func:`~miio.devicestatus.action` decorators over creating the descriptors manually.
"""

from __future__ import annotations

from collections.abc import Callable
from enum import Enum, Flag, auto
from typing import Any, Callable, Optional
from typing import Any

import attr

Expand Down Expand Up @@ -49,11 +52,11 @@ class Descriptor:
#: Human readable name.
name: str
#: Type of the property, if applicable.
type: Optional[type] = None
type: type | None = None
#: Unit of the property, if applicable.
unit: Optional[str] = None
unit: str | None = None
#: Name of the attribute in the status container that contains the value, if applicable.
status_attribute: Optional[str] = None
status_attribute: str | None = None
#: Additional data related to this descriptor.
extras: dict = attr.ib(factory=dict, repr=False)
#: Access flags (read, write, execute) for the described item.
Expand Down Expand Up @@ -81,10 +84,10 @@ class ActionDescriptor(Descriptor):
"""Describes a button exposed by the device."""

# Callable to execute the action.
method: Optional[Callable] = attr.ib(default=None, repr=False)
method: Callable | None = 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)
method_name: str | None = attr.ib(default=None, repr=False)
inputs: list[Any] | None = attr.ib(default=None, repr=True)

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

Expand Down Expand Up @@ -125,10 +128,10 @@ class PropertyDescriptor(Descriptor):
#: Constraint type defining the allowed values for an integer property.
constraint: PropertyConstraint = attr.ib(default=PropertyConstraint.Unset)
#: Callable to set the value of the property.
setter: Optional[Callable] = attr.ib(default=None, repr=False)
setter: Callable | None = attr.ib(default=None, repr=False)
#: Name of the method in the device class that can be used to set the value.
#: If set, the callable with this name will override the `setter` attribute.
setter_name: Optional[str] = attr.ib(default=None, repr=False)
setter_name: str | None = attr.ib(default=None, repr=False)

@property
def __cli_output__(self) -> str:
Expand All @@ -151,9 +154,9 @@ class EnumDescriptor(PropertyDescriptor):

constraint: PropertyConstraint = PropertyConstraint.Choice
#: Name of the attribute in the device class that returns the choices.
choices_attribute: Optional[str] = attr.ib(default=None, repr=False)
choices_attribute: str | None = attr.ib(default=None, repr=False)
#: Enum class containing the available choices.
choices: Optional[type[Enum]] = attr.ib(default=None, repr=False)
choices: type[Enum] | None = attr.ib(default=None, repr=False)

@property
def __cli_output__(self) -> str:
Expand Down Expand Up @@ -181,7 +184,7 @@ class RangeDescriptor(PropertyDescriptor):
step: int
#: Name of the attribute in the device class that returns the range.
#: If set, this will override the individual min/max/step values.
range_attribute: Optional[str] = attr.ib(default=None)
range_attribute: str | None = attr.ib(default=None)
type: type = int
constraint: PropertyConstraint = PropertyConstraint.Range

Expand Down
24 changes: 12 additions & 12 deletions miio/device.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
from enum import Enum
from typing import Any, Dict, List, Optional, Union, cast, final # noqa: F401
from typing import Any, final

import click

Expand Down Expand Up @@ -49,19 +49,19 @@ def __init_subclass__(cls, **kwargs):

def __init__(
self,
ip: Optional[str] = None,
token: Optional[str] = None,
ip: str | None = None,
token: str | None = None,
start_id: int = 0,
debug: int = 0,
lazy_discover: bool = True,
timeout: Optional[int] = None,
timeout: int | None = None,
*,
model: Optional[str] = None,
model: str | None = None,
) -> None:
self.ip = ip
self.token: Optional[str] = token
self._model: Optional[str] = model
self._info: Optional[DeviceInfo] = None
self.token: str | None = token
self._model: str | None = model
self._info: DeviceInfo | None = None
# TODO: use _info's noneness instead?
self._initialized: bool = False
self._descriptors: DescriptorCollection = DescriptorCollection(device=self)
Expand All @@ -74,8 +74,8 @@ def __init__(
def send(
self,
command: str,
parameters: Optional[Any] = None,
retry_count: Optional[int] = None,
parameters: Any | None = None,
retry_count: int | None = None,
*,
extra_parameters=None,
) -> Any:
Expand Down Expand Up @@ -329,7 +329,7 @@ def call_action(self, name: str, params=None):
try:
act = self.actions()[name]
except KeyError:
raise ValueError("Unable to find action '%s'" % name)
raise ValueError(f"Unable to find action '{name}'")

if params is None:
return act.method()
Expand All @@ -346,7 +346,7 @@ def change_setting(self, name: str, params=None):
try:
setting = self.settings()[name]
except KeyError:
raise ValueError("Unable to find setting '%s'" % name)
raise ValueError(f"Unable to find setting '{name}'")

params = params if params is not None else []

Expand Down
5 changes: 2 additions & 3 deletions miio/devicefactory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
from typing import Optional

import click

Expand Down Expand Up @@ -78,14 +77,14 @@ def class_for_model(cls, model: str):
)
return impl

raise DeviceException("No implementation found for model %s" % model)
raise DeviceException(f"No implementation found for model {model}")

@classmethod
def create(
self,
host: str,
token: str,
model: Optional[str] = None,
model: str | None = None,
*,
force_generic_miot=False,
) -> Device:
Expand Down
Loading
Loading