1 """Support for mobile_app push notifications."""
3 from __future__
import annotations
6 from functools
import partial
7 from http
import HTTPStatus
18 BaseNotificationService,
32 ATTR_PUSH_RATE_LIMITS,
33 ATTR_PUSH_RATE_LIMITS_ERRORS,
34 ATTR_PUSH_RATE_LIMITS_MAXIMUM,
35 ATTR_PUSH_RATE_LIMITS_RESETS_AT,
36 ATTR_PUSH_RATE_LIMITS_SUCCESSFUL,
45 from .util
import supports_push
47 _LOGGER = logging.getLogger(__name__)
51 """Return a dictionary of push enabled registrations."""
54 for webhook_id, entry
in hass.data[DOMAIN][DATA_CONFIG_ENTRIES].items():
58 targets[entry.data[ATTR_DEVICE_NAME]] = webhook_id
64 """Output rate limit log line at given level."""
65 if ATTR_PUSH_RATE_LIMITS
not in resp:
68 rate_limits = resp[ATTR_PUSH_RATE_LIMITS]
69 resetsAt = rate_limits[ATTR_PUSH_RATE_LIMITS_RESETS_AT]
70 resetsAtTime = dt_util.parse_datetime(resetsAt) - dt_util.utcnow()
72 "mobile_app push notification rate limits for %s: "
73 "%d sent, %d allowed, %d errors, "
80 rate_limits[ATTR_PUSH_RATE_LIMITS_SUCCESSFUL],
81 rate_limits[ATTR_PUSH_RATE_LIMITS_MAXIMUM],
82 rate_limits[ATTR_PUSH_RATE_LIMITS_ERRORS],
83 str(resetsAtTime).split(
".", maxsplit=1)[0],
90 discovery_info: DiscoveryInfoType |
None =
None,
91 ) -> MobileAppNotificationService:
92 """Get the mobile_app notification service."""
98 """Implement the notification service for mobile_app."""
101 """Initialize the service."""
106 """Return a dictionary of registered targets."""
110 """Send a message to the Lambda APNS gateway."""
111 data = {ATTR_MESSAGE: message}
115 kwargs.get(ATTR_TITLE)
is not None
116 and kwargs.get(ATTR_TITLE) != ATTR_TITLE_DEFAULT
118 data[ATTR_TITLE] = kwargs.get(ATTR_TITLE)
120 if not (targets := kwargs.get(ATTR_TARGET)):
123 if kwargs.get(ATTR_DATA)
is not None:
124 data[ATTR_DATA] = kwargs.get(ATTR_DATA)
126 local_push_channels = self.hass.data[DOMAIN][DATA_PUSH_CHANNEL]
128 for target
in targets:
129 registration = self.hass.data[DOMAIN][DATA_CONFIG_ENTRIES][target].data
131 if target
in local_push_channels:
141 if ATTR_PUSH_URL
not in registration[ATTR_APP_DATA]:
143 "Device not connected to local push notifications"
149 """Send a message to a target."""
150 app_data = registration[ATTR_APP_DATA]
151 push_token = app_data[ATTR_PUSH_TOKEN]
152 push_url = app_data[ATTR_PUSH_URL]
154 target_data =
dict(data)
155 target_data[ATTR_PUSH_TOKEN] = push_token
158 ATTR_APP_ID: registration[ATTR_APP_ID],
159 ATTR_APP_VERSION: registration[ATTR_APP_VERSION],
160 ATTR_WEBHOOK_ID: target,
162 if ATTR_OS_VERSION
in registration:
163 reg_info[ATTR_OS_VERSION] = registration[ATTR_OS_VERSION]
165 target_data[
"registration_info"] = reg_info
168 async
with asyncio.timeout(10):
170 push_url, json=target_data
172 result = await response.json()
174 if response.status
in (
182 fallback_error = result.get(
"errorMessage",
"Unknown error")
184 f
"Internal server error, please try again later: {fallback_error}"
186 message = result.get(
"message", fallback_message)
188 if "message" in result:
189 if message[-1]
not in [
".",
"?",
"!"]:
191 message +=
" This message is generated externally to Home Assistant."
193 if response.status == HTTPStatus.TOO_MANY_REQUESTS:
194 _LOGGER.warning(message)
196 self.hass, registration[ATTR_DEVICE_NAME], result, logging.WARNING
199 _LOGGER.error(message)
202 _LOGGER.error(
"Timeout sending notification to %s", push_url)
203 except aiohttp.ClientError
as err:
204 _LOGGER.error(
"Error sending notification to %s: %r", push_url, err)
def async_send_message(self, message="", **kwargs)
def _async_send_remote_message_target(self, target, registration, data)
web.Response post(self, web.Request request, str config_key)
None async_send_notification(LaMetricDataUpdateCoordinator coordinator, ServiceCall call, list[Chart|Goal|Simple] frames)
MobileAppNotificationService async_get_service(HomeAssistant hass, ConfigType config, DiscoveryInfoType|None discovery_info=None)
def log_rate_limits(hass, device_name, resp, level=logging.INFO)
def push_registrations(hass)
bool supports_push(HomeAssistant hass, str webhook_id)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)