Coverage for custom_components/supernotify/transports/sms.py: 89%
35 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-11-21 23:31 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-11-21 23:31 +0000
1import logging
2from typing import Any
4from homeassistant.components.notify.const import ATTR_DATA, ATTR_TARGET
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 MessageOnlyPolicy, TransportConfig
16from custom_components.supernotify.transport import (
17 Transport,
18)
20RE_VALID_PHONE = r"^(\+\d{1,3})?\s?\(?\d{1,4}\)?[\s.-]?\d{3}[\s.-]?\d{4}$"
22_LOGGER = logging.getLogger(__name__)
25class SMSTransport(Transport):
26 name = TRANSPORT_SMS
27 MAX_MESSAGE_LENGTH = 158
29 def __init__(self, *args: Any, **kwargs: Any) -> None:
30 super().__init__(*args, **kwargs)
32 @property
33 def default_config(self) -> TransportConfig:
34 config = TransportConfig()
35 config.delivery_defaults.options = {
36 OPTION_SIMPLIFY_TEXT: True,
37 OPTION_STRIP_URLS: False,
38 OPTION_MESSAGE_USAGE: MessageOnlyPolicy.COMBINE_TITLE,
39 OPTION_TARGET_CATEGORIES: [ATTR_PHONE],
40 }
41 return config
43 def validate_action(self, action: str | None) -> bool:
44 """Override in subclass if transport has fixed action or doesn't require one"""
45 return action is not None
47 async def deliver(self, envelope: Envelope) -> bool:
48 _LOGGER.debug("SUPERNOTIFY notify_sms: %s", envelope.delivery_name)
50 data: dict[str, Any] = envelope.data or {}
51 mobile_numbers = envelope.target.phone or []
53 if not envelope.message:
54 _LOGGER.warning("SUPERNOTIFY notify_sms: No message to send")
55 return False
57 message: str = envelope.message or ""
58 if len(message) > self.MAX_MESSAGE_LENGTH:
59 _LOGGER.debug(
60 "SUPERNOTIFY notify_sms: Message too long (%d characters), truncating to %d characters",
61 len(message),
62 self.MAX_MESSAGE_LENGTH,
63 )
65 action_data = {"message": message[: self.MAX_MESSAGE_LENGTH], ATTR_TARGET: mobile_numbers}
66 if data and data.get("data"):
67 action_data[ATTR_DATA] = data.get("data", {})
69 return await self.call_action(envelope, action_data=action_data)