From 6bc12e17fd0fd34622517b6fa871342983571007 Mon Sep 17 00:00:00 2001 From: Nicholas Alipaz Date: Mon, 19 Jun 2017 14:52:14 -0700 Subject: [PATCH] Initial mqtt notification agent --- plexpy/config.py | 23 ++++++ plexpy/notifiers.py | 175 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 197 insertions(+), 1 deletion(-) diff --git a/plexpy/config.py b/plexpy/config.py index 119c0cca..2e5c9247 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -297,6 +297,29 @@ _CONFIG_DEFINITIONS = { 'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0), 'MONITORING_INTERVAL': (int, 'Monitoring', 60), 'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0), + 'MQTT_ENABLED': (int, 'MQTT', 0), + 'MQTT_ON_PLAY': (int, 'MQTT', 0), + 'MQTT_ON_STOP': (int, 'MQTT', 0), + 'MQTT_ON_PAUSE': (int, 'MQTT', 0), + 'MQTT_ON_RESUME': (int, 'MQTT', 0), + 'MQTT_ON_BUFFER': (int, 'MQTT', 0), + 'MQTT_ON_WATCHED': (int, 'MQTT', 0), + 'MQTT_ON_CREATED': (int, 'MQTT', 0), + 'MQTT_ON_EXTDOWN': (int, 'MQTT', 0), + 'MQTT_ON_INTDOWN': (int, 'MQTT', 0), + 'MQTT_ON_EXTUP': (int, 'MQTT', 0), + 'MQTT_ON_INTUP': (int, 'MQTT', 0), + 'MQTT_ON_PMSUPDATE': (int, 'MQTT', 0), + 'MQTT_ON_CONCURRENT': (int, 'MQTT', 0), + 'MQTT_ON_NEWDEVICE': (int, 'MQTT', 0), + 'MQTT_BROKER': (str, 'MQTT', '127.0.0.1'), + 'MQTT_PORT': (int, 'MQTT', 1883), + 'MQTT_KEEP_ALIVE': (int, 'MQTT', 60), + 'MQTT_BIND_ADDRESS': (str, 'MQTT', ''), + 'MQTT_PROTOCOL': (str, 'MQTT', ''), + 'MQTT_USERNAME': (str, 'MQTT', ''), + 'MQTT_PASSWORD': (str, 'MQTT', ''), + 'MQTT_TOPIC': (str, 'MQTT', 'plexpy'), 'NMA_APIKEY': (str, 'NMA', ''), 'NMA_ENABLED': (int, 'NMA', 0), 'NMA_PRIORITY': (int, 'NMA', 0), diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 33cd6c28..55729e39 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -87,7 +87,8 @@ AGENT_IDS = {'growl': 0, 'join': 18, 'hipchat': 19, 'discord': 20, - 'androidapp': 21 + 'androidapp': 21, + 'mqtt': 22 } @@ -184,6 +185,11 @@ def available_notification_agents(): 'name': 'osx', 'id': AGENT_IDS['osx'] }) + if MQTT().validate(): + agents.append({'label': 'MQTT', + 'name': 'mqtt', + 'id': AGENT_IDS['mqtt'] + }) return agents @@ -362,6 +368,8 @@ def get_agent_class(agent_id=None, config=None): return DISCORD(config=config) elif agent_id == 21: return ANDROIDAPP(config=config) + elif agent_id == 22: + return MQTT(config=config) else: return Notifier(config=config) else: @@ -3167,6 +3175,170 @@ class XBMC(Notifier): return config_option +class MQTT(Notifier): + """ + MQTT notifications + """ + _DEFAULT_CONFIG = {'broker': '', + 'port': '1883', + 'keep_alive': '60', + 'bind_address': '', + 'protocol': 'MQTTv311', + 'username': '', + 'password': '', + 'topic': '' + } + + def __init__(self, config=None): + self.set_config(config) + self.data = '' + self.loop_flag = 1 + self.counter = 0 + self.success = False + self.qos = 1 + self.retain = False + self.client_id = 'plexpy' + logger.info(u"PlexPy Notifiers :: MQTT init.") + + try: + self.mqtt = __import__("paho.mqtt.client", globals(), locals(), ['client'], 0) + self.mqtt_client = self.mqtt.Client(None, True, None, config['protocol']) + self.mqtt_client.username_pw_set(config['username'], config['password']) + self.mqtt_client.on_connect = self.on_connect + self.mqtt_client.on_publish = self.on_publish + except: + logger.error(u"PlexPy Notifiers :: Cannot load MQTT Notifications agent.") + pass + + logger.info(u"PlexPy Notifiers :: MQTT initialized.") + + + def validate(self): + try: + self.mqtt = __import__("paho.mqtt.client", globals(), locals(), ['client'], 0) + return True + except: + return False + + def on_connect(self, client, userdata, flags, rc): + logger.info(u"PlexPy Notifiers :: MQTT notification: connect") + if rc == 0: + status = 'Connection successful' + elif rc == 1: + status = 'Connection refused - incorrect protocol version' + elif rc == 2: + status = 'Connection refused - invalid client identifier' + elif rc == 3: + status = 'Connection refused - server unavailable' + elif rc == 4: + status = 'Connection refused - bad username or password' + elif rc == 5: + status = 'Connection refused - not authorised' + else: + status = 'Connection refused - unknown error' + + if rc == 0: + logger.info(u"PlexPy Notifiers :: MQTT connection: %s.", status) + logger.info(u"PlexPy Notifiers :: MQTT notification: Publishing message.") + (rc, mid) = self.mqtt_client.publish(self.topic, json.dumps(self.data), self.qos, self.retain) + else: + logger.warn(u"PlexPy Notifiers :: MQTT connection: %s.", status) + + def on_publish(self, client, userdata, mid): + logger.info(u"PlexPy Notifiers :: MQTT notification: publish") + self.mqtt_client.disconnect() + self.mqtt_client.loop_stop() + self.loop_flag = 0 + if mid == 1: + self.success = True + logger.info(u"PlexPy Notifiers :: MQTT notification: Publishing succeeded.") + if self.qos == 0: + logger.info(u"PlexPy Notifiers :: MQTT notification: Note that because QoS is set to %s we can't be sure everything was delivered, only that the message was sent.", self.qos) + else: + logger.warn(u"PlexPy Notifiers :: MQTT notification: Publishing failed.") + + def notify(self, subject='', body='', action='', **kwargs): + logger.info(u"PlexPy Notifiers :: MQTT notification: notify.") + if not subject or not body: + return + logger.info(u"PlexPy Notifiers :: MQTT notification: subject/body okay.") + self.data = {'title': subject.encode("utf-8"), + 'body': body.encode("utf-8"), + 'topic': self.config['topic'].encode("utf-8")} + logger.info(u"PlexPy Notifiers :: MQTT notification: data okay.") + + self.mqtt_client.connect(self.config['broker'], port=self.config['port'], keepalive=self.config['keep_alive'], bind_address=self.config['bind_address']) + self.mqtt_client.loop_start() + logger.info(u"PlexPy Notifiers :: MQTT notification: connect ran.") + logger.info(u"PlexPy Notifiers :: MQTT notification: loop started.") + + while self.loop_flag == 1 and self.counter < 1000: + logger.info(u"PlexPy Notifiers :: MQTT notification: waiting for on_publish to complete %s", self.counter) + time.sleep(.01) + self.counter+=1 + + self.mqtt_client.disconnect() + self.mqtt_client.loop_stop() + return self.success + + def return_config_options(self): + config_option = [{'label': 'Broker', + 'value': self.config['broker'], + 'name': 'mqtt_broker', + 'description': 'The hostname or IP address of the remote broker.', + 'input_type': 'text' + }, + {'label': 'Port', + 'value': self.config['port'], + 'name': 'mqtt_port', + 'description': 'The network port of the server host to connect to.', + 'input_type': 'number' + }, + {'label': 'Keep-alive', + 'value': self.config['keep_alive'], + 'name': 'mqtt_keep_alive', + 'description': 'Maximum period in seconds allowed between communications with the broker.', + 'input_type': 'number' + }, + {'label': 'Bind Address', + 'value': self.config['bind_address'], + 'name': 'mqtt_bind_address', + 'description': 'The IP address of a local network interface to bind this client to, assuming multiple interfaces exist. Typically left empty.', + 'input_type': 'text' + }, + {'label': 'Protocol', + 'value': self.config['protocol'], + 'name': 'mqtt_protocol', + 'description': 'The version of the MQTT protocol to use.', + 'input_type': 'select', + 'select_options': {'': '', + 'MQTTv31': '3.1', + 'MQTTv311': '3.1.1' + } + }, + {'label': 'Username', + 'value': self.config['username'], + 'name': 'mqtt_username', + 'description': 'The username to authenticate to the mqtt broker.', + 'input_type': 'text' + }, + {'label': 'Password', + 'value': self.config['password'], + 'name': 'mqtt_password', + 'description': 'The password to authenticate to the mqtt broker.', + 'input_type': 'password' + }, + {'label': 'Topic', + 'value': self.config['topic'], + 'name': 'mqtt_topic', + 'description': 'The topic to publish notifications to.', + 'input_type': 'text' + } + ] + + return config_option + + def upgrade_config_to_db(): logger.info(u"PlexPy Notifiers :: Upgrading to new notification system...") @@ -3280,3 +3452,4 @@ def upgrade_config_to_db(): # Add a new notifier and update the config notifier_id = add_notifier_config(agent_id=agent_id) set_notifier_config(notifier_id=notifier_id, agent_id=agent_id, **notifier_config) +