Coverage for custom_components / supernotify / transports / sms.py: 100%
38 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-11 22:18 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-11 22:18 +0000
1from __future__ import annotations
3import logging
4from typing import TYPE_CHECKING, Any
6from homeassistant.components.notify.const import ATTR_DATA, ATTR_TARGET
8from custom_components.supernotify.const import (
9 ATTR_PHONE,
10 OPTION_MESSAGE_USAGE,
11 OPTION_SIMPLIFY_TEXT,
12 OPTION_STRIP_URLS,
13 OPTION_TARGET_CATEGORIES,
14 TRANSPORT_SMS,
15)
16from custom_components.supernotify.model import DebugTrace, MessageOnlyPolicy, TransportConfig, TransportFeature
17from custom_components.supernotify.transport import (
18 Transport,
19)
21if TYPE_CHECKING:
22 from custom_components.supernotify.envelope import Envelope
24RE_VALID_PHONE = r"^(\+\d{1,3})?\s?\(?\d{1,4}\)?[\s.-]?\d{3}[\s.-]?\d{4}$"
26_LOGGER = logging.getLogger(__name__)
29class SMSTransport(Transport):
30 name = TRANSPORT_SMS
31 MAX_MESSAGE_LENGTH = 158
33 def __init__(self, *args: Any, **kwargs: Any) -> None:
34 super().__init__(*args, **kwargs)
36 @property
37 def supported_features(self) -> TransportFeature:
38 return TransportFeature.MESSAGE | TransportFeature.TITLE
40 @property
41 def default_config(self) -> TransportConfig:
42 config = TransportConfig()
43 config.delivery_defaults.options = {
44 OPTION_SIMPLIFY_TEXT: True,
45 OPTION_STRIP_URLS: False,
46 OPTION_MESSAGE_USAGE: MessageOnlyPolicy.COMBINE_TITLE,
47 OPTION_TARGET_CATEGORIES: [ATTR_PHONE],
48 }
49 return config
51 def validate_action(self, action: str | None) -> bool:
52 """Override in subclass if transport has fixed action or doesn't require one"""
53 return action is not None
55 async def deliver(self, envelope: Envelope, debug_trace: DebugTrace | None = None) -> bool: # noqa: ARG002
56 _LOGGER.debug("SUPERNOTIFY notify_sms: %s", envelope.delivery_name)
58 data: dict[str, Any] = envelope.data or {}
59 mobile_numbers = envelope.target.phone or []
61 if not envelope.message:
62 _LOGGER.warning("SUPERNOTIFY notify_sms: No message to send")
63 return False
65 message: str = envelope.message or ""
66 if len(message) > self.MAX_MESSAGE_LENGTH:
67 _LOGGER.debug(
68 "SUPERNOTIFY notify_sms: Message too long (%d characters), truncating to %d characters",
69 len(message),
70 self.MAX_MESSAGE_LENGTH,
71 )
73 action_data = {"message": message[: self.MAX_MESSAGE_LENGTH], ATTR_TARGET: mobile_numbers}
74 if data and data.get("data"):
75 action_data[ATTR_DATA] = data.get("data", {})
77 return await self.call_action(envelope, action_data=action_data)