Home Assistant Unofficial Reference 2024.12.1
notify.py
Go to the documentation of this file.
1 """Pushsafer platform for notify component."""
2 
3 from __future__ import annotations
4 
5 import base64
6 from http import HTTPStatus
7 import logging
8 import mimetypes
9 
10 import requests
11 from requests.auth import HTTPBasicAuth
12 import voluptuous as vol
13 
15  ATTR_DATA,
16  ATTR_TARGET,
17  ATTR_TITLE,
18  ATTR_TITLE_DEFAULT,
19  PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA,
20  BaseNotificationService,
21 )
22 from homeassistant.const import ATTR_ICON
23 from homeassistant.core import HomeAssistant
25 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
26 
27 _LOGGER = logging.getLogger(__name__)
28 _RESOURCE = "https://www.pushsafer.com/api"
29 _ALLOWED_IMAGES = ["image/gif", "image/jpeg", "image/png"]
30 
31 CONF_DEVICE_KEY = "private_key"
32 CONF_TIMEOUT = 15
33 
34 # Top level attributes in 'data'
35 ATTR_SOUND = "sound"
36 ATTR_VIBRATION = "vibration"
37 ATTR_ICONCOLOR = "iconcolor"
38 ATTR_URL = "url"
39 ATTR_URLTITLE = "urltitle"
40 ATTR_TIME2LIVE = "time2live"
41 ATTR_PRIORITY = "priority"
42 ATTR_RETRY = "retry"
43 ATTR_EXPIRE = "expire"
44 ATTR_CONFIRM = "confirm"
45 ATTR_ANSWER = "answer"
46 ATTR_ANSWEROPTIONS = "answeroptions"
47 ATTR_ANSWERFORCE = "answerforce"
48 ATTR_PICTURE1 = "picture1"
49 
50 # Attributes contained in picture1
51 ATTR_PICTURE1_URL = "url"
52 ATTR_PICTURE1_PATH = "path"
53 ATTR_PICTURE1_USERNAME = "username"
54 ATTR_PICTURE1_PASSWORD = "password"
55 ATTR_PICTURE1_AUTH = "auth"
56 
57 PLATFORM_SCHEMA = NOTIFY_PLATFORM_SCHEMA.extend(
58  {vol.Required(CONF_DEVICE_KEY): cv.string}
59 )
60 
61 
63  hass: HomeAssistant,
64  config: ConfigType,
65  discovery_info: DiscoveryInfoType | None = None,
66 ) -> PushsaferNotificationService:
67  """Get the Pushsafer.com notification service."""
69  config.get(CONF_DEVICE_KEY), hass.config.is_allowed_path
70  )
71 
72 
73 class PushsaferNotificationService(BaseNotificationService):
74  """Implementation of the notification service for Pushsafer.com."""
75 
76  def __init__(self, private_key, is_allowed_path):
77  """Initialize the service."""
78  self._private_key_private_key = private_key
79  self.is_allowed_pathis_allowed_path = is_allowed_path
80 
81  def send_message(self, message="", **kwargs):
82  """Send a message to specified target."""
83  if kwargs.get(ATTR_TARGET) is None:
84  targets = ["a"]
85  _LOGGER.debug("No target specified. Sending push to all")
86  else:
87  targets = kwargs.get(ATTR_TARGET)
88  _LOGGER.debug("%s target(s) specified", len(targets))
89 
90  title = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
91  data = kwargs.get(ATTR_DATA, {})
92 
93  # Converting the specified image to base64
94  picture1 = data.get(ATTR_PICTURE1)
95  picture1_encoded = ""
96  if picture1 is not None:
97  _LOGGER.debug("picture1 is available")
98  url = picture1.get(ATTR_PICTURE1_URL, None)
99  local_path = picture1.get(ATTR_PICTURE1_PATH, None)
100  username = picture1.get(ATTR_PICTURE1_USERNAME)
101  password = picture1.get(ATTR_PICTURE1_PASSWORD)
102  auth = picture1.get(ATTR_PICTURE1_AUTH)
103 
104  if url is not None:
105  _LOGGER.debug("Loading image from url %s", url)
106  picture1_encoded = self.load_from_urlload_from_url(url, username, password, auth)
107  elif local_path is not None:
108  _LOGGER.debug("Loading image from file %s", local_path)
109  picture1_encoded = self.load_from_fileload_from_file(local_path)
110  else:
111  _LOGGER.warning("Missing url or local_path for picture1")
112  else:
113  _LOGGER.debug("picture1 is not specified")
114 
115  payload = {
116  "k": self._private_key_private_key,
117  "t": title,
118  "m": message,
119  "s": data.get(ATTR_SOUND, ""),
120  "v": data.get(ATTR_VIBRATION, ""),
121  "i": data.get(ATTR_ICON, ""),
122  "c": data.get(ATTR_ICONCOLOR, ""),
123  "u": data.get(ATTR_URL, ""),
124  "ut": data.get(ATTR_URLTITLE, ""),
125  "l": data.get(ATTR_TIME2LIVE, ""),
126  "pr": data.get(ATTR_PRIORITY, ""),
127  "re": data.get(ATTR_RETRY, ""),
128  "ex": data.get(ATTR_EXPIRE, ""),
129  "cr": data.get(ATTR_CONFIRM, ""),
130  "a": data.get(ATTR_ANSWER, ""),
131  "ao": data.get(ATTR_ANSWEROPTIONS, ""),
132  "af": data.get(ATTR_ANSWERFORCE, ""),
133  "p": picture1_encoded,
134  }
135 
136  for target in targets:
137  payload["d"] = target
138  response = requests.post(_RESOURCE, data=payload, timeout=CONF_TIMEOUT)
139  if response.status_code != HTTPStatus.OK:
140  _LOGGER.error("Pushsafer failed with: %s", response.text)
141  else:
142  _LOGGER.debug("Push send: %s", response.json())
143 
144  @classmethod
145  def get_base64(cls, filebyte, mimetype):
146  """Convert the image to the expected base64 string of pushsafer."""
147  if mimetype not in _ALLOWED_IMAGES:
148  _LOGGER.warning("%s is a not supported mimetype for images", mimetype)
149  return None
150 
151  base64_image = base64.b64encode(filebyte).decode("utf8")
152  return f"data:{mimetype};base64,{base64_image}"
153 
154  def load_from_url(self, url=None, username=None, password=None, auth=None):
155  """Load image/document/etc from URL."""
156  if url is not None:
157  _LOGGER.debug("Downloading image from %s", url)
158  if username is not None and password is not None:
159  auth_ = HTTPBasicAuth(username, password)
160  response = requests.get(url, auth=auth_, timeout=CONF_TIMEOUT)
161  else:
162  response = requests.get(url, timeout=CONF_TIMEOUT)
163  return self.get_base64get_base64(response.content, response.headers["content-type"])
164  _LOGGER.warning("No url was found in param")
165 
166  return None
167 
168  def load_from_file(self, local_path=None):
169  """Load image/document/etc from a local path."""
170  try:
171  if local_path is not None:
172  _LOGGER.debug("Loading image from local path")
173  if self.is_allowed_pathis_allowed_path(local_path):
174  file_mimetype = mimetypes.guess_type(local_path)
175  _LOGGER.debug("Detected mimetype %s", file_mimetype)
176  with open(local_path, "rb") as binary_file:
177  data = binary_file.read()
178  return self.get_base64get_base64(data, file_mimetype[0])
179  else:
180  _LOGGER.warning("Local path not found in params!")
181  except OSError as error:
182  _LOGGER.error("Can't load from local path: %s", error)
183 
184  return None
def load_from_url(self, url=None, username=None, password=None, auth=None)
Definition: notify.py:154
None open(self, **Any kwargs)
Definition: lock.py:86
PushsaferNotificationService get_service(HomeAssistant hass, ConfigType config, DiscoveryInfoType|None discovery_info=None)
Definition: notify.py:66