core/homeassistant/components/pushbullet/notify.py

178 lines
6.4 KiB
Python
Raw Normal View History

"""Pushbullet platform for notify component."""
import logging
import mimetypes
from pushbullet import InvalidKeyError, PushBullet, PushError
import voluptuous as vol
from homeassistant.components.notify import (
2019-07-31 19:25:30 +00:00
ATTR_DATA,
ATTR_TARGET,
ATTR_TITLE,
ATTR_TITLE_DEFAULT,
PLATFORM_SCHEMA,
BaseNotificationService,
)
from homeassistant.const import CONF_API_KEY
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
ATTR_URL = "url"
ATTR_FILE = "file"
ATTR_FILE_URL = "file_url"
ATTR_LIST = "list"
2019-07-31 19:25:30 +00:00
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_API_KEY): cv.string})
def get_service(hass, config, discovery_info=None):
2017-03-27 08:35:27 +00:00
"""Get the Pushbullet notification service."""
try:
pushbullet = PushBullet(config[CONF_API_KEY])
2015-01-11 16:09:25 +00:00
except InvalidKeyError:
_LOGGER.error("Wrong API key supplied")
2015-11-09 06:15:34 +00:00
return None
return PushBulletNotificationService(pushbullet)
class PushBulletNotificationService(BaseNotificationService):
2016-03-08 10:46:32 +00:00
"""Implement the notification service for Pushbullet."""
def __init__(self, pb):
2016-03-08 10:46:32 +00:00
"""Initialize the service."""
self.pushbullet = pb
self.pbtargets = {}
self.refresh()
def refresh(self):
2016-03-08 10:46:32 +00:00
"""Refresh devices, contacts, etc.
2017-03-27 08:35:27 +00:00
pbtargets stores all targets available from this Pushbullet instance
into a dict. These are Pushbullet objects!. It sacrifices a bit of
memory for faster processing at send_message.
As of sept 2015, contacts were replaced by chats. This is not
2015-11-25 07:56:50 +00:00
implemented in the module yet.
"""
2015-11-15 20:49:42 +00:00
self.pushbullet.refresh()
self.pbtargets = {
2019-07-31 19:25:30 +00:00
"device": {tgt.nickname.lower(): tgt for tgt in self.pushbullet.devices},
"channel": {
tgt.channel_tag.lower(): tgt for tgt in self.pushbullet.channels
},
}
def send_message(self, message=None, **kwargs):
2016-03-08 10:46:32 +00:00
"""Send a message to a specified target.
2015-11-15 20:49:42 +00:00
If no target specified, a 'normal' push will be sent to all devices
2017-03-27 08:35:27 +00:00
linked to the Pushbullet account.
Email is special, these are assumed to always exist. We use a special
2015-11-25 07:56:50 +00:00
call which doesn't require a push object.
2015-11-15 20:49:42 +00:00
"""
targets = kwargs.get(ATTR_TARGET)
title = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
data = kwargs.get(ATTR_DATA)
2015-11-15 23:46:16 +00:00
refreshed = False
if not targets:
# Backward compatibility, notify all devices in own account.
self._push_data(message, title, data, self.pushbullet)
2017-03-27 08:35:27 +00:00
_LOGGER.info("Sent notification to self")
2015-11-15 23:46:16 +00:00
return
# Main loop, process all targets specified.
2015-11-15 23:46:16 +00:00
for target in targets:
try:
2019-07-31 19:25:30 +00:00
ttype, tname = target.split("/", 1)
except ValueError:
2017-03-27 08:35:27 +00:00
_LOGGER.error("Invalid target syntax: %s", target)
continue
2015-11-15 23:46:16 +00:00
# Target is email, send directly, don't use a target object.
# This also seems to work to send to all devices in own account.
2019-07-31 19:25:30 +00:00
if ttype == "email":
self._push_data(message, title, data, self.pushbullet, email=tname)
2017-03-27 08:35:27 +00:00
_LOGGER.info("Sent notification to email %s", tname)
continue
# Target is sms, send directly, don't use a target object.
if ttype == "sms":
self._push_data(
message, title, data, self.pushbullet, phonenumber=tname
)
_LOGGER.info("Sent sms notification to %s", tname)
continue
# Refresh if name not found. While awaiting periodic refresh
# solution in component, poor mans refresh.
if ttype not in self.pbtargets:
2017-03-27 08:35:27 +00:00
_LOGGER.error("Invalid target syntax: %s", target)
continue
2015-11-28 23:41:30 +00:00
tname = tname.lower()
if tname not in self.pbtargets[ttype] and not refreshed:
2015-11-15 23:46:16 +00:00
self.refresh()
refreshed = True
# Attempt push_note on a dict value. Keys are types & target
# name. Dict pbtargets has all *actual* targets.
try:
2019-07-31 19:25:30 +00:00
self._push_data(message, title, data, self.pbtargets[ttype][tname])
2017-03-27 08:35:27 +00:00
_LOGGER.info("Sent notification to %s/%s", ttype, tname)
2015-11-15 23:46:16 +00:00
except KeyError:
2017-03-27 08:35:27 +00:00
_LOGGER.error("No such target: %s/%s", ttype, tname)
2015-11-15 23:46:16 +00:00
continue
def _push_data(self, message, title, data, pusher, email=None, phonenumber=None):
"""Create the message content."""
2019-07-31 19:25:30 +00:00
if data is None:
data = {}
data_list = data.get(ATTR_LIST)
url = data.get(ATTR_URL)
filepath = data.get(ATTR_FILE)
file_url = data.get(ATTR_FILE_URL)
try:
email_kwargs = {}
if email:
2019-07-31 19:25:30 +00:00
email_kwargs["email"] = email
if phonenumber:
device = pusher.devices[0]
pusher.push_sms(device, phonenumber, message)
elif url:
pusher.push_link(title, url, body=message, **email_kwargs)
elif filepath:
if not self.hass.config.is_allowed_path(filepath):
_LOGGER.error("Filepath is not valid or allowed")
return
2019-07-31 19:25:30 +00:00
with open(filepath, "rb") as fileh:
filedata = self.pushbullet.upload_file(fileh, filepath)
2019-07-31 19:25:30 +00:00
if filedata.get("file_type") == "application/x-empty":
_LOGGER.error("Can not send an empty file")
return
filedata.update(email_kwargs)
2019-07-31 19:25:30 +00:00
pusher.push_file(title=title, body=message, **filedata)
elif file_url:
2019-07-31 19:25:30 +00:00
if not file_url.startswith("http"):
_LOGGER.error("URL should start with http or https")
return
2019-07-31 19:25:30 +00:00
pusher.push_file(
title=title,
body=message,
file_name=file_url,
file_url=file_url,
file_type=(mimetypes.guess_type(file_url)[0]),
**email_kwargs,
)
elif data_list:
pusher.push_list(title, data_list, **email_kwargs)
else:
pusher.push_note(title, message, **email_kwargs)
except PushError as err:
_LOGGER.error("Notify failed: %s", err)