Skip to content

Commit 602e5a5

Browse files
committed
no-mistakes(lint): format repo with ruff; fix annotation drift in new code
1 parent 0723535 commit 602e5a5

30 files changed

Lines changed: 749 additions & 410 deletions

‎__init__.py‎

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@
3636

3737
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.BINARY_SENSOR]
3838

39+
3940
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
4041
"""Set up the Lumentree integration."""
4142
# Config flow is handled automatically by Home Assistant
4243
# when config_flow: true is set in manifest.json
4344
return True
4445

46+
4547
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
4648
"""Set up Lumentree from a config entry."""
4749
_LOGGER.info(f"Setting up Lumentree: {entry.title} ({entry.entry_id})")
@@ -74,7 +76,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
7476
if "_error" in device_api_info:
7577
_LOGGER.error(f"API error getting device info: {device_api_info['_error']}")
7678
raise ConfigEntryNotReady(f"API error: {device_api_info['_error']}")
77-
hass.data[DOMAIN][entry.entry_id]['device_api_info'] = device_api_info
79+
hass.data[DOMAIN][entry.entry_id]["device_api_info"] = device_api_info
7880
_LOGGER.info(
7981
f"Stored API info: Model={device_api_info.get('deviceType')}, "
8082
f"ID={device_api_info.get('deviceId')}"
@@ -95,12 +97,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
9597
monthly_coord = MonthlyStatsCoordinator(hass, aggregator, device_sn, entry.entry_id)
9698
yearly_coord = YearlyStatsCoordinator(hass, aggregator, device_sn, entry.entry_id)
9799
total_coord = TotalStatsCoordinator(hass, aggregator, device_sn, entry.entry_id)
98-
hass.data[DOMAIN][entry.entry_id].update({
99-
"daily_coordinator": daily_coord,
100-
"monthly_coordinator": monthly_coord,
101-
"yearly_coordinator": yearly_coord,
102-
"total_coordinator": total_coord,
103-
})
100+
hass.data[DOMAIN][entry.entry_id].update(
101+
{
102+
"daily_coordinator": daily_coord,
103+
"monthly_coordinator": monthly_coord,
104+
"yearly_coordinator": yearly_coord,
105+
"total_coordinator": total_coord,
106+
}
107+
)
104108
_LOGGER.info(f"Created total coordinator for {device_sn}")
105109

106110
# Prime daily coordinator first (non-blocking), then stats coordinators after delay
@@ -205,7 +209,9 @@ async def _async_stop_mqtt(event: Event) -> None:
205209
await client_to_stop.disconnect()
206210

207211
entry.async_on_unload(_cancel_timer_on_unload)
208-
entry.async_on_unload(hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_mqtt))
212+
entry.async_on_unload(
213+
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_mqtt)
214+
)
209215

