Coverage for custom_components/supernotify/notify.py: 97%
39 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-09-25 21:14 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-09-25 21:14 +0000
1from __future__ import annotations
3import logging
4from typing import TYPE_CHECKING, Any, cast
6from homeassistant.components.notify.legacy import BaseNotificationService
7from homeassistant.const import (
8 CONF_TARGET,
9)
10from homeassistant.core import (
11 HomeAssistant,
12 ServiceCall,
13)
15from .engine import SupernotifyEngine
16from .model import NotifyEntityPlatform
18if TYPE_CHECKING:
19 from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
20 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
22 from . import SupernotifyConfigEntry
23from .const import (
24 ATTR_DATA,
25 CONF_MESSAGE,
26 CONF_TITLE,
27)
29_LOGGER = logging.getLogger(__name__)
31PARALLEL_UPDATES = 0
34class SuperNotificationService(BaseNotificationService):
35 """Legacy notify-platform compatibility shim.
37 The only reason this exists is to satisfy HA core's notify/legacy.py
38 BaseNotificationService contract, so notify.supernotify and notify.<target> keep working.
39 It adds nothing but that glue on top of SupernotifyEngine - nothing else in this
40 integration (SupernotifyEntity, RecipientNotifyEntity, the supplemental actions, tests
41 targeting the engine) references this class or BaseNotificationService. If/when HA core
42 drops BaseNotificationService, delete this class and the matching async_setup/
43 async_register_services/async_unregister_services calls in __init__.py; everything else
44 keeps working unchanged.
45 """
47 def __init__(self, engine: SupernotifyEngine, *args: Any, **kwargs: Any) -> None:
48 super().__init__()
49 self.engine = engine
51 async def async_unregister_services(self) -> None:
52 _LOGGER.info("SUPERNOTIFY Unregistering notify service")
53 return await super().async_unregister_services()
55 async def _async_notify_message_service(self, service: ServiceCall) -> None:
56 """Override of BaseNotificationService._async_notify_message_service (notify/legacy.py)
57 to forward the calling action's Context through to async_send_message. HA core's
58 implementation builds its own kwargs from service.data and drops service.context
59 entirely, which is why every notify.supernotify/notify.<target> call otherwise loses
60 its link back to the triggering automation, showing up downstream (e.g. in the mobile
61 app's notification history) as having no recorded cause.
62 """
63 kwargs: dict[str, Any] = {}
64 message: str = service.data[CONF_MESSAGE]
65 if title := service.data.get(CONF_TITLE):
66 kwargs[CONF_TITLE] = title
67 if self.registered_targets.get(service.service) is not None:
68 kwargs[CONF_TARGET] = [self.registered_targets[service.service]]
69 elif service.data.get(CONF_TARGET) is not None:
70 kwargs[CONF_TARGET] = service.data.get(CONF_TARGET)
71 kwargs[CONF_MESSAGE] = message
72 kwargs[ATTR_DATA] = service.data.get(ATTR_DATA)
73 kwargs["context"] = service.context
75 await self.engine.async_send_message(**kwargs)
78async def async_get_service(
79 hass: HomeAssistant,
80 config: ConfigType,
81 discovery_info: DiscoveryInfoType | None = None,
82) -> None:
83 """Legacy `notify: - platform: supernotify` entrypoint - see async_setup_legacy in legacy
84 BaseNotificationService.
86 The config entry is now the sole, unconditional owner of notify.supernotify (see
87 async_setup_entry in __init__.py), so this leftover legacy YAML block never builds or
88 registers a service any more - it only raises a fixable repair pointing at the migration
89 (see repairs.py) and declines to set up (returning None is HA's supported "decline" path for
90 a legacy notify platform - a clean one-line log, no exception).
92 A `name:` in this leftover block still gets synced onto the owning entry every load though
93 (not gated behind that repair), and likewise for its template_path/media_path/etc and
94 archive/dupe_check/housekeeping settings - otherwise an entry auto-bootstrapped blank by
95 async_setup (see __init__.py), which happens before anyone gets around to opening and
96 confirming the migration repair, would keep running on defaults with nothing configured,
97 silently breaking automations, template/media paths and archiving on every restart until the
98 repair is manually confirmed. That repair is only ever needed for delivery/transports/
99 scenarios/etc - a "simple" install with none of that has no reason to see it at all, so this
100 core migration must not depend on it.
101 """
102 _ = discovery_info
104 from .repairs import async_create_legacy_yaml_issue, async_sync_entry_from_legacy_config
106 legacy_config = dict(config)
107 async_sync_entry_from_legacy_config(hass, legacy_config)
108 async_create_legacy_yaml_issue(hass, legacy_config)
111async def async_setup_entry(
112 hass: HomeAssistant,
113 entry: SupernotifyConfigEntry,
114 async_add_entities: AddConfigEntryEntitiesCallback,
115) -> None:
116 """Expose each configured recipient as its own notify entity.
118 Forwarded to from async_setup_entry in __init__.py once the SupernotifyEngine (entry.
119 runtime_data) is fully initialized, so people_registry is already populated.
120 """
121 # _ = hass
122 entry.runtime_data.context.people_registry.expose_notify_entities(
123 entry.entry_id, async_add_entities, cast(NotifyEntityPlatform, entry.runtime_data)
124 )