Clean up all remaining ruff errors; lint is now fully green

Config:
- .ruff.toml target-version py310 -> py312, matching pyproject's
  requires-python (>=3.12) and the 3.12 'type' aliases switch.py
  already ships. Unblocks two false invalid-syntax errors.
- Ignore D102 in tests (test-class methods), consistent with the
  existing D100/D103 test ignores.

Code (no behavior changes):
- COM812 trailing commas + UP017 dt.UTC via ruff --fix, black re-wrap
- RUF002: unicode minus/en-dash -> ASCII hyphen in docstrings
- SIM105: contextlib.suppress for the lux-reading parse in config_flow
- TRY300: move return out of try in _read_lux_sensor
- PLC0415: hoist inline asyncio/logging imports to module top
- E741: rename ambiguous 'l' comprehension variable to light_id
- PT006/PT018: parametrize tuple + split compound assertion
- D102: docstring for OptionsFlow.async_step_init
- ARG001/ARG002: noqa with justification on HA-required signatures
- PLR0912/PLR0915: noqa on prepare_adaptation_data and
  _update_attrs_and_maybe_adapt_lights (upstream-inherited complexity;
  splitting would hurt readability more than it helps)

./scripts/lint passes clean; 148 tests pass.
This commit is contained in:
Casey 2026-06-05 12:16:40 +02:00
commit 1c56674379
11 changed files with 68 additions and 37 deletions

View file

@ -123,7 +123,10 @@ def _remove_orphan_lux_sensors(
registry.async_remove(entry.entity_id)
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
async def async_migrate_entry(
hass: HomeAssistant, # noqa: ARG001 — signature required by HA
config_entry: ConfigEntry,
) -> bool:
"""Reject older config-entry versions with a friendly recreate message.
Spec R8 + design D4: this fork deliberately does not migrate upstream

View file

@ -145,12 +145,12 @@ def _tanh_day_curve(
"""Piecewise tanh ramp from value_min to value_max around sunrise/sunset.
Schema (specs/options-flow/spec.md R4):
- t ≤ t_sunrise − half_width: value_min
- t_sunrise − half_width < t < t_sunrise + half_width:
- t ≤ t_sunrise - half_width: value_min
- t_sunrise - half_width < t < t_sunrise + half_width:
tanh ramp min → max
- t_sunrise + half_width ≤ t ≤ t_sunset − half_width:
- t_sunrise + half_width ≤ t ≤ t_sunset - half_width:
value_max
- t_sunset − half_width < t < t_sunset + half_width:
- t_sunset - half_width < t < t_sunset + half_width:
tanh ramp max → min
- t ≥ t_sunset + half_width: value_min
"""

View file

@ -6,6 +6,7 @@ Diagnostics) using HA's `section()` helper and native selectors throughout.
See design.md decisions 1, 5, 6, 9, 14 and specs/options-flow/spec.md R1-R7.
"""
import contextlib
import logging
from typing import Any
@ -464,6 +465,7 @@ class OptionsFlowHandler(OptionsFlowWithReload):
continue
async def async_step_init(self, user_input: dict[str, Any] | None = None):
"""Render the single-page options form and handle its submission."""
conf = self.config_entry
if conf.source == SOURCE_IMPORT:
return self.async_abort(reason="yaml_managed")
@ -508,10 +510,8 @@ class OptionsFlowHandler(OptionsFlowWithReload):
if lux_sensor_id:
lux_state = self.hass.states.get(lux_sensor_id)
if lux_state and lux_state.state not in ("unavailable", "unknown"):
try:
with contextlib.suppress(TypeError, ValueError):
lux_reading = f"{float(lux_state.state):.0f} lx"
except (TypeError, ValueError):
pass
return self.async_show_form(
step_id="init",

View file

@ -1,5 +1,6 @@
"""Utility functions for HA core."""
import asyncio
import logging
from collections.abc import Awaitable, Callable
@ -81,8 +82,6 @@ def setup_service_call_interceptor(
call.data,
)
# Call original service handler with processed data
import asyncio
target = existing_service.job.target
if asyncio.iscoroutinefunction(target):
await target(call)

View file

@ -1112,9 +1112,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
return None
try:
value = float(state.state)
return value if value > 0 else None
except (TypeError, ValueError):
return None
return value if value > 0 else None
def _call_on_remove_callbacks(self) -> None:
"""Call callbacks registered by async_on_remove."""
@ -1211,7 +1211,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
force=False,
)
async def prepare_adaptation_data(
# Complexity inherited from upstream's per-light adaptation pipeline;
# splitting it would obscure the linear lux-gate -> service-data flow.
async def prepare_adaptation_data( # noqa: PLR0912, PLR0915
self,
light: str,
transition: int | None = None,
@ -1502,7 +1504,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
SIGNAL_OUTPUTS_UPDATED.format(entry_id=self._config_entry.entry_id),
)
async def _update_attrs_and_maybe_adapt_lights(
async def _update_attrs_and_maybe_adapt_lights( # noqa: PLR0912
self,
*,
context: Context,
@ -1542,7 +1544,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
if self._lux_turned_off and self._target_lux > 0:
current_lux = self._read_lux_sensor()
if current_lux is not None and current_lux <= self._target_lux:
recovering = [l for l in self._lux_turned_off if l in lights]
recovering = [
light_id for light_id in self._lux_turned_off if light_id in lights
]
self._lux_turned_off -= set(recovering)
for light_id in recovering:
_LOGGER.debug(
@ -1653,7 +1657,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
def fire_manual_control_event(
self,
light: str,
context: Context,
context: Context, # noqa: ARG002 — signature kept for manager compatibility
) -> None:
"""Deprecated no-op kept for compatibility with the manager bus listener.