Sending notifications with MQTT and Pushover.net

I’ve improved a bit on my Pushover.net setup, described in this previous article.

I started using MQTT, namely the Mosquitto broker for sending data to/from my machines, instead of the old point-to-point setup i was using.

MQTT provides publish/subscribe, with messages up to 256MB each. It also features QoS, ranging from 0 - which basically just means the message was sent on the network, to 2, where you have guaranteed delivery of messages to the broker.

The protocol also has the option to retain any received messages, so that “late arriving” subscribers can get the entire feed of messages.

The broker is running on a Raspberry Pi, and uses about 1MB memory.

the code for sending out push notifications now looks like this (github):

#!/usr/bin/env python3
import paho.mqtt.client as paho
import http.client, urllib
import argparse
import logging
import json
import sys


def parse_message(self, message):
    dataDict = json.loads(message)
    return dataDict

def on_connect(client, userdata, flags, rc):
    client.subscribe("/notification/#",0)

def on_message(client, userdata, msg):
	logging.info("received message with topic"+msg.topic)
	msgData = msg.payload.decode('utf-8')
	dataDict = json.loads(msgData)
	if('notification' in dataDict):
  	  notification = dataDict['notification']
    	conn = http.client.HTTPSConnection("api.pushover.net:443")
    conn.request("POST", "/1/messages.json",
    urllib.parse.urlencode({
        "token": "APP_TOKEN",
        "user": "USER_TOKEN",
        "message": notification,
    }), { "Content-type": "application/x-www-form-urlencoded" })
    conn.getresponse()

def get_args():
	ap = argparse.ArgumentParser()
    ap.add_argument('-H','--host',help="MQTT Host to connect to", default='localhost')
	ap.add_argument('-p','--port',help="MQTT port to connect to", type=int, default='1883')
	ap.add_argument('-v', help="Increase log level, can be specified multiple times", action='count', default=1)
    args = ap.parse_args()
	return args

def main():
    args = get_args()
    #loglevel = logging.WARNING
    loglevel = logging.INFO
    if args.v == 2:
        loglevel = logging.INFO
    elif (args.v > 2) or 'pdb' in sys.modules:
        loglevel = logging.DEBUG

	#Logger
    logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=loglevel)
    client = paho.Client()
	client.user_data_set(args)
    client.on_connect = on_connect
    client.on_message = on_message
    client.connect(args.host, args.port, 60)
    client.loop_forever()

The code starts by connecting to the broker, and once connected, subscribes to any message under the /notification namespace.

To send a notification from any device, all that is needed is the following code (i use JSON for the message format, which is nice and entirely optional):

#!/usr/bin/env python3
import paho.mqtt.publish as paho
paho.single('/notification/test','{"notification": "test body"}')

because the “server” listens for anything under /notification, i could make the namespace databearing, i.e. to send out notifications from this host :

'/notification/vile/temperature_alert'

would imply that the notification originated at ‘vile’ and we’re talking about a temperature alert - more info will be in the notification content.

With only 3 lines of code on the client, i can now send a push notification from any server/device/whatever, without the need for configuring anything except the hostname of the broker and the TLS certificate (optional but recommended)


See also