diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index e6cdf372dc8..8e1d20e1376 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -51,8 +51,7 @@ def setup(hass, config): continue # create platform service - notify_service = notify_implementation.get_service( - hass, {DOMAIN: p_config}) + notify_service = notify_implementation.get_service(hass, p_config) if notify_service is None: _LOGGER.error("Failed to initialize notification service %s", diff --git a/homeassistant/components/notify/file.py b/homeassistant/components/notify/file.py index 3fe6d555524..41e5fd90667 100644 --- a/homeassistant/components/notify/file.py +++ b/homeassistant/components/notify/file.py @@ -20,14 +20,14 @@ _LOGGER = logging.getLogger(__name__) def get_service(hass, config): """ Get the file notification service. """ - if not validate_config(config, + if not validate_config({DOMAIN: config}, {DOMAIN: ['filename', 'timestamp']}, _LOGGER): return None - filename = config[DOMAIN]['filename'] - timestamp = config[DOMAIN]['timestamp'] + filename = config['filename'] + timestamp = config['timestamp'] return FileNotificationService(hass, filename, timestamp) diff --git a/homeassistant/components/notify/instapush.py b/homeassistant/components/notify/instapush.py index 839eac24a0d..82a48e3ddeb 100644 --- a/homeassistant/components/notify/instapush.py +++ b/homeassistant/components/notify/instapush.py @@ -9,6 +9,8 @@ https://home-assistant.io/components/notify.instapush.html import logging import json +import requests + from homeassistant.helpers import validate_config from homeassistant.components.notify import ( DOMAIN, ATTR_TITLE, BaseNotificationService) @@ -21,7 +23,7 @@ _RESOURCE = 'https://api.instapush.im/v1/' def get_service(hass, config): """ Get the instapush notification service. """ - if not validate_config(config, + if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_API_KEY, 'app_secret', 'event', @@ -29,52 +31,29 @@ def get_service(hass, config): _LOGGER): return None + headers = {'x-instapush-appid': config[CONF_API_KEY], + 'x-instapush-appsecret': config['app_secret']} + try: - import requests - - except ImportError: - _LOGGER.exception( - "Unable to import requests. " - "Did you maybe not install the 'Requests' package?") - + response = requests.get(_RESOURCE + 'events/list', + headers=headers).json() + except ValueError: + _LOGGER.error('Unexpected answer from Instapush API.') return None - # pylint: disable=unused-variable - try: - response = requests.get(_RESOURCE) + if 'error' in response: + _LOGGER.error(response['msg']) + return None - except requests.ConnectionError: + if len([app for app in response if app['title'] == config['event']]) == 0: _LOGGER.error( - "Connection error " - "Please check if https://instapush.im is available.") - + "No app match your given value. " + "Please create an app at https://instapush.im") return None - instapush = requests.Session() - headers = {'x-instapush-appid': config[DOMAIN][CONF_API_KEY], - 'x-instapush-appsecret': config[DOMAIN]['app_secret']} - response = instapush.get(_RESOURCE + 'events/list', - headers=headers) - - try: - if response.json()['error']: - _LOGGER.error(response.json()['msg']) - # pylint: disable=bare-except - except: - try: - next(events for events in response.json() - if events['title'] == config[DOMAIN]['event']) - except StopIteration: - _LOGGER.error( - "No event match your given value. " - "Please create an event at https://instapush.im") - else: - return InstapushNotificationService( - config[DOMAIN].get(CONF_API_KEY), - config[DOMAIN]['app_secret'], - config[DOMAIN]['event'], - config[DOMAIN]['tracker'] - ) + return InstapushNotificationService( + config[CONF_API_KEY], config['app_secret'], config['event'], + config['tracker']) # pylint: disable=too-few-public-methods @@ -82,9 +61,6 @@ class InstapushNotificationService(BaseNotificationService): """ Implements notification service for Instapush. """ def __init__(self, api_key, app_secret, event, tracker): - # pylint: disable=no-name-in-module, unused-variable - from requests import Session - self._api_key = api_key self._app_secret = app_secret self._event = event @@ -94,8 +70,6 @@ class InstapushNotificationService(BaseNotificationService): 'x-instapush-appsecret': self._app_secret, 'Content-Type': 'application/json'} - self.instapush = Session() - def send_message(self, message="", **kwargs): """ Send a message to a user. """ @@ -104,10 +78,8 @@ class InstapushNotificationService(BaseNotificationService): data = {"event": self._event, "trackers": {self._tracker: title + " : " + message}} - response = self.instapush.post( - _RESOURCE + 'post', - data=json.dumps(data), - headers=self._headers) + response = requests.post(_RESOURCE + 'post', data=json.dumps(data), + headers=self._headers) if response.json()['status'] == 401: _LOGGER.error( diff --git a/homeassistant/components/notify/nma.py b/homeassistant/components/notify/nma.py index a9fa6559e71..4c3920d5b79 100644 --- a/homeassistant/components/notify/nma.py +++ b/homeassistant/components/notify/nma.py @@ -9,6 +9,8 @@ https://home-assistant.io/components/notify.nma.html import logging import xml.etree.ElementTree as ET +import requests + from homeassistant.helpers import validate_config from homeassistant.components.notify import ( DOMAIN, ATTR_TITLE, BaseNotificationService) @@ -21,31 +23,20 @@ _RESOURCE = 'https://www.notifymyandroid.com/publicapi/' def get_service(hass, config): """ Get the NMA notification service. """ - if not validate_config(config, + if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_API_KEY]}, _LOGGER): return None - try: - # pylint: disable=unused-variable - from requests import Session - - except ImportError: - _LOGGER.exception( - "Unable to import requests. " - "Did you maybe not install the 'Requests' package?") - - return None - - nma = Session() - response = nma.get(_RESOURCE + 'verify', - params={"apikey": config[DOMAIN][CONF_API_KEY]}) + response = requests.get(_RESOURCE + 'verify', + params={"apikey": config[CONF_API_KEY]}) tree = ET.fromstring(response.content) if tree[0].tag == 'error': _LOGGER.error("Wrong API key supplied. %s", tree[0].text) - else: - return NmaNotificationService(config[DOMAIN][CONF_API_KEY]) + return None + + return NmaNotificationService(config[CONF_API_KEY]) # pylint: disable=too-few-public-methods @@ -53,26 +44,20 @@ class NmaNotificationService(BaseNotificationService): """ Implements notification service for NMA. """ def __init__(self, api_key): - # pylint: disable=no-name-in-module, unused-variable - from requests import Session - self._api_key = api_key - self._data = {"apikey": self._api_key} - - self.nma = Session() def send_message(self, message="", **kwargs): """ Send a message to a user. """ - title = kwargs.get(ATTR_TITLE) + data = { + "apikey": self._api_key, + "application": 'home-assistant', + "event": kwargs.get(ATTR_TITLE), + "description": message, + "priority": 0, + } - self._data['application'] = 'home-assistant' - self._data['event'] = title - self._data['description'] = message - self._data['priority'] = 0 - - response = self.nma.get(_RESOURCE + 'notify', - params=self._data) + response = requests.get(_RESOURCE + 'notify', params=data) tree = ET.fromstring(response.content) if tree[0].tag == 'error': diff --git a/homeassistant/components/notify/pushbullet.py b/homeassistant/components/notify/pushbullet.py index 49aaccd3004..50b25410b27 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/notify/pushbullet.py @@ -8,41 +8,29 @@ https://home-assistant.io/components/notify.pushbullet.html """ import logging -from homeassistant.helpers import validate_config -from homeassistant.components.notify import ( - DOMAIN, ATTR_TITLE, BaseNotificationService) +from homeassistant.components.notify import ATTR_TITLE, BaseNotificationService from homeassistant.const import CONF_API_KEY _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pushbullet.py==0.7.1'] +REQUIREMENTS = ['pushbullet.py==0.8.1'] def get_service(hass, config): """ Get the PushBullet notification service. """ + from pushbullet import InvalidKeyError - if not validate_config(config, - {DOMAIN: [CONF_API_KEY]}, - _LOGGER): + if CONF_API_KEY not in config: + _LOGGER.error("Unable to find config key '%s'", CONF_API_KEY) return None try: - # pylint: disable=unused-variable - from pushbullet import PushBullet, InvalidKeyError # noqa - - except ImportError: - _LOGGER.exception( - "Unable to import pushbullet. " - "Did you maybe not install the 'pushbullet.py' package?") - - return None - - try: - return PushBulletNotificationService(config[DOMAIN][CONF_API_KEY]) + return PushBulletNotificationService(config[CONF_API_KEY]) except InvalidKeyError: _LOGGER.error( "Wrong API key supplied. " "Get it at https://www.pushbullet.com/account") + return None # pylint: disable=too-few-public-methods @@ -50,9 +38,9 @@ class PushBulletNotificationService(BaseNotificationService): """ Implements notification service for Pushbullet. """ def __init__(self, api_key): - from pushbullet import PushBullet + from pushbullet import Pushbullet - self.pushbullet = PushBullet(api_key) + self.pushbullet = Pushbullet(api_key) def send_message(self, message="", **kwargs): """ Send a message to a user. """ diff --git a/homeassistant/components/notify/pushover.py b/homeassistant/components/notify/pushover.py index e2b4b4c4b40..80c9bad1509 100644 --- a/homeassistant/components/notify/pushover.py +++ b/homeassistant/components/notify/pushover.py @@ -21,32 +21,21 @@ _LOGGER = logging.getLogger(__name__) def get_service(hass, config): """ Get the pushover notification service. """ - if not validate_config(config, + if not validate_config({DOMAIN: config}, {DOMAIN: ['user_key', CONF_API_KEY]}, _LOGGER): return None - try: - # pylint: disable=no-name-in-module, unused-variable - from pushover import InitError - - except ImportError: - _LOGGER.exception( - "Unable to import pushover. " - "Did you maybe not install the 'python-pushover.py' package?") - - return None + from pushover import InitError try: - api_token = config[DOMAIN].get(CONF_API_KEY) - return PushoverNotificationService( - config[DOMAIN]['user_key'], - api_token) - + return PushoverNotificationService(config['user_key'], + config[CONF_API_KEY]) except InitError: _LOGGER.error( "Wrong API key supplied. " "Get it at https://pushover.net") + return None # pylint: disable=too-few-public-methods @@ -54,7 +43,6 @@ class PushoverNotificationService(BaseNotificationService): """ Implements notification service for Pushover. """ def __init__(self, user_key, api_token): - # pylint: disable=no-name-in-module, unused-variable from pushover import Client self._user_key = user_key self._api_token = api_token @@ -63,11 +51,9 @@ class PushoverNotificationService(BaseNotificationService): def send_message(self, message="", **kwargs): """ Send a message to a user. """ - - # pylint: disable=no-name-in-module from pushover import RequestError - title = kwargs.get(ATTR_TITLE) + try: - self.pushover.send_message(message, title=title) + self.pushover.send_message(message, title=kwargs.get(ATTR_TITLE)) except RequestError: _LOGGER.exception("Could not send pushover notification") diff --git a/homeassistant/components/notify/services.yaml b/homeassistant/components/notify/services.yaml index e69de29bb2d..3bfdb6d4de3 100644 --- a/homeassistant/components/notify/services.yaml +++ b/homeassistant/components/notify/services.yaml @@ -0,0 +1,11 @@ +notify: + description: Send a notification + + fields: + message: + description: Message body of the notification. + example: The garage door has been open for 10 minutes. + + title: + description: Optional title for your notification. + example: 'Your Garage Door Friend' diff --git a/homeassistant/components/notify/slack.py b/homeassistant/components/notify/slack.py index 0b168ed5075..3542b060c34 100644 --- a/homeassistant/components/notify/slack.py +++ b/homeassistant/components/notify/slack.py @@ -9,8 +9,7 @@ https://home-assistant.io/components/notify.slack.html import logging from homeassistant.helpers import validate_config -from homeassistant.components.notify import ( - DOMAIN, BaseNotificationService) +from homeassistant.components.notify import DOMAIN, BaseNotificationService from homeassistant.const import CONF_API_KEY REQUIREMENTS = ['slacker==0.6.8'] @@ -20,34 +19,22 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=unused-variable def get_service(hass, config): """ Get the slack notification service. """ + import slacker - if not validate_config(config, + if not validate_config({DOMAIN: config}, {DOMAIN: ['default_channel', CONF_API_KEY]}, _LOGGER): return None try: - # pylint: disable=no-name-in-module, unused-variable - from slacker import Error as SlackError - - except ImportError: - _LOGGER.exception( - "Unable to import slacker. " - "Did you maybe not install the 'slacker.py' package?") - - return None - - try: - api_token = config[DOMAIN].get(CONF_API_KEY) - return SlackNotificationService( - config[DOMAIN]['default_channel'], - api_token) + config['default_channel'], + config[CONF_API_KEY]) - except SlackError as ex: - _LOGGER.error( + except slacker.Error: + _LOGGER.exception( "Slack authentication failed") - _LOGGER.exception(ex) + return None # pylint: disable=too-few-public-methods @@ -56,6 +43,7 @@ class SlackNotificationService(BaseNotificationService): def __init__(self, default_channel, api_token): from slacker import Slacker + self._default_channel = default_channel self._api_token = api_token self.slack = Slacker(self._api_token) @@ -63,11 +51,11 @@ class SlackNotificationService(BaseNotificationService): def send_message(self, message="", **kwargs): """ Send a message to a user. """ + import slacker - from slacker import Error as SlackError channel = kwargs.get('channel', self._default_channel) + try: self.slack.chat.post_message(channel, message) - except SlackError as ex: + except slacker.Error: _LOGGER.exception("Could not send slack notification") - _LOGGER.exception(ex) diff --git a/homeassistant/components/notify/smtp.py b/homeassistant/components/notify/smtp.py index 758e7839e50..65c0d381d80 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/notify/smtp.py @@ -20,35 +20,31 @@ _LOGGER = logging.getLogger(__name__) def get_service(hass, config): """ Get the mail notification service. """ - if not validate_config(config, - {DOMAIN: ['server', - 'port', - 'sender', - 'username', - 'password', - 'recipient']}, - _LOGGER): + if not validate_config( + {DOMAIN: config}, + {DOMAIN: ['server', 'port', 'sender', 'username', 'password', + 'recipient']}, _LOGGER): return None - smtp_server = config[DOMAIN]['server'] - port = int(config[DOMAIN]['port']) - username = config[DOMAIN]['username'] - password = config[DOMAIN]['password'] + smtp_server = config['server'] + port = int(config['port']) + username = config['username'] + password = config['password'] + starttls = int(config['starttls']) server = None try: server = smtplib.SMTP(smtp_server, port) server.ehlo() - if int(config[DOMAIN]['starttls']) == 1: + if starttls == 1: server.starttls() server.ehlo() try: server.login(username, password) - except (smtplib.SMTPException, smtplib.SMTPSenderRefused) as error: - _LOGGER.exception(error, - "Please check your settings.") + except (smtplib.SMTPException, smtplib.SMTPSenderRefused): + _LOGGER.exception("Please check your settings.") return None @@ -66,18 +62,13 @@ def get_service(hass, config): return None - if server: - server.quit() + finally: + if server: + server.quit() return MailNotificationService( - config[DOMAIN]['server'], - config[DOMAIN]['port'], - config[DOMAIN]['sender'], - config[DOMAIN]['starttls'], - config[DOMAIN]['username'], - config[DOMAIN]['password'], - config[DOMAIN]['recipient'] - ) + smtp_server, port, config['sender'], starttls, username, password, + config['recipient']) # pylint: disable=too-few-public-methods, too-many-instance-attributes @@ -90,7 +81,7 @@ class MailNotificationService(BaseNotificationService): self._server = server self._port = port self._sender = sender - self.starttls = int(starttls) + self.starttls = starttls self.username = username self.password = password self.recipient = recipient diff --git a/homeassistant/components/notify/syslog.py b/homeassistant/components/notify/syslog.py index 7881e68476d..2c000ed037e 100644 --- a/homeassistant/components/notify/syslog.py +++ b/homeassistant/components/notify/syslog.py @@ -52,16 +52,14 @@ PRIORITIES = {5: syslog.LOG_EMERG, def get_service(hass, config): """ Get the mail notification service. """ - if not validate_config(config, - {DOMAIN: ['facility', - 'option', - 'priority']}, + if not validate_config({DOMAIN: config}, + {DOMAIN: ['facility', 'option', 'priority']}, _LOGGER): return None - _facility = FACILITIES.get(config[DOMAIN]['facility'], 40) - _option = OPTIONS.get(config[DOMAIN]['option'], 10) - _priority = PRIORITIES.get(config[DOMAIN]['priority'], -1) + _facility = FACILITIES.get(config['facility'], 40) + _option = OPTIONS.get(config['option'], 10) + _priority = PRIORITIES.get(config['priority'], -1) return SyslogNotificationService(_facility, _option, _priority) diff --git a/homeassistant/components/notify/telegram.py b/homeassistant/components/notify/telegram.py index 46f1acb1e75..690f964bad8 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/notify/telegram.py @@ -16,35 +16,27 @@ from homeassistant.const import CONF_API_KEY _LOGGER = logging.getLogger(__name__) -try: - import telegram -except ImportError: - _LOGGER.exception( - "Unable to import python-telegram-bot. " - "Did you maybe not install the 'python-telegram-bot' package?") - REQUIREMENTS = ['python-telegram-bot==2.8.7'] def get_service(hass, config): """ Get the Telegram notification service. """ + import telegram - if not validate_config(config, + if not validate_config({DOMAIN: config}, {DOMAIN: [CONF_API_KEY, 'chat_id']}, _LOGGER): return None try: - bot = telegram.Bot(token=config[DOMAIN][CONF_API_KEY]) + bot = telegram.Bot(token=config[CONF_API_KEY]) username = bot.getMe()['username'] _LOGGER.info("Telegram bot is '%s'.", username) except urllib.error.HTTPError: _LOGGER.error("Please check your access token.") return None - return TelegramNotificationService( - config[DOMAIN][CONF_API_KEY], - config[DOMAIN]['chat_id']) + return TelegramNotificationService(config[CONF_API_KEY], config['chat_id']) # pylint: disable=too-few-public-methods @@ -52,12 +44,15 @@ class TelegramNotificationService(BaseNotificationService): """ Implements notification service for Telegram. """ def __init__(self, api_key, chat_id): + import telegram + self._api_key = api_key self._chat_id = chat_id self.bot = telegram.Bot(token=self._api_key) def send_message(self, message="", **kwargs): """ Send a message to a user. """ + import telegram title = kwargs.get(ATTR_TITLE) @@ -65,4 +60,4 @@ class TelegramNotificationService(BaseNotificationService): self.bot.sendMessage(chat_id=self._chat_id, text=title + " " + message) except telegram.error.TelegramError: - _LOGGER.error("Your chat id '%s' is not valid.", self._chat_id) + _LOGGER.exception("Error sending message.") diff --git a/homeassistant/components/notify/xmpp.py b/homeassistant/components/notify/xmpp.py index d4f5a7336a6..c29f14a51ad 100644 --- a/homeassistant/components/notify/xmpp.py +++ b/homeassistant/components/notify/xmpp.py @@ -10,14 +10,6 @@ import logging _LOGGER = logging.getLogger(__name__) -try: - import sleekxmpp - -except ImportError: - _LOGGER.exception( - "Unable to import sleekxmpp. " - "Did you maybe not install the 'SleekXMPP' package?") - from homeassistant.helpers import validate_config from homeassistant.components.notify import ( DOMAIN, ATTR_TITLE, BaseNotificationService) @@ -28,28 +20,14 @@ REQUIREMENTS = ['sleekxmpp==1.3.1', 'dnspython3==1.12.0'] def get_service(hass, config): """ Get the Jabber (XMPP) notification service. """ - if not validate_config(config, - {DOMAIN: ['sender', - 'password', - 'recipient']}, + if not validate_config({DOMAIN: config}, + {DOMAIN: ['sender', 'password', 'recipient']}, _LOGGER): return None - try: - SendNotificationBot(config[DOMAIN]['sender'] + '/home-assistant', - config[DOMAIN]['password'], - config[DOMAIN]['recipient'], - '') - except ImportError: - _LOGGER.exception( - "Unable to contact jabber server." - "Please check your credentials.") - - return None - - return XmppNotificationService(config[DOMAIN]['sender'], - config[DOMAIN]['password'], - config[DOMAIN]['recipient']) + return XmppNotificationService(config['sender'], + config['password'], + config['recipient']) # pylint: disable=too-few-public-methods @@ -65,40 +43,39 @@ class XmppNotificationService(BaseNotificationService): """ Send a message to a user. """ title = kwargs.get(ATTR_TITLE) - data = title + ": " + message + data = "{}: {}".format(title, message) if title else message - SendNotificationBot(self._sender + '/home-assistant', - self._password, - self._recipient, - data) + send_message(self._sender + '/home-assistant', self._password, + self._recipient, data) -class SendNotificationBot(sleekxmpp.ClientXMPP): - """ Service for sending Jabber (XMPP) messages. """ +def send_message(sender, password, recipient, message): + import sleekxmpp - def __init__(self, jid, password, recipient, msg): + class SendNotificationBot(sleekxmpp.ClientXMPP): + """ Service for sending Jabber (XMPP) messages. """ - super(SendNotificationBot, self).__init__(jid, password) + def __init__(self): + super(SendNotificationBot, self).__init__(sender, password) - logging.basicConfig(level=logging.ERROR) + logging.basicConfig(level=logging.ERROR) - self.recipient = recipient - self.msg = msg + self.use_tls = True + self.use_ipv6 = False + self.add_event_handler('failed_auth', self.check_credentials) + self.add_event_handler('session_start', self.start) + self.connect() + self.process() - self.use_tls = True - self.use_ipv6 = False - self.add_event_handler('failed_auth', self.check_credentials) - self.add_event_handler('session_start', self.start) - self.connect() - self.process(block=False) + def start(self, event): + """ Starts the communication and sends the message. """ + self.send_presence() + self.get_roster() + self.send_message(mto=recipient, mbody=message, mtype='chat') + self.disconnect(wait=True) - def start(self, event): - """ Starts the communication and sends the message. """ - self.send_presence() - self.get_roster() - self.send_message(mto=self.recipient, mbody=self.msg, mtype='chat') - self.disconnect(wait=True) + def check_credentials(self, event): + """" Disconnect from the server if credentials are invalid. """ + self.disconnect() - def check_credentials(self, event): - """" Disconnect from the server if credentials are invalid. """ - self.disconnect() + SendNotificationBot(sender, password, recipient, message) diff --git a/requirements_all.txt b/requirements_all.txt index 3843a431cca..ca5be2e24a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -29,7 +29,7 @@ tellcore-py==1.1.2 python-nmap==0.4.3 # PushBullet (notify.pushbullet) -pushbullet.py==0.7.1 +pushbullet.py==0.8.1 # Nest Thermostat (thermostat.nest) python-nest==2.6.0