Coverage for custom_components/supernotify/transports/notify_entity.py: 100%

46 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-09-25 21:14 +0000

1from __future__ import annotations 

2 

3import logging 

4from typing import TYPE_CHECKING, Any 

5 

6from homeassistant.const import ATTR_ENTITY_ID # ATTR_VARIABLES from script.const has import issues 

7from homeassistant.helpers.typing import ConfigType 

8 

9from custom_components.supernotify.const import ( 

10 INCLUSION_DEFAULT, 

11 RE_NOTIFY_ENTITY_ID, 

12 TRANSPORT_NOTIFY_ENTITY, 

13) 

14from custom_components.supernotify.model import ( 

15 DebugTrace, 

16 MessageOnlyPolicy, 

17 TransportConfig, 

18 TransportFeature, 

19) 

20from custom_components.supernotify.options import ( 

21 OPTION_MESSAGE_USAGE, 

22 OPTION_SIMPLIFY_TEXT, 

23 OPTION_STRIP_URLS, 

24 OPTION_TARGET_SELECT, 

25 OPTION_UNIQUE_TARGETS, 

26) 

27from custom_components.supernotify.schema import SelectionRank 

28from custom_components.supernotify.target import TargetEntityCategory 

29from custom_components.supernotify.transport import ( 

30 Transport, 

31) 

32 

33if TYPE_CHECKING: 

34 from custom_components.supernotify.envelope import Envelope 

35 from custom_components.supernotify.hass_api import HomeAssistantAPI 

36 

37_LOGGER = logging.getLogger(__name__) 

38 

39 

40FIXED_ACTION = "notify.send_message" 

41 

42 

43class NotifyEntityTransport(Transport): 

44 """Call any notify entity""" 

45 

46 name = TRANSPORT_NOTIFY_ENTITY 

47 

48 def __init__(self, *args: Any, **kwargs: Any) -> None: 

49 super().__init__(*args, **kwargs) 

50 

51 @property 

52 def supported_features(self) -> TransportFeature: 

53 return TransportFeature.MESSAGE | TransportFeature.TITLE 

54 

55 @property 

56 def default_config(self) -> TransportConfig: 

57 config = TransportConfig() 

58 config.delivery_defaults.action = FIXED_ACTION 

59 config.delivery_defaults.selection_rank = SelectionRank.LAST 

60 config.delivery_defaults.inclusion = self.inclusion_mode 

61 config.delivery_defaults.options = { 

62 OPTION_SIMPLIFY_TEXT: False, 

63 OPTION_STRIP_URLS: False, 

64 OPTION_MESSAGE_USAGE: MessageOnlyPolicy.STANDARD, 

65 OPTION_UNIQUE_TARGETS: True, 

66 OPTION_TARGET_SELECT: [RE_NOTIFY_ENTITY_ID], 

67 } 

68 return config 

69 

70 @property 

71 def target_categories(self) -> list[str | TargetEntityCategory]: 

72 # no platform restriction - the generic catch-all for any notify.* entity not 

73 # claimed by a more specific transport (html5, alexa_devices); selection_rank=LAST 

74 # ensures those get first refusal 

75 return [TargetEntityCategory(domain="notify")] 

76 

77 @property 

78 def inclusion_mode(self) -> list[str]: 

79 # a notify.* entity maps cleanly to a recipient, so it's reasonable to fire on 

80 # every notification by default 

81 return [INCLUSION_DEFAULT] 

82 

83 def is_viable(self, hass_api: HomeAssistantAPI) -> bool: 

84 return bool(hass_api.entity_ids_for_domain("notify")) 

85 

86 def build_standard_deliveries(self, hass_api: HomeAssistantAPI) -> dict[str, ConfigType]: 

87 return {self.name: {}} 

88 

89 async def deliver(self, envelope: Envelope, debug_trace: DebugTrace | None = None) -> bool: 

90 targets = envelope.target.entity_ids or [] 

91 if not targets: 

92 _LOGGER.warning("SUPERNOTIFY notify_entity: no targets") 

93 return False 

94 target_data: dict[str, Any] = {ATTR_ENTITY_ID: targets} 

95 # area_id 

96 # device_id 

97 # label_id 

98 action_data = envelope.core_action_data() 

99 

100 return await self.call_action(envelope, FIXED_ACTION, action_data=action_data, target_data=target_data)