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

38 statements  

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

1import logging 

2from typing import Any 

3 

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

5 

6from custom_components.supernotify 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.envelope import Envelope 

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

16from custom_components.supernotify.transport import ( 

17 Transport, 

18) 

19 

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

21 

22_LOGGER = logging.getLogger(__name__) 

23 

24 

25class SMSTransport(Transport): 

26 name = TRANSPORT_SMS 

27 MAX_MESSAGE_LENGTH = 158 

28 

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

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

31 

32 @property 

33 def supported_features(self) -> TransportFeature: 

34 return TransportFeature.MESSAGE | TransportFeature.TITLE 

35 

36 @property 

37 def default_config(self) -> TransportConfig: 

38 config = TransportConfig() 

39 config.delivery_defaults.options = { 

40 OPTION_SIMPLIFY_TEXT: True, 

41 OPTION_STRIP_URLS: False, 

42 OPTION_MESSAGE_USAGE: MessageOnlyPolicy.COMBINE_TITLE, 

43 OPTION_TARGET_CATEGORIES: [ATTR_PHONE], 

44 } 

45 return config 

46 

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

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

49 return action is not None 

50 

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

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

53 

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

55 mobile_numbers = envelope.target.phone or [] 

56 

57 if not envelope.message: 

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

59 return False 

60 

61 message: str = envelope.message or "" 

62 if len(message) > self.MAX_MESSAGE_LENGTH: 

63 _LOGGER.debug( 

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

65 len(message), 

66 self.MAX_MESSAGE_LENGTH, 

67 ) 

68 

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

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

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

72 

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