210216
# Services: backfill_now, recompute_month_year, purge_cache, backfill_all, backfill_gaps,
211217
# mark_empty_dates, mark_coverage_range
@@ -218,9 +224,7 @@ def _required_year(call) -> int:
218224
"""
219225
raw = call.data.get("year")
220226
if raw is None:
221-
raise HomeAssistantError(
222-
"The 'year' field is required for this service."
223-
)
227+
raise HomeAssistantError("The 'year' field is required for this service.")
224228
try:
225229
return int(raw)
226230
except (TypeError, ValueError) as err:
@@ -294,19 +298,25 @@ async def _svc_purge_and_backfill(call):
294298
max_years = int(max_years_val) if max_years_val is not None else 5
295299
optimize_cache = call.data.get("optimize_cache", True)
296300

297-
_LOGGER.warning(f"Purging ALL cache for device {device_id} and starting fresh smart backfill...")
301+
_LOGGER.warning(
302+
f"Purging ALL cache for device {device_id} and starting fresh smart backfill..."
303+
)
298304
result = await hass.async_add_executor_job(cache_io.purge_device, device_id)
299305
_LOGGER.info(f"Purge result: {result}")
300306

301307
_LOGGER.info(f"Starting smart backfill for {max_years} years...")
302-
stats = await aggregator.smart_backfill(max_years=max_years, optimize_cache=optimize_cache)
308+
stats = await aggregator.smart_backfill(
309+
max_years=max_years, optimize_cache=optimize_cache
310+
)
303311
_LOGGER.info(f"Purge and smart backfill completed: {stats}")
304312

305313
async def _svc_smart_backfill(call):
306314
"""Smart backfill using getYearData/getMonthData APIs."""
307315
max_years = int(call.data.get("max_years", 10))
308316
optimize_cache = call.data.get("optimize_cache", True)
309-
stats = await aggregator.smart_backfill(max_years=max_years, optimize_cache=optimize_cache)
317+
stats = await aggregator.smart_backfill(
318+
max_years=max_years, optimize_cache=optimize_cache
319+
)
310320
_LOGGER.info(f"Smart backfill completed: {stats}")
311321

312322
async def _svc_backfill_all(call):
@@ -323,7 +333,9 @@ async def _svc_backfill_gaps(call):
323333
async def _svc_backfill_empty_dates(call):
324334
max_years = int(call.data.get("max_years", 5))
325335
max_days_per_run = int(call.data.get("max_days_per_run", 100))
326-
result = await aggregator.backfill_empty_dates(max_years=max_years, max_days_per_run=max_days_per_run)
336+
result = await aggregator.backfill_empty_dates(
337+
max_years=max_years, max_days_per_run=max_days_per_run
338+
)
327339
_LOGGER.info(
328340
f"backfill_empty_dates service completed: "
329341
f"recovered={result.get('recovered', 0)}, "
@@ -336,7 +348,9 @@ async def _svc_enable_purge_on_startup(call):
336348
new_options = entry.options.copy()
337349
new_options["purge_and_backfill_on_startup"] = True
338350
hass.config_entries.async_update_entry(entry, options=new_options)
339-
_LOGGER.warning("purge_and_backfill_on_startup has been enabled. It will run on next restart and auto-disable after completion.")
351+
_LOGGER.warning(
352+
"purge_and_backfill_on_startup has been enabled. It will run on next restart and auto-disable after completion."
353+
)
340354

341355
async def _svc_disable_purge_on_startup(call):
342356
"""Disable purge and backfill on startup."""
@@ -386,8 +400,12 @@ def _mark() -> None:
386400
hass.services.async_register(DOMAIN, "backfill_empty_dates", _svc_backfill_empty_dates)
387401
hass.services.async_register(DOMAIN, "mark_empty_dates", _svc_mark_empty_dates)
388402
hass.services.async_register(DOMAIN, "mark_coverage_range", _svc_mark_coverage_range)
389-
hass.services.async_register(DOMAIN, "enable_purge_on_startup", _svc_enable_purge_on_startup)
390-
hass.services.async_register(DOMAIN, "disable_purge_on_startup", _svc_disable_purge_on_startup)
403+
hass.services.async_register(
404+
DOMAIN, "enable_purge_on_startup", _svc_enable_purge_on_startup
405+
)
406+
hass.services.async_register(
407+
DOMAIN, "disable_purge_on_startup", _svc_disable_purge_on_startup
408+
)
391409

392410
# Auto backfill: first-run (background) and nightly delta
393411
# Check if we need to purge and backfill on startup (from entry options or default False)
@@ -396,27 +414,35 @@ def _mark() -> None:
396414
async def _first_run_backfill() -> None:
397415
try:
398416
if should_purge_on_startup:
399-
_LOGGER.warning("PURGE_AND_BACKFILL_ON_STARTUP is enabled - purging all cache...")
417+
_LOGGER.warning(
418+
"PURGE_AND_BACKFILL_ON_STARTUP is enabled - purging all cache..."
419+
)
400420
result = await hass.async_add_executor_job(cache_io.purge_device, device_id)
401421
_LOGGER.info(f"Purge result: {result}")
402422
_LOGGER.warning("Starting smart backfill for 5 years...")
403423
# Use smart backfill for faster performance
404424
stats = await aggregator.smart_backfill(max_years=5, optimize_cache=True)
405425
_LOGGER.info(f"Smart backfill completed: {stats}")
406426
else:
407-
_LOGGER.info("Auto backfill: starting smart backfill for last 5 years (background)")
427+
_LOGGER.info(
428+
"Auto backfill: starting smart backfill for last 5 years (background)"
429+
)
408430
# Use smart backfill - much faster than daily backfill
409431
stats = await aggregator.smart_backfill(max_years=5, optimize_cache=True)
410432
_LOGGER.info(f"Auto backfill: completed 5-year history - {stats}")
411433

412434
# Auto-disable purge_and_backfill_on_startup after successful backfill
413435
if should_purge_on_startup:
414-
_LOGGER.info("Backfill completed successfully. Auto-disabling PURGE_AND_BACKFILL_ON_STARTUP...")
436+
_LOGGER.info(
437+
"Backfill completed successfully. Auto-disabling PURGE_AND_BACKFILL_ON_STARTUP..."
438+
)
415439
# Update entry options to disable the flag
416440
new_options = entry.options.copy()
417441
new_options["purge_and_backfill_on_startup"] = False
418442
hass.config_entries.async_update_entry(entry, options=new_options)
419-
_LOGGER.info("PURGE_AND_BACKFILL_ON_STARTUP has been automatically set to False")
443+
_LOGGER.info(
444+
"PURGE_AND_BACKFILL_ON_STARTUP has been automatically set to False"
445+
)
420446
except Exception as err:
421447
_LOGGER.error(f"Auto backfill initial failed: {err}")
422448

@@ -439,7 +465,9 @@ async def _nightly_delta(now=None):
439465
hass.async_create_task(_first_run_backfill())
440466

441467
# Schedule nightly job every 24h — store before risky step so we can clean up on failure
442-
remove_nightly = async_track_time_interval(hass, _nightly_delta, datetime.timedelta(hours=24))
468+
remove_nightly = async_track_time_interval(
469+
hass, _nightly_delta, datetime.timedelta(hours=24)
470+
)
443471
hass.data[DOMAIN][entry.entry_id]["remove_nightly"] = remove_nightly
444472

445473
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -475,6 +503,7 @@ async def _nightly_delta(now=None):
475503
hass.data[DOMAIN].pop(entry.entry_id, None)
476504
return False
477505

506+
478507
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
479508
"""Unload a config entry."""
480509
device_sn = entry.data.get(CONF_DEVICE_SN, "unknown")
@@ -508,7 +537,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
508537
_LOGGER.warning(f"Error cancelling nightly timer {device_sn}: {timer_err}")
509538

510539
# Cleanup coordinators (they should be cleaned up by platform unload, but ensure cleanup)
511-
for coord_key in ["daily_coordinator", "monthly_coordinator", "yearly_coordinator", "total_coordinator"]:
540+
for coord_key in [
541+
"daily_coordinator",
542+
"monthly_coordinator",
543+
"yearly_coordinator",
544+
"total_coordinator",
545+
]:
512546
coord = entry_data.get(coord_key)
513547
if coord and hasattr(coord, "async_shutdown"):
514548
try:

‎binary_sensor.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""
22
Lumentree Binary Sensor Platform - Redirect to entities/binary_sensor.py
33
"""
4+
45
from .entities.binary_sensor import * # noqa: F401, F403

