Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export AUTO_NTFY_DONE_IGNORE="vim screen meld"
implementation that doesn\'t have additional dependencies
- [Rocket.Chat](https://Rocket.Chat) support requires installing
as `pip install ntfy[rocketchat]`
- [MQTT](https://mqtt.org) support requires installing as
`pip install ntfy[mqtt]`

To install multiple extras, separate with commas: e.g.,
`pip install ntfy[pid,emoji]`.
Expand Down Expand Up @@ -441,6 +443,7 @@ Required parameters:

You must either specify `token`, or `userId` and `password`.


[Webpush](https://github.com/dschep/ntfy-webpush) - `ntfy_webpush`
\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~
Webpush support is provded by an external ntfy module, install like
Expand All @@ -458,6 +461,24 @@ Required parameters:
For more info, see [ntfy-webpush]{#ntfy-webpush}
\<https://github.com/dschep/ntfy-webpush\>\`\_

### [MQTT](https://mqtt.org) - `mqtt`
-------------------------------------------------------------
Requires extras, install like this: `pip install ntfy[mqtt]`.

Required parameters:

: - `hostname` - The MQTT broker hostname
- `topic` - The MQTT topic to publish to

Optional parameters:

: - `port` - The MQTT broker port (defaults to 1883)
- `qos` - The Quality of Service level (defaults to 1)
- `username` - The MQTT username
- `password` - The MQTT password



### 3rd party backends

To use or implement your own backends, specify the full path of the
Expand Down
42 changes: 42 additions & 0 deletions ntfy/backends/mqtt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import json
import logging
import paho.mqtt.publish as publish

def notify(title, message, **kwargs):
"""
Publishes a notification to an MQTT topic.
"""
hostname = kwargs.get("hostname")
topic = kwargs.get("topic")

if not hostname or not topic:
logging.error("MQTT backend requires 'hostname' and 'topic' parameters in the configuration.")
return

port = int(kwargs.get("port", 1883))
qos = int(kwargs.get("qos", 1))
retcode = kwargs.get("retcode")
username = kwargs.get("username")
password = kwargs.get("password")

payload = json.dumps({
"title": title,
"message": message,
"retcode": retcode
})

auth = None
if username:
auth = {'username': username, 'password': password}

try:
publish.single(
topic=topic,
payload=payload,
hostname=hostname,
port=port,
qos=qos,
auth=auth
)
except Exception as e:
logging.error("Failed to publish MQTT message: %s", e)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
'slack':['slack_sdk'],
'rocketchat':['rocketchat-API'],
'matrix':['matrix_client'],
'mqtt': ['paho-mqtt'],
}
test_deps = ['mock', 'sleekxmpp', 'emoji', 'psutil']

Expand Down
58 changes: 58 additions & 0 deletions tests/test_mqtt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import json
from unittest import TestCase
from mock import patch
from ntfy.backends.mqtt import notify

class TestMQTT(TestCase):
@patch('paho.mqtt.publish.single')
def test_basic(self, mock_publish):
notify('title', 'message', hostname='localhost', topic='test')
mock_publish.assert_called_once_with(
topic='test',
payload=json.dumps({'title': 'title', 'message': 'message', 'retcode': None}),
hostname='localhost',
port=1883,
qos=1,
auth=None
)

@patch('paho.mqtt.publish.single')
def test_full(self, mock_publish):
notify(
'title',
'message',
hostname='example.com',
topic='ntfy',
port=8883,
qos=2,
username='user',
password='pass',
retcode=0
)
mock_publish.assert_called_once_with(
topic='ntfy',
payload=json.dumps({'title': 'title', 'message': 'message', 'retcode': 0}),
hostname='example.com',
port=8883,
qos=2,
auth={'username': 'user', 'password': 'pass'}
)

@patch('paho.mqtt.publish.single')
@patch('logging.error')
def test_missing_config(self, mock_log, mock_publish):
notify('title', 'message', hostname='localhost')
mock_publish.assert_not_called()
mock_log.assert_called_once()

mock_log.reset_mock()
notify('title', 'message', topic='test')
mock_publish.assert_not_called()
mock_log.assert_called_once()

@patch('paho.mqtt.publish.single')
@patch('logging.error')
def test_publish_error(self, mock_log, mock_publish):
mock_publish.side_effect = Exception('MQTT Error')
notify('title', 'message', hostname='localhost', topic='test')
mock_log.assert_called_once()