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
30 changes: 28 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,40 @@ allowed_tags = [
major_tags= ["refactor"]

[tool.ruff]
lint.ignore = ["F403", "E741"]
lint.select=["E", "F", "UP", "I"]
lint.ignore = []
# TODO: Enable ASYNC (flake8-async) in a dedicated follow-up PR once async open() and timeout parameters are refactored.
lint.select = [
"A",
"B",
"BLE",
"C4",
"E",
"F",
"FA",
"I",
"ICN",
"ISC",
"PGH",
"PIE",
"UP",
"W",
"YTT",
]
line-length = 120
extend-exclude = ["roborock/map/proto/*_pb2.py"]

[tool.ruff.lint.per-file-ignores]
"*/__init__.py" = ["F401"]
"roborock/__init__.py" = ["F403"]
"roborock/data/__init__.py" = ["F403"]
"roborock/data/b01_q7/__init__.py" = ["F403"]
"roborock/data/b01_q10/__init__.py" = ["F403"]
"roborock/data/dyad/__init__.py" = ["F403"]
"roborock/data/mower/__init__.py" = ["F403"]
"roborock/data/v1/__init__.py" = ["F403"]
"roborock/data/zeo/__init__.py" = ["F403"]
"roborock/map/proto/*_pb2.py" = ["E501", "I001", "UP009"]
"tests/*" = ["B010"]

[[tool.mypy.overrides]]
module = ["roborock.map.proto.*"]
Expand Down
4 changes: 2 additions & 2 deletions roborock/broadcast_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
from asyncio import BaseTransport, Lock

from construct import ( # type: ignore
from construct import ( # type: ignore[import-untyped]
Bytes,
Checksum,
GreedyBytes,
Expand Down Expand Up @@ -63,7 +63,7 @@ def datagram_received(self, data: bytes, _):
parsed_message = BroadcastMessage(duid=json_payload["duid"], ip=json_payload["ip"], version=version)
_LOGGER.debug(f"Received broadcast: {parsed_message}")
self.devices_found.append(parsed_message)
except Exception as e:
except Exception as e: # noqa: BLE001
_LOGGER.warning(f"Failed to decode message: {data!r}. Error: {e}")

async def discover(self) -> list[BroadcastMessage]:
Expand Down
22 changes: 11 additions & 11 deletions roborock/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
```
"""

# ruff: noqa: BLE001

import asyncio
import datetime
import functools
Expand All @@ -38,9 +40,9 @@
import click
import click_shell
import yaml
from pyshark import FileCapture # type: ignore
from pyshark.capture.live_capture import LiveCapture, UnknownInterfaceException # type: ignore
from pyshark.packet.packet import Packet # type: ignore
from pyshark import FileCapture # type: ignore[import-untyped]
from pyshark.capture.live_capture import LiveCapture, UnknownInterfaceException # type: ignore[import-untyped]
from pyshark.packet.packet import Packet # type: ignore[import-untyped]
except ImportError as err:
raise SystemExit(
f"The 'roborock' command line tool requires extra dependencies that are not installed ({err.name}).\n"
Expand Down Expand Up @@ -307,7 +309,7 @@ async def set(self, value: CacheData) -> None:
@click.pass_context
def cli(ctx, debug: int):
logging_config: dict[str, Any] = {"level": logging.DEBUG if debug > 0 else logging.INFO}
logging.basicConfig(**logging_config) # type: ignore
logging.basicConfig(**logging_config) # type: ignore[call-overload]
ctx.obj = RoborockContext()


Expand Down Expand Up @@ -988,26 +990,24 @@ def on_package(packet: Packet):
local_key,
)
print(f"Received request: {f}")
except BaseException as e:
except Exception as e:
print(e)
pass
elif packet.ip.src == device_ip:
try:
f, buffer["data"] = MessageParser.parse(
buffer["data"] + bytes.fromhex(packet.DATA.data),
local_key,
)
print(f"Received response: {f}")
except BaseException as e:
except Exception as e:
print(e)
pass

try:
await capture.packets_from_tshark(on_package, close_tshark=not file_provided)
except UnknownInterfaceException:
except UnknownInterfaceException as err:
raise RoborockException(
"You need to run 'rvictl -s XXXXXXXX-XXXXXXXXXXXXXXXX' first, with an iPhone connected to usb port"
)
) from err


def _parse_diagnostic_file(diagnostic_path: Path) -> dict[str, dict[str, Any]]:
Expand Down Expand Up @@ -1319,7 +1319,7 @@ def write_markdown_table(product_features: dict[str, dict[str, any]], all_featur
]
# Regular features are the remaining keys, sorted alphabetically
# We filter out the special rows to avoid duplicating them.
sorted_features = sorted(list(all_features - set(special_rows)))
sorted_features = sorted(all_features - set(special_rows))

header = ["Feature"] + sorted_products

Expand Down
2 changes: 1 addition & 1 deletion roborock/data/code_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def _missing_(cls: type[Self], key) -> Self:
if warning not in completed_warnings:
completed_warnings.add(warning)
_LOGGER.warning(warning)
return cls.unknown # type: ignore
return cls.unknown # type: ignore[attr-defined]
default_value = next(item for item in cls)
warning = f"Missing {cls.__name__} code: {key} - defaulting to {default_value}"
if warning not in completed_warnings:
Expand Down
10 changes: 5 additions & 5 deletions roborock/data/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def _attr_repr(obj: Any) -> str:
continue
try:
v = getattr(obj, k)
except (RuntimeError, Exception):
except Exception: # noqa: BLE001
continue
if callable(v):
continue
Expand Down Expand Up @@ -210,7 +210,7 @@ class Reference(RoborockBase):
r: str | None = None
a: str | None = None
m: str | None = None
l: str | None = None
l: str | None = None # noqa: E741


@dataclass
Expand Down Expand Up @@ -379,9 +379,9 @@ class HomeDataSchedule(RoborockBase):
class HomeData(RoborockBase):
id: int
name: str
products: list[HomeDataProduct] = field(default_factory=lambda: [])
devices: list[HomeDataDevice] = field(default_factory=lambda: [])
received_devices: list[HomeDataDevice] = field(default_factory=lambda: [])
products: list[HomeDataProduct] = field(default_factory=list)
devices: list[HomeDataDevice] = field(default_factory=list)
received_devices: list[HomeDataDevice] = field(default_factory=list)
lon: Any | None = None
lat: Any | None = None
geo_name: Any | None = None
Expand Down
2 changes: 1 addition & 1 deletion roborock/data/v1/v1_code_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class RoborockDssCodes(RoborockEnum):
def _missing_(cls: type[Self], key) -> Self:
# If the calculated value is not provided, then it should be viewed as okay.
# As the math will sometimes result in you getting numbers that don't matter.
return cls.okay # type: ignore
return cls.okay # type: ignore[attr-defined]


class ClearWaterBoxStatus(RoborockDssCodes):
Expand Down
2 changes: 1 addition & 1 deletion roborock/device_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ def from_feature_flags(
elif (product_features := f.metadata.get("product_features")) is not None:
if product_nickname is not None:
available_features = PRODUCT_FEATURE_MAP.get(product_nickname, [])
if any(feat in available_features for feat in product_features): # type: ignore
if any(feat in available_features for feat in product_features):
kwargs[f.name] = True

# The app combines runtime shake-mop, model shake/spin, and roller-mop
Expand Down
2 changes: 1 addition & 1 deletion roborock/devices/rpc/b01_q7_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def on_message(response_message: RoborockMessage) -> None:
return
try:
response = response_matcher(response_message)
except Exception as ex:
except Exception as ex: # noqa: BLE001
future.set_exception(ex)
return
if response is not None:
Expand Down
2 changes: 1 addition & 1 deletion roborock/devices/traits/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
]


class Trait(ABC):
class Trait(ABC): # noqa: B024
"""Base class for all traits."""
2 changes: 1 addition & 1 deletion roborock/devices/traits/v1/clean_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def convert(self, response: common.V1ResponseData) -> CleanRecord:
rec.square_meter_area or 0
)
return final_record
except Exception:
except Exception: # noqa: BLE001
# Return final record when an exception occurred
return final_record
# There are still a few unknown variables in this.
Expand Down
2 changes: 1 addition & 1 deletion roborock/devices/traits/v1/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,6 @@ async def _refresh_rooms(self) -> list[HomeDataRoom]:
rooms_by_id.update({room.iot_id: room for room in shared_rooms})
return list(rooms_by_id.values())
return await self._web_api.get_rooms()
except Exception:
except Exception: # noqa: BLE001
_LOGGER.debug("Failed to fetch rooms from web API", exc_info=True)
return []
2 changes: 1 addition & 1 deletion roborock/devices/transport/local_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ async def _keep_alive_loop(self) -> None:
await self._ping()
except asyncio.CancelledError:
break
except Exception:
except Exception: # noqa: BLE001
self._logger.debug("Keep-alive ping failed", exc_info=True)
# Retry next interval

Expand Down
2 changes: 1 addition & 1 deletion roborock/map/b01_grid_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def solve_calibration(
min_sx, max_sx, min_sy, max_sy = min(sx), max(sx), min(sy), max(sy)
if (max_sx - min_sx) >= w or (max_sy - min_sy) >= h:
continue # path wider/taller than the map at this resolution
pts = list(zip(sx, sy))
pts = list(zip(sx, sy, strict=True))
# Slide so every point stays in-bounds: px = px_f + ox in [0, w), py = oy - py_f in [0, h).
for ox in range(ceil(-min_sx), ceil(w - max_sx)):
for oy in range(ceil(max_sy), ceil(h + min_sy)):
Expand Down
4 changes: 2 additions & 2 deletions roborock/map/b01_q10_map_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ def _drop_stray_leading_point(points: list[Q10Point]) -> list[Q10Point]:
"""
if len(points) < 3:
return points
steps = [math.hypot(b.x - a.x, b.y - a.y) for a, b in zip(points, points[1:])]
steps = [math.hypot(b.x - a.x, b.y - a.y) for a, b in zip(points, points[1:], strict=False)]
median_rest = statistics.median(steps[1:])
if median_rest > 0 and steps[0] > _STRAY_POINT_STEP_RATIO * median_rest:
return points[1:]
Expand Down Expand Up @@ -425,7 +425,7 @@ def _infer_layout(decoded: bytes, width: int) -> tuple[int, bytes, bytes]:
up with the marker. Used as a fallback when the header carries no usable
height.
"""
for room_count in range(0, _MAX_ROOMS + 1):
for room_count in range(_MAX_ROOMS + 1):
room_data_length = 2 + room_count * _ROOM_RECORD_LENGTH
area = len(decoded) - room_data_length
if area <= 0 or area % width:
Expand Down
2 changes: 1 addition & 1 deletion roborock/mqtt/roborock_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ async def _run_reconnect_loop(self, start_future: asyncio.Future[None] | None) -
await self._connection_task
except asyncio.CancelledError:
_LOGGER.debug("MQTT connection task cancelled")
except Exception:
except Exception: # noqa: BLE001
# Exceptions are logged and handled in _run_connection.
# There is a special case for exceptions on startup where we return
# immediately. Otherwise, we let the reconnect loop retry with
Expand Down
6 changes: 3 additions & 3 deletions roborock/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from collections.abc import Callable
from urllib.parse import urlparse

from construct import ( # type: ignore
from construct import ( # type: ignore[import-untyped]
Bytes,
Checksum,
ChecksumError,
Expand Down Expand Up @@ -65,7 +65,7 @@ def ensure_bytes(msg: bytes | str) -> bytes:
@staticmethod
def encode_timestamp(_timestamp: int) -> bytes:
hex_value = f"{_timestamp:x}".zfill(8)
return "".join(list(map(lambda idx: hex_value[idx], [5, 6, 3, 7, 1, 2, 0, 4]))).encode()
return "".join([hex_value[idx] for idx in [5, 6, 3, 7, 1, 2, 0, 4]]).encode()

@staticmethod
def md5(data: bytes) -> bytes:
Expand Down Expand Up @@ -470,7 +470,7 @@ def build(
}
)
return self.con.build(
{"messages": [message for message in messages], "remaining": b""},
{"messages": list(messages), "remaining": b""},
local_key=local_key,
prefixed=prefixed,
connect_nonce=connect_nonce,
Expand Down
8 changes: 5 additions & 3 deletions roborock/protocols/a01_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def decode_rpc_response(message: RoborockMessage) -> dict[int, Any]:
try:
unpadded = unpad(message.payload, AES.block_size)
except ValueError as err:
raise RoborockException(f"Unable to unpad A01 payload: {err}")
raise RoborockException(f"Unable to unpad A01 payload: {err}") from err

try:
payload = json.loads(unpadded.decode())
Expand All @@ -74,5 +74,7 @@ def decode_rpc_response(message: RoborockMessage) -> dict[int, Any]:
raise RoborockException(f"Invalid A01 message format: 'dps' should be a dictionary for {message.payload!r}")
try:
return {int(key): value for key, value in datapoints.items()}
except ValueError:
raise RoborockException(f"Invalid A01 message format: 'dps' key should be an integer for {message.payload!r}")
except ValueError as err:
raise RoborockException(
f"Invalid A01 message format: 'dps' key should be an integer for {message.payload!r}"
) from err
6 changes: 4 additions & 2 deletions roborock/protocols/b01_q7_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ def decode_rpc_response(message: RoborockMessage) -> dict[int, Any]:
raise RoborockException(f"Invalid B01 message format: 'dps' should be a dictionary for {message.payload!r}")
try:
return {int(key): value for key, value in datapoints.items()}
except ValueError:
raise RoborockException(f"Invalid B01 message format: 'dps' key should be an integer for {message.payload!r}")
except ValueError as err:
raise RoborockException(
f"Invalid B01 message format: 'dps' key should be an integer for {message.payload!r}"
) from err


@dataclass
Expand Down
4 changes: 2 additions & 2 deletions roborock/testing/v1_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ async def _handle_publish(self, message: RoborockMessage, channel: FakeChannel)
msg_id = inner["id"]
method = inner["method"]
params = inner.get("params", [])
except Exception as e:
except Exception as e: # noqa: BLE001
_LOGGER.debug("Failed to parse plaintext JSON RPC payload: %s", e, exc_info=True)
return

Expand All @@ -358,7 +358,7 @@ async def _handle_publish(self, message: RoborockMessage, channel: FakeChannel)
if handler:
try:
result = handler(params)
except Exception as e:
except Exception as e: # noqa: BLE001
error = str(e)
_LOGGER.debug("Error executing command handler for %s: %s", method, e, exc_info=True)
else:
Expand Down
2 changes: 1 addition & 1 deletion roborock/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


def unpack_list(value: list[T], size: int) -> list[T | None]:
return (value + [None] * size)[:size] # type: ignore
return (value + [None] * size)[:size]
Comment thread
allenporter marked this conversation as resolved.


class RoborockLoggerAdapter(logging.LoggerAdapter):
Expand Down
6 changes: 3 additions & 3 deletions tests/data/test_code_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_invalid_from_value() -> None:


@pytest.mark.parametrize(
"input, expected",
"input_value, expected",
[
("START_CLEAN", B01_Q10_DP.START_CLEAN),
("start_clean", B01_Q10_DP.START_CLEAN),
Expand All @@ -103,9 +103,9 @@ def test_invalid_from_value() -> None:
(999999, None),
],
)
def test_from_any_optional(input: str | int, expected: B01_Q10_DP | None) -> None:
def test_from_any_optional(input_value: str | int, expected: B01_Q10_DP | None) -> None:
"""Test from_any_optional method."""
assert B01_Q10_DP.from_any_optional(input) == expected
assert B01_Q10_DP.from_any_optional(input_value) == expected


def test_homedata_product_unknown_category():
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/local_async_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def response_queue_fixture() -> Generator[asyncio.Queue[bytes], None, None]:
response_queue: asyncio.Queue[bytes] = asyncio.Queue()
yield response_queue
if not response_queue.empty():
warnings.warn("Some enqueued local device responses were not consumed during the test")
warnings.warn("Some enqueued local device responses were not consumed during the test", stacklevel=2)


@pytest.fixture(name="local_async_request_handler")
Expand Down
Loading
Loading