‎config_flow.py‎

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ async def _get_api_client(self) -> LumentreeHttpApiClient:
7070

7171
except Exception as session_exc:
7272
_LOGGER.error(f"Failed to initialize API client: {session_exc}")
73-
raise ApiException(f"API Client Initialization failed: {session_exc}") from session_exc
73+
raise ApiException(
74+
f"API Client Initialization failed: {session_exc}"
75+
) from session_exc
7476
else:
7577
_LOGGER.debug(f"Reusing existing API client instance: {type(self._api_client)}")
7678

@@ -112,7 +114,9 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Con
112114
self._http_token = token
113115

114116
masked_token = (token[:6] + "...") if token and len(token) > 6 else "***"
115-
_LOGGER.info("Auth success for %s (token masked: %s)", self._device_id_input, masked_token)
117+
_LOGGER.info(
118+
"Auth success for %s (token masked: %s)", self._device_id_input, masked_token
119+
)
116120

117121
return await self.async_step_confirm_device()
118122

@@ -126,10 +130,14 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Con
126130
_LOGGER.exception(f"Unexpected auth error {self._device_id_input}: {exc}")
127131
errors["base"] = "unknown"
128132

129-
schema = vol.Schema({vol.Required(CONF_DEVICE_ID, default=self._device_id_input or ""): str})
133+
schema = vol.Schema(
134+
{vol.Required(CONF_DEVICE_ID, default=self._device_id_input or ""): str}
135+
)
130136
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
131137

