This commit is contained in:
Home Assistant Version Control
2026-08-02 15:33:12 +00:00
parent 60dd13b52c
commit 2ce8792155
39 changed files with 1992 additions and 157 deletions
@@ -158,6 +158,7 @@ class NotificationCoordinator:
recipients_fired: list[str] = []
message = self._render_template(meta, context)
nav_url = self._resolve_nav_url(cfg)
# Multi-parent routing (#687): thin the PARENT recipients down per the
# configured policy. Child routes are never touched — a reminder for a
@@ -179,7 +180,7 @@ class NotificationCoordinator:
notify_service = self._resolve_notify_service(recipient_id)
if not notify_service:
continue
await self._send_to(notify_service, message, meta, context)
await self._send_to(notify_service, message, meta, context, nav_url)
recipients_fired.append(recipient_id)
# NOTE: deliberately no unconditional persistent_notification here.
@@ -283,6 +284,7 @@ class NotificationCoordinator:
}
message = "[TEST] " + self._render_template(meta, ctx)
cfg = self.storage.get_notification_config(type_id)
nav_url = self._resolve_nav_url(cfg)
sent: list[str] = []
for recipient_id, route in cfg.routes.items():
if not route.enabled:
@@ -290,7 +292,7 @@ class NotificationCoordinator:
notify_service = self._resolve_notify_service(recipient_id)
if not notify_service:
continue
await self._send_to(notify_service, message, meta, ctx)
await self._send_to(notify_service, message, meta, ctx, nav_url)
sent.append(recipient_id)
await self._fire_persistent_notification(type_id, message)
return sent
@@ -308,6 +310,13 @@ class NotificationCoordinator:
child.quiet_hours_start, child.quiet_hours_end, dt_util.now()
)
def _resolve_nav_url(self, cfg) -> str:
"""Tap target for this notification: per-type override, else global default."""
per_type = (getattr(cfg, "nav_url", "") or "").strip()
if per_type:
return per_type
return str(self.storage.get_setting("notification_nav_url", "/taskmate") or "").strip()
def _resolve_notify_service(self, recipient_id: str) -> str:
if recipient_id.startswith("child:"):
child_id = recipient_id.split(":", 1)[1]
@@ -349,7 +358,7 @@ class NotificationCoordinator:
async def _send_to(
self, notify_service: str, message: str,
meta: "NotificationTypeMeta", context: dict[str, Any],
meta: "NotificationTypeMeta", context: dict[str, Any], nav_url: str = "",
) -> None:
domain, service = (
notify_service.split(".", 1) if "." in notify_service
@@ -395,6 +404,13 @@ class NotificationCoordinator:
push["image"] = photo_url
push["attachment"] = {"url": photo_url, "content-type": "jpeg"}
# Tap target (#734): open a chosen place when the notification body is
# tapped. clickAction=Android, url=iOS — the same value works on both.
# Only the mobile app honours these; other backends ignore data, so skip.
if nav_url and service.startswith("mobile_app"):
push["clickAction"] = nav_url
push["url"] = nav_url
if push:
data["data"] = push
@@ -693,6 +709,15 @@ class NotificationCoordinator:
await self.storage.async_save()
await self.async_setup_schedules()
async def set_nav_url(self, type_id: str | None, nav_url: str) -> None:
"""Set the tap target — global (type_id falsy) or per notification type."""
nav_url = (nav_url or "").strip()
if type_id:
self.storage.set_notification_nav_url(type_id, nav_url)
else:
self.storage.set_setting("notification_nav_url", nav_url)
await self.storage.async_save()
def _has_outstanding_chores_today(self, child_id: str) -> bool:
"""Returns True if the child has at least one chore assigned today
that has no approved/pending completion yet."""