189 files
This commit is contained in:
1 parent
79fefc0a7c
commit
303886f212
189 files changed
+21402
-3514
No files matched your search
@@ -30,6 +30,7 @@ from ..const import (
|
||||
CONF_QUIET_HOURS_END,
|
||||
CONF_QUIET_HOURS_START,
|
||||
CONF_SNOOZE_DURATION_HOURS,
|
||||
CONF_TASKS,
|
||||
DEFAULT_MAX_NOTIFICATIONS_PER_DAY,
|
||||
DEFAULT_SNOOZE_DURATION_HOURS,
|
||||
DOMAIN,
|
||||
@@ -37,6 +38,16 @@ from ..const import (
|
||||
)
|
||||
from .global_options import get_global_options
|
||||
from .i18n import normalize_language
|
||||
from .notify_hooks import (
|
||||
KIND_BUDGET,
|
||||
KIND_BUNDLE,
|
||||
KIND_DIGEST,
|
||||
KIND_LEAD_TIME,
|
||||
KIND_STATUS,
|
||||
KIND_WARRANTY,
|
||||
async_emit_and_dispatch,
|
||||
notification_context,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -692,7 +703,7 @@ async def async_dispatch_notify(
|
||||
url = (service_data.get("data") or {}).get("url")
|
||||
msg = service_data.get("message", "")
|
||||
if url and "](" not in msg:
|
||||
link_text = _notif_t("open_task_link", getattr(hass.config, "language", None) or "en")
|
||||
link_text = _notif_t("open_task_link", normalize_language(hass))
|
||||
service_data = {**service_data, "message": f"{msg}\n\n[{link_text}]({url})"}
|
||||
if hass.services.has_service(domain, name):
|
||||
await hass.services.async_call(domain, name, service_data, blocking=blocking)
|
||||
@@ -767,6 +778,7 @@ class NotificationManager:
|
||||
message: str,
|
||||
entry_id: str,
|
||||
task_id: str,
|
||||
context: Mapping[str, Any],
|
||||
) -> bool:
|
||||
"""Resolve targets (per-user services, else the global service) and
|
||||
fan the notification out; True when at least one send succeeded.
|
||||
@@ -803,6 +815,7 @@ class NotificationManager:
|
||||
message=message,
|
||||
entry_id=entry_id,
|
||||
task_id=task_id,
|
||||
context=context,
|
||||
):
|
||||
success = True
|
||||
return success
|
||||
@@ -812,6 +825,16 @@ class NotificationManager:
|
||||
"""Get the HA UI language as a 2-letter table key."""
|
||||
return normalize_language(self.hass)
|
||||
|
||||
def _skip_allowed(self, entry_id: str, task_id: str) -> bool:
|
||||
"""#150: a skip-locked task gets no "Skip" action button — the tap
|
||||
could only fail in the action handler (bug review 2026-09-04).
|
||||
Unknown entries/tasks keep the button (nothing to check against)."""
|
||||
entry = self.hass.config_entries.async_get_entry(entry_id)
|
||||
if entry is None:
|
||||
return True
|
||||
task = (entry.data.get(CONF_TASKS) or {}).get(task_id) or {}
|
||||
return task.get("allow_skip") is not False
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
"""Check if notifications are globally enabled."""
|
||||
@@ -1034,12 +1057,25 @@ class NotificationManager:
|
||||
lang = self._lang
|
||||
title, message = self._build_message(new_status, lang, task_name, object_name, days_until_due, next_due)
|
||||
|
||||
context = notification_context(
|
||||
self.hass,
|
||||
KIND_STATUS,
|
||||
status=new_status,
|
||||
entry_id=entry_id,
|
||||
task_id=task_id,
|
||||
task_name=task_name,
|
||||
object_name=object_name,
|
||||
days_until_due=days_until_due,
|
||||
next_due=next_due,
|
||||
responsible_user_id=responsible_user_id,
|
||||
)
|
||||
if not await self._resolve_and_send(
|
||||
responsible_user_id,
|
||||
title=title,
|
||||
message=message,
|
||||
entry_id=entry_id,
|
||||
task_id=task_id,
|
||||
context=context,
|
||||
):
|
||||
return
|
||||
|
||||
@@ -1111,6 +1147,7 @@ class NotificationManager:
|
||||
message: str,
|
||||
entry_id: str,
|
||||
task_id: str,
|
||||
context: Mapping[str, Any] | None = None,
|
||||
) -> bool:
|
||||
"""Send notification via specific service, optionally with action buttons.
|
||||
|
||||
@@ -1136,7 +1173,7 @@ class NotificationManager:
|
||||
"title": f"\u2705 {_notif_t('action_complete', lang)}",
|
||||
}
|
||||
)
|
||||
if options.get(CONF_ACTION_SKIP_ENABLED, False):
|
||||
if options.get(CONF_ACTION_SKIP_ENABLED, False) and self._skip_allowed(entry_id, task_id):
|
||||
actions.append(
|
||||
{
|
||||
"action": f"MS_SKIP_{entry_id}_{task_id}",
|
||||
@@ -1160,8 +1197,10 @@ class NotificationManager:
|
||||
if actions:
|
||||
service_data["data"]["actions"] = actions[:3] # Android supports max 3
|
||||
|
||||
if context is None:
|
||||
context = notification_context(self.hass, KIND_STATUS, entry_id=entry_id, task_id=task_id)
|
||||
try:
|
||||
return await async_dispatch_notify(self.hass, service, service_data)
|
||||
return await async_emit_and_dispatch(self.hass, service, service_data, context)
|
||||
except (HomeAssistantError, ValueError, TypeError):
|
||||
_LOGGER.exception("Failed to send notification to %s", service)
|
||||
return False
|
||||
@@ -1216,8 +1255,18 @@ class NotificationManager:
|
||||
url=f"/maintenance-supporter?entry_id={entry_id}",
|
||||
)
|
||||
|
||||
context = notification_context(
|
||||
self.hass,
|
||||
KIND_BUNDLE,
|
||||
entry_id=entry_id,
|
||||
object_name=object_name,
|
||||
tasks=[
|
||||
{"task_id": t.get("task_id"), "task_name": t.get("task_name"), "status": t.get("status")}
|
||||
for t in tasks
|
||||
],
|
||||
)
|
||||
try:
|
||||
if await async_dispatch_notify(self.hass, self.notify_service, service_data):
|
||||
if await async_emit_and_dispatch(self.hass, self.notify_service, service_data, context):
|
||||
self._last_notified[bundle_key] = dt_util.now()
|
||||
self._daily_count += 1
|
||||
_LOGGER.debug("Bundled notification sent: %s - %s", title, message)
|
||||
@@ -1241,8 +1290,9 @@ class NotificationManager:
|
||||
_notif_t("digest_message", lang, overdue=str(overdue), due_soon=str(due_soon)),
|
||||
tag="maintenance_weekly_digest",
|
||||
)
|
||||
context = notification_context(self.hass, KIND_DIGEST, overdue=overdue, due_soon=due_soon)
|
||||
try:
|
||||
await async_dispatch_notify(self.hass, self.notify_service, service_data)
|
||||
await async_emit_and_dispatch(self.hass, self.notify_service, service_data, context)
|
||||
_LOGGER.debug("Weekly digest sent: %s overdue, %s due soon", overdue, due_soon)
|
||||
except (HomeAssistantError, ValueError, TypeError):
|
||||
_LOGGER.exception("Failed to send weekly digest")
|
||||
@@ -1267,8 +1317,9 @@ class NotificationManager:
|
||||
),
|
||||
tag="maintenance_warranty_reminder",
|
||||
)
|
||||
context = notification_context(self.hass, KIND_WARRANTY, names=list(names), days=days)
|
||||
try:
|
||||
await async_dispatch_notify(self.hass, self.notify_service, service_data)
|
||||
await async_emit_and_dispatch(self.hass, self.notify_service, service_data, context)
|
||||
_LOGGER.debug("Warranty reminder sent: %s object(s)", len(names))
|
||||
except (HomeAssistantError, ValueError, TypeError):
|
||||
_LOGGER.exception("Failed to send warranty reminder")
|
||||
@@ -1319,12 +1370,25 @@ class NotificationManager:
|
||||
due=next_due if next_due is not None else "?",
|
||||
)
|
||||
|
||||
context = notification_context(
|
||||
self.hass,
|
||||
KIND_LEAD_TIME,
|
||||
status="due_soon",
|
||||
entry_id=entry_id,
|
||||
task_id=task_id,
|
||||
task_name=task_name,
|
||||
object_name=object_name,
|
||||
days_until_due=days,
|
||||
next_due=next_due,
|
||||
responsible_user_id=responsible_user_id,
|
||||
)
|
||||
if await self._resolve_and_send(
|
||||
responsible_user_id,
|
||||
title=title,
|
||||
message=message,
|
||||
entry_id=entry_id,
|
||||
task_id=task_id,
|
||||
context=context,
|
||||
):
|
||||
self._daily_count += 1
|
||||
_LOGGER.debug("Lead reminder sent: %s due in %s day(s)", task_name, days)
|
||||
@@ -1365,8 +1429,9 @@ class NotificationManager:
|
||||
|
||||
service_data = _service_payload(title, message, tag=f"maintenance_budget_{period}")
|
||||
|
||||
context = notification_context(self.hass, KIND_BUDGET, period=period, spent=spent, budget=budget, percent=pct)
|
||||
try:
|
||||
if await async_dispatch_notify(self.hass, self.notify_service, service_data):
|
||||
if await async_emit_and_dispatch(self.hass, self.notify_service, service_data, context):
|
||||
self._last_notified[budget_key] = dt_util.now()
|
||||
self._daily_count += 1
|
||||
_LOGGER.debug("Budget alert sent: %s - %s", title, message)
|
||||
|
||||
Reference in new issue
Block a user