Skip to content

Commit 6ae6324

Browse files
committed
Refactor backend and remove redundant code
1 parent 69f2879 commit 6ae6324

File tree

12 files changed

+102
-116
lines changed

12 files changed

+102
-116
lines changed

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ tasks.
2727
- Include adequate debug/info logs and honor configurable log levels (`info`/`debug`).
2828
- Consult https://developers.home-assistant.io/ for information on developing Home Assistant integrations.
2929
- Provide type hints for all function parameters and returns.
30+
- Avoid packages/modules named common, utils, const etc. Instead, prefer placing constants an interfaces near the consumer where possible.
31+
- Prioritize readable, maintainable code.

custom_components/maint/__init__.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,32 @@
33
from __future__ import annotations
44

55
import logging
6-
from typing import TYPE_CHECKING
6+
from typing import TYPE_CHECKING, Any
77

88
from homeassistant.const import Platform
99
from homeassistant.helpers import config_validation as cv
1010

11-
from .const import (
12-
DEFAULT_TITLE,
13-
DOMAIN,
14-
)
11+
from .config_flow import DEFAULT_TITLE
12+
from .domain import DOMAIN
1513
from .models import MaintConfigEntry, MaintRuntimeData, MaintTaskStore
1614
from .panel import async_register_panel, async_unregister_panel
1715
from .websocket import async_register_websocket_handlers
1816

17+
# We only support setup from the UI so we need to load a config entry only schema.
1918
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
19+
20+
# Define the platforms we use.
2021
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
22+
23+
# Define a key for storing and retrieving the task store in hass.data
24+
DATA_KEY_TASK_STORE = "task_store"
25+
26+
# Define a key for indicating whether or not websockets have been registered.
27+
DATA_KEY_WS_REGISTERED = "ws_registered"
28+
2129
_LOGGER = logging.getLogger(__name__)
2230

31+
2332
if TYPE_CHECKING:
2433
from homeassistant.config_entries import ConfigEntry
2534
from homeassistant.core import HomeAssistant
@@ -28,13 +37,13 @@
2837

2938
async def async_setup(hass: HomeAssistant, _config: ConfigType) -> bool:
3039
"""Set up Maint."""
31-
data = hass.data.setdefault(DOMAIN, {})
40+
data: dict[str, Any] = hass.data.setdefault(DOMAIN, {})
3241
_LOGGER.info("Setting up Maint integration")
3342
await _async_get_task_store(hass)
3443
await async_register_panel(hass)
35-
if not data.get("ws_registered"):
44+
if not data.get(DATA_KEY_WS_REGISTERED):
3645
async_register_websocket_handlers(hass)
37-
data["ws_registered"] = True
46+
data[DATA_KEY_WS_REGISTERED] = True
3847
_LOGGER.debug("Registered Maint websocket handlers")
3948
return True
4049

@@ -83,12 +92,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
8392

8493
async def _async_get_task_store(hass: HomeAssistant) -> MaintTaskStore:
8594
"""Return the shared Maint task store."""
86-
data = hass.data.setdefault(DOMAIN, {})
87-
if (store := data.get("task_store")) is None:
95+
data: dict[str, Any] = hass.data.setdefault(DOMAIN, {})
96+
if (store := data.get(DATA_KEY_TASK_STORE)) is None:
8897
_LOGGER.debug("Creating Maint task store")
8998
store = MaintTaskStore(hass)
9099
await store.async_load()
91-
data["task_store"] = store
100+
data[DATA_KEY_TASK_STORE] = store
92101
else:
93102
_LOGGER.debug("Reusing existing Maint task store")
94103
return store

custom_components/maint/binary_sensor.py

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,16 @@
1010
from homeassistant.helpers.dispatcher import async_dispatcher_connect
1111
from homeassistant.util import slugify
1212

13-
from .const import (
14-
DOMAIN,
15-
EVENT_TASK_DUE,
16-
SIGNAL_TASK_CREATED,
17-
SIGNAL_TASK_DELETED,
18-
SIGNAL_TASK_UPDATED,
19-
)
13+
from .domain import DOMAIN
14+
from .models import SIGNAL_TASK_CREATED, SIGNAL_TASK_DELETED, SIGNAL_TASK_UPDATED
2015