132-
async def async_step_confirm_device(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
138+
async def async_step_confirm_device(
139+
self, user_input: dict[str, Any] | None = None
140+
) -> ConfigFlowResult:
133141
"""Handle device confirmation step."""
134142
errors: dict[str, str] = {}
135143
api: LumentreeHttpApiClient | None = None
@@ -162,7 +170,9 @@ async def async_step_confirm_device(self, user_input: dict[str, Any] | None = No
162170
if isinstance(device_info_api, dict) and "_error" in device_info_api:
163171
api_error = device_info_api["_error"]
164172
_LOGGER.error("API error when getting device info: %s", api_error)
165-
errors["base"] = "invalid_auth" if "Auth" in api_error else "cannot_connect_deviceinfo"
173+
errors["base"] = (
174+
"invalid_auth" if "Auth" in api_error else "cannot_connect_deviceinfo"
175+
)
166176
return self.async_show_form(
167177
step_id="confirm_device",
168178
description_placeholders={"device_name": "Err", "device_sn": "Err"},
@@ -174,7 +184,9 @@ async def async_step_confirm_device(self, user_input: dict[str, Any] | None = No
174184
)
175185

176186
if not self._device_sn_from_api:
177-
_LOGGER.warning("deviceId not found for %s. Using input ID.", self._device_id_input)
187+
_LOGGER.warning(
188+
"deviceId not found for %s. Using input ID.", self._device_id_input
189+
)
178190
self._device_sn_from_api = self._device_id_input
179191
elif self._device_sn_from_api != self._device_id_input:
180192
_LOGGER.warning(
@@ -184,16 +196,28 @@ async def async_step_confirm_device(self, user_input: dict[str, Any] | None = No
184196
)
185197

186198
self._device_name = (
187-
(device_info_api.get("remarkName") if isinstance(device_info_api, dict) else None)
188-
or (device_info_api.get("deviceType") if isinstance(device_info_api, dict) else None)
199+
(
200+
device_info_api.get("remarkName")
201+
if isinstance(device_info_api, dict)
202+
else None
203+
)
204+
or (
205+
device_info_api.get("deviceType")
206+
if isinstance(device_info_api, dict)
207+
else None
208+
)
189209
or f"Lumentree {self._device_sn_from_api}"
190210
)
191211

192212
_LOGGER.info(
193213
"Device Info: ID/SN='%s', Name='%s', Type='%s'",
194214
self._device_sn_from_api,
195215
self._device_name,
196-
(device_info_api.get("deviceType") if isinstance(device_info_api, dict) else None),
216+
(
217+
device_info_api.get("deviceType")
218+
if isinstance(device_info_api, dict)
219+
else None
220+
),
197221
)
198222

199223
await self.async_set_unique_id(self._device_sn_from_api)
@@ -241,7 +265,9 @@ async def async_step_confirm_device(self, user_input: dict[str, Any] | None = No
241265
}
242266

243267
if self._reauth_entry:
244-
_LOGGER.info(f"Updating entry {self._reauth_entry.entry_id} for {self._device_sn_from_api} reauth")
268+
_LOGGER.info(
269+
f"Updating entry {self._reauth_entry.entry_id} for {self._device_sn_from_api} reauth"
270+
)
245271
self.hass.config_entries.async_update_entry(self._reauth_entry, data=config_data)
246272
await self.hass.config_entries.async_reload(self._reauth_entry.entry_id)
247273
return self.async_abort(reason="reauth_successful")
@@ -266,7 +292,9 @@ async def async_step_reauth(self, user_input: dict[str, Any] | None = None) -> C
266292
self._api_client = None
267293
return await self.async_step_user(user_input={CONF_DEVICE_ID: self._device_id_input})
268294

269-
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
295+
async def async_step_reconfigure(
296+
self, user_input: dict[str, Any] | None = None
297+
) -> ConfigFlowResult:
270298
"""Handle reconfigure flow (HA 2024.3+)."""
271299
_LOGGER.info("Reconfigure flow started")
272300
reconfigure_entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])

0 commit comments

Comments
 (0)