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

37 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2026-04-01 15:06 +0000

1import logging 

2from typing import TYPE_CHECKING, Any 

3 

4from homeassistant.components.notify.const import ATTR_DATA, ATTR_TARGET 

5 

6from custom_components.supernotify.const import ( 

7 ATTR_PHONE, 

8 OPTION_MESSAGE_USAGE, 

9 OPTION_SIMPLIFY_TEXT, 

10 OPTION_STRIP_URLS, 

11 OPTION_TARGET_CATEGORIES, 

12 TRANSPORT_SMS, 

13) 

14from custom_components.supernotify.model import DebugTrace, MessageOnlyPolicy, TransportConfig, TransportFeature 

15from custom_components.supernotify.transport import ( 

16 Transport, 

17) 

18 

19if TYPE_CHECKING: 

20 from custom_components.supernotify.envelope import Envelope 

21 

22RE_VALID_PHONE = r"^(\+\d{1,3})?\s?\(?\d{1,4}\)?[\s.-]?\d{3}[\s.-]?\d{4}$" 

23 

24_LOGGER = logging.getLogger(__name__) 

25 

26 

27class SMSTransport(Transport): 

28 name = TRANSPORT_SMS 

29 MAX_MESSAGE_LENGTH = 158 

30 

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

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

33 

34 @property 

35 def supported_features(self) -> TransportFeature: 

36 return TransportFeature.MESSAGE | TransportFeature.TITLE 

37 

38 @property 

39 def default_config(self) -> TransportConfig: 

40 config = TransportConfig() 

41 config.delivery_defaults.options = { 

42 OPTION_SIMPLIFY_TEXT: True, 

43 OPTION_STRIP_URLS: False, 

44 OPTION_MESSAGE_USAGE: MessageOnlyPolicy.COMBINE_TITLE, 

45 OPTION_TARGET_CATEGORIES: [ATTR_PHONE], 

46 } 

47 return config 

48 

49 def validate_action(self, action: str | None) -> bool: 

50 """Override in subclass if transport has fixed action or doesn't require one""" 

51 return action is not None 

52 

53 async def deliver(self, envelope: Envelope, debug_trace: DebugTrace | None = None) -> bool: # noqa: ARG002 

54 _LOGGER.debug("SUPERNOTIFY notify_sms: %s", envelope.delivery_name) 

55 

56 data: dict[str, Any] = envelope.data or {} 

57 mobile_numbers = envelope.target.phone or [] 

58 

59 if not envelope.message: 

60 _LOGGER.warning("SUPERNOTIFY notify_sms: No message to send") 

61 return False 

62 

63 message: str = envelope.message or "" 

64 if len(message) > self.MAX_MESSAGE_LENGTH: 

65 _LOGGER.debug( 

66 "SUPERNOTIFY notify_sms: Message too long (%d characters), truncating to %d characters", 

67 len(message), 

68 self.MAX_MESSAGE_LENGTH, 

69 ) 

70 

71 action_data = {"message": message[: self.MAX_MESSAGE_LENGTH], ATTR_TARGET: mobile_numbers} 

72 if data and data.get("data"): 

73 action_data[ATTR_DATA] = data.get("data", {}) 

74 

75 return await self.call_action(envelope, action_data=action_data)