2116
if TYPE_CHECKING:
2217
from homeassistant.helpers.entity_platform import AddEntitiesCallback
2318

2419
from .models import MaintConfigEntry, MaintTask
2520

2621
_LOGGER = logging.getLogger(__name__)
22+
EVENT_TASK_DUE = "maint_task_due"
2723

2824

2925
async def async_setup_entry(
@@ -37,38 +33,38 @@ async def async_setup_entry(
3733
tasks = await store.async_list_tasks(entry.entry_id)
3834

3935
entities: dict[str, MaintTaskBinarySensor] = {}
40-
async_add_entities(
41-
[
42-
entities.setdefault(
43-
task.task_id, MaintTaskBinarySensor(entry=entry, task=task)
44-
)
45-
for task in tasks
46-
]
47-
)
36+
initial_entities = []
37+
for task in tasks:
38+
entity = MaintTaskBinarySensor(entry=entry, task=task)
39+
entities[task.task_id] = entity
40+
initial_entities.append(entity)
41+
async_add_entities(initial_entities)
42+
43+
def _upsert_entity(task: MaintTask) -> MaintTaskBinarySensor | None:
44+
"""Create or update an entity for a task and return it if newly added."""
45+
if existing := entities.get(task.task_id):
46+
existing.handle_task_update(task)
47+
return None
48+
49+
entity = MaintTaskBinarySensor(entry=entry, task=task)
50+
entities[task.task_id] = entity
51+
return entity
4852

4953
@callback
5054
def handle_task_created(entry_id: str, task: MaintTask) -> None:
5155
"""Create a sensor when a new task is added."""
5256
if entry_id != entry.entry_id:
5357
return
54-
if task.task_id in entities:
55-
entities[task.task_id].handle_task_update(task)
56-
return
57-
entity = MaintTaskBinarySensor(entry=entry, task=task)
58-
entities[task.task_id] = entity
59-
async_add_entities([entity])
58+
if entity := _upsert_entity(task):
59+
async_add_entities([entity])
6060

6161
@callback
6262
def handle_task_updated(entry_id: str, task: MaintTask) -> None:
6363
"""Update an existing sensor when a task is modified."""
6464
if entry_id != entry.entry_id:
6565
return
66-
if entity := entities.get(task.task_id):
67-
entity.handle_task_update(task)
68-
return
69-
entity = MaintTaskBinarySensor(entry=entry, task=task)
70-
entities[task.task_id] = entity
71-
async_add_entities([entity])
66+
if entity := _upsert_entity(task):
67+
async_add_entities([entity])
7268

7369
@callback
7470
def handle_task_deleted(entry_id: str, task: MaintTask) -> None:

custom_components/maint/config_flow.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66

77
from homeassistant import config_entries
88

9-
from .const import (
10-
DEFAULT_TITLE,
11-
DOMAIN,
12-
)
9+
from .domain import DOMAIN
1310

1411
if TYPE_CHECKING:
1512
from collections.abc import Mapping
1613

14+
DEFAULT_TITLE = "Maint"
15+
1716

1817
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
1918
"""Handle a config flow for Maint."""

custom_components/maint/const.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

custom_components/maint/domain.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Domain for the Maint integration."""
2+
3+
DOMAIN = "maint"

custom_components/maint/models.py

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@
1313
from homeassistant.helpers.storage import Store
1414
from homeassistant.util import dt as dt_util
1515

16-
from .const import (
17-
SIGNAL_TASK_CREATED,
18-
SIGNAL_TASK_DELETED,
19-
SIGNAL_TASK_UPDATED,
20-
)
16+
from .domain import DOMAIN
2117

2218
if TYPE_CHECKING:
23-
from homeassistant.config_entries import ConfigEntry
2419
from homeassistant.core import HomeAssistant
2520

26-
STORAGE_KEY = "maint.tasks"
21+
SIGNAL_TASK_CREATED = "maint_task_created"
22+
SIGNAL_TASK_UPDATED = "maint_task_updated"
23+
SIGNAL_TASK_DELETED = "maint_task_deleted"
24+
25+
STORAGE_KEY = f"{DOMAIN}.store"
2726
STORAGE_VERSION = 1
27+
2828
FREQUENCY_UNIT_DAYS: Literal["days"] = "days"
2929
FREQUENCY_UNIT_WEEKS: Literal["weeks"] = "weeks"
3030
FREQUENCY_UNIT_MONTHS: Literal["months"] = "months"
@@ -35,12 +35,6 @@
3535
)
3636
FrequencyUnit = Literal["days", "weeks", "months"]
3737

38-
39-
class _UnsetType:
40-
"""Sentinel type for unset optional values."""
41-
42-
43-
UNSET = _UnsetType()
4438
_LOGGER = logging.getLogger(__name__)
4539

4640

@@ -140,29 +134,25 @@ async def async_load(self) -> None:
140134
return
141135
_LOGGER.debug("Loading Maint tasks from storage")
142136
data = await self._store.async_load()
143-
if not data:
144-
self._tasks = {}
145-
else:
146-
entries = data.get("entries", {})
147-
tasks_by_entry: dict[str, dict[str, MaintTask]] = {}
148-
for entry_id, tasks in entries.items():
149-
entry_tasks: dict[str, MaintTask] = {}
150-
for task_data in tasks:
151-
entry_tasks[task_data["task_id"]] = MaintTask.from_dict(task_data)
152-
tasks_by_entry[entry_id] = entry_tasks
153-
self._tasks = tasks_by_entry
154-
total_tasks = sum(len(tasks) for tasks in self._tasks.values())
137+
entries = data.get("entries", {}) if data else {}
138+
self._tasks = {
139+
entry_id: {
140+
task_data["task_id"]: MaintTask.from_dict(task_data)
141+
for task_data in tasks
142+
}
143+
for entry_id, tasks in entries.items()
144+
}
145+
entries_count, tasks_count = self._counts()
155146
_LOGGER.debug(
156147
"Loaded Maint task store: %s entries, %s tasks",
157-
len(self._tasks),
158-
total_tasks,
148+
entries_count,
149+
tasks_count,
159150
)
160151
self._loaded = True
161152

162153
async def _async_save(self) -> None:
163154
"""Persist tasks to disk."""
164-
entries_count = len(self._tasks)
165-
tasks_count = sum(len(tasks) for tasks in self._tasks.values())
155+
entries_count, tasks_count = self._counts()
166156
await self._store.async_save(
167157
{
168158
"entries": {
@@ -177,6 +167,10 @@ async def _async_save(self) -> None:
177167
tasks_count,
178168
)
179169

170+
def _counts(self) -> tuple[int, int]:
171+
"""Return counts of entries and tasks for logging."""
172+
return len(self._tasks), sum(len(tasks) for tasks in self._tasks.values())
173+
180174
async def _async_get_entry_tasks(self, entry_id: str) -> dict[str, MaintTask]:
181175
"""Return the task mapping for an entry."""
182176
await self.async_load()
@@ -314,8 +308,8 @@ def validate( # noqa: PLR0913
314308
message = "description cannot be empty"
315309
raise ValueError(message)
316310

317-
if last_completed is not None and last_completed == "":
318-
message = "last_completed cannot be empty"
311+
if last_completed is not None and not isinstance(last_completed, date):
312+
message = "last_completed must be a date"
319313
raise ValueError(message)
320314

321315
if frequency is not None and frequency <= 0:

custom_components/maint/panel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from homeassistant.components import frontend, panel_custom
1010
from homeassistant.components.http import StaticPathConfig
1111

12-
from .const import DOMAIN
12+
from .domain import DOMAIN
1313

1414
if TYPE_CHECKING:
1515
from homeassistant.core import HomeAssistant

custom_components/maint/sensor.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@
1010
from homeassistant.helpers import entity_registry as er
1111
from homeassistant.helpers.dispatcher import async_dispatcher_connect
1212

13-
from .const import (
14-
DOMAIN,
15-
SIGNAL_TASK_CREATED,
16-
SIGNAL_TASK_DELETED,
17-
SIGNAL_TASK_UPDATED,
18-
)
13+
from .domain import DOMAIN
14+
from .models import SIGNAL_TASK_CREATED, SIGNAL_TASK_DELETED, SIGNAL_TASK_UPDATED
1915

2016
if TYPE_CHECKING:
2117
from homeassistant.helpers.entity_platform import AddEntitiesCallback

0 commit comments

Comments
 (0)