348 files

This commit is contained in:
Home Assistant Version Control
2026-07-23 19:02:12 +00:00
parent 0ed1687503
commit 016af2e013
348 changed files with 12368 additions and 11689 deletions
+35
View File
@@ -21,13 +21,19 @@ from .coord_badges import BadgeCoordinator
from .coord_calendar import CalendarMixin
from .coord_challenges import ChallengesMixin
from .coord_chores import ChoresMixin
from .coord_guests import GuestsMixin
from .coord_mandatory import MandatoryMixin
from .coord_notifications import NotificationCoordinator
from .coord_points import PointsMixin
from .coord_quests import QuestsMixin
from .coord_reports import ReportsMixin
from .coord_rewards import RewardsMixin
from .coord_roulette import RouletteMixin
from .coord_scheduled import ScheduledChangesMixin
from .coord_templates import TemplatesMixin
from .coord_timed import TimedMixin
from .coord_tts import ReadAloudMixin
from .coord_unlocks import UnlocksMixin
from .models import Child
from .storage import TaskMateStorage
@@ -46,6 +52,12 @@ class TaskMateCoordinator(
TimedMixin,
CalendarMixin,
TemplatesMixin,
ScheduledChangesMixin,
ReportsMixin,
RouletteMixin,
ReadAloudMixin,
GuestsMixin,
UnlocksMixin,
DataUpdateCoordinator,
):
"""Coordinator to manage TaskMate data."""
@@ -66,6 +78,12 @@ class TaskMateCoordinator(
self._unsub_prune: Callable[[], None] | None = None
self._unsub_availability: Callable[[], None] | None = None
self._tracked_availability_entities: set[str] = set()
self._tracked_visibility_entities: set[str] = set()
# Bumped whenever a tracked external entity (child availability, chore
# visibility, chore weather) changes state. The data snapshot is reused
# while storage.data_version is unchanged, so sensors that derive
# attributes from live entity state need this to invalidate their cache.
self.external_state_version: int = 0
self._unsub_surprise: Callable[[], None] | None = None
self._unsub_weekly: Callable[[], None] | None = None
self._unsub_mandatory: list[Callable[[], None]] = []
@@ -74,6 +92,8 @@ class TaskMateCoordinator(
self._avail_cache: dict | None = None
# (data_version, snapshot) cache for _async_update_data (PERF-2).
self._data_snapshot_cache: tuple[int, dict[str, Any]] | None = None
# Scheduled reverts for timed unlock rewards (#678).
self._unlock_timers: list = []
def difficulty_multiplier(self, tier: str) -> float:
"""Return the points multiplier for a difficulty tier.
@@ -265,6 +285,11 @@ class TaskMateCoordinator(
await self.storage.async_save()
await self._async_backfill_career_history()
await self._async_stop_stale_timed_sessions()
# Catch up on scheduled changes (#675) that came due while HA was off —
# the parent still expects "from 1 September" to have happened.
await self.async_apply_due_scheduled_changes(refresh=False)
# A restart mid-unlock must never strand the TV on (#678).
await self.async_resume_unlocks()
await self.async_refresh()
# Schedule midnight streak check at 00:00:05
self._unsub_midnight = async_track_time_change(
@@ -607,6 +632,7 @@ class TaskMateCoordinator(
async def async_shutdown(self) -> None:
"""Shutdown the coordinator and clean up listeners."""
self.cancel_unlock_timers()
if self._unsub_midnight:
self._unsub_midnight()
self._unsub_midnight = None
@@ -640,9 +666,13 @@ class TaskMateCoordinator(
saves. One step failing must not stop the rest.
"""
steps = [
self.async_apply_due_scheduled_changes,
self.async_prune_roulette_state,
self.async_archive_expired_guests,
self._async_check_streaks,
self._async_expire_one_shot_chores,
self._async_expire_dated_chores,
self._async_expire_deadline_chores,
self._async_restock_rewards,
self._async_expire_rewards,
self._async_decay_points,
@@ -698,6 +728,11 @@ class TaskMateCoordinator(
# May stop sessions (mutates + saves -> bumps the version), so run first.
await self._async_auto_stop_capped_sessions()
await self._async_check_family_goal()
# Reactive-chore deadlines are minutes long, so they can't wait for
# midnight maintenance like the date-based expiries do. refresh=False:
# we are already inside the refresh, and the snapshot below picks the
# change up in this same tick.
await self._async_expire_deadline_chores(refresh=False)
self._refresh_tracked_availability_entities()
version = self.storage.data_version
cached = getattr(self, "_data_snapshot_cache", None)