251 files
This commit is contained in:
@@ -68,8 +68,11 @@ def _compute_common(coordinator: TaskMateCoordinator) -> dict:
|
||||
"""
|
||||
data = coordinator.data
|
||||
data_id = id(data)
|
||||
# External entities TaskMate reads (a child's photo among them) are not part
|
||||
# of ``data``, so their version is part of the key.
|
||||
external = getattr(coordinator, "external_state_version", 0)
|
||||
cached = getattr(coordinator, _COMMON_CACHE_ATTR, None)
|
||||
if cached and cached.get("data_id") == data_id:
|
||||
if cached and cached.get("data_id") == data_id and cached.get("external") == external:
|
||||
return cached["common"]
|
||||
|
||||
children = data.get("children", [])
|
||||
@@ -142,10 +145,15 @@ def _compute_common(coordinator: TaskMateCoordinator) -> dict:
|
||||
"pool_by_child_reward": pool_by_child_reward,
|
||||
"pool_total_by_reward": pool_total_by_reward,
|
||||
"total_allocated_by_child": total_allocated_by_child,
|
||||
# Each child's photo (from their chosen picture entity), resolved once
|
||||
# per update: every call site that draws a child's face reads this.
|
||||
"child_avatar_images": {c.id: coordinator.child_avatar_image(c) for c in children},
|
||||
# This week's points earned vs. the most on offer, per child.
|
||||
"child_week_progress": coordinator.week_progress(children, chores, all_completions),
|
||||
# Sent back to redo by an inspection (#981): not "done" on the card.
|
||||
"inspection_redo_ids": _inspection_redo_ids(coordinator),
|
||||
}
|
||||
setattr(coordinator, _COMMON_CACHE_ATTR, {"data_id": data_id, "common": common})
|
||||
setattr(coordinator, _COMMON_CACHE_ATTR, {"data_id": data_id, "external": external, "common": common})
|
||||
return common
|
||||
|
||||
|
||||
@@ -193,6 +201,8 @@ def _build_children_summary(coordinator: TaskMateCoordinator, common: dict) -> l
|
||||
if getattr(c, "is_guest", False)
|
||||
else {}
|
||||
),
|
||||
# Grown-ups (#1061): cards leave these out unless show_adults is set.
|
||||
**({"is_adult": True} if getattr(c, "is_adult", False) else {}),
|
||||
# Chore roulette (#677): today's pick + spins left, so the card can
|
||||
# show the result and disable the button once the allowance is used.
|
||||
**(
|
||||
@@ -217,6 +227,10 @@ def _build_children_summary(coordinator: TaskMateCoordinator, common: dict) -> l
|
||||
"total_points_earned": getattr(c, "total_points_earned", 0) or 0,
|
||||
"total_chores_completed": getattr(c, "total_chores_completed", 0) or 0,
|
||||
"avatar": getattr(c, "avatar", "mdi:account-circle") or "mdi:account-circle",
|
||||
# The child's photo, or "" — cards fall back to `avatar`.
|
||||
"avatar_image": common.get("child_avatar_images", {}).get(c.id, ""),
|
||||
"week_points_earned": common.get("child_week_progress", {}).get(c.id, {}).get("earned", 0),
|
||||
"week_points_available": common.get("child_week_progress", {}).get(c.id, {}).get("available", 0),
|
||||
"last_completion_date": getattr(c, "last_completion_date", None),
|
||||
"streak_paused": getattr(c, "streak_paused", False),
|
||||
"on_vacation": coordinator._is_child_on_vacation(c),
|
||||
@@ -698,6 +712,9 @@ def _build_pending_reward_claims(common: dict) -> list[dict]:
|
||||
"reward_id": rc.reward_id,
|
||||
"child_id": rc.child_id,
|
||||
"child_name": child.name,
|
||||
# No avatar_image here on purpose: the cards resolve a claim's
|
||||
# face from the overview sensor's children[], and a copy per
|
||||
# claim pushed this sensor past the 16KB recorder limit.
|
||||
"child_avatar": getattr(child, "avatar", "mdi:account-circle") or "mdi:account-circle",
|
||||
"reward_name": reward.name,
|
||||
"reward_icon": reward.icon or "mdi:gift",
|
||||
@@ -1575,6 +1592,7 @@ class ChildPointsSensor(TaskMateBaseSensor):
|
||||
"child_id": child.id,
|
||||
"child_name": child.name,
|
||||
"avatar": child.avatar,
|
||||
"avatar_image": self.coordinator.child_avatar_image(child),
|
||||
"total_points_earned": child.total_points_earned,
|
||||
"total_chores_completed": child.total_chores_completed,
|
||||
"current_streak": child.current_streak,
|
||||
@@ -1615,6 +1633,18 @@ class ChildStatsSensor(TaskMateBaseSensor):
|
||||
child = self.coordinator.get_child(self.child_id)
|
||||
return child.avatar if child else "mdi:account-circle"
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
"""The child's photo, so HA's own UI shows their face too.
|
||||
|
||||
HA prefers a picture over ``icon`` when both are set, so returning
|
||||
None (the default) leaves the MDI avatar above in charge.
|
||||
"""
|
||||
child = self.coordinator.get_child(self.child_id)
|
||||
if not child:
|
||||
return None
|
||||
return self.coordinator.child_avatar_image(child) or None
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict:
|
||||
"""Return additional attributes."""
|
||||
@@ -1650,6 +1680,7 @@ class ChildStatsSensor(TaskMateBaseSensor):
|
||||
"child_id": child.id,
|
||||
"child_name": child.name,
|
||||
"avatar": child.avatar,
|
||||
"avatar_image": self.coordinator.child_avatar_image(child),
|
||||
"points": child.points,
|
||||
"total_points_earned": child.total_points_earned,
|
||||
"total_chores_completed": child.total_chores_completed,
|
||||
@@ -1869,6 +1900,10 @@ class ChildBadgesSensor(TaskMateBaseSensor):
|
||||
earned.sort(key=lambda e: e.get("earned_at") or "", reverse=True)
|
||||
|
||||
return {
|
||||
# Who the badges belong to, so the card can put a face to them.
|
||||
"child_name": child.name,
|
||||
"child_avatar": getattr(child, "avatar", "mdi:account-circle") or "mdi:account-circle",
|
||||
"avatar_image": self.coordinator.child_avatar_image(child),
|
||||
"earned": earned,
|
||||
"available": available,
|
||||
"total_badges": len(applicable),
|
||||
|
||||
Reference in New Issue
Block a user