summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2021-05-18 20:03:06 +0300
committerGitHub <noreply@github.com>2021-05-18 20:03:06 +0300
commitbcf6a5f0df5154429870015745e22ad12c1b2cbd (patch)
treea7aa7b1678e6e79abd91c77dd688abbb7ddcd6d2
parentddaf888e477253b5dfcfa6016b44e4ab5ec51b47 (diff)
Add MQTT support and bump to v1.3.0 (#12)
* Initial MQTT branch * Cont * Added info to README. Added MQTT filter. * Added MQTT filter. * Fixed using localized string for filter * Added support to filter by area name Fixed compare case insensitive Updated README * Revert change * Fix error on missing mqtt init * Reorder params * Cleanup MQTT implementation * Update MQTT README * Bump to v1.3.0 Co-authored-by: Adi Miller <adi@gomiller.com>
-rw-r--r--README.md24
-rw-r--r--alarmpy/alarmpy.py79
-rw-r--r--setup.py12
3 files changed, 107 insertions, 8 deletions
diff --git a/README.md b/README.md
index 979ef1d..02e178d 100644
--- a/README.md
+++ b/README.md
@@ -59,9 +59,33 @@ Options:
--quiet Print only active alarms
--desktop-notifications Create push notifications on your desktop
notification center (currently only in Mac OS)
+ --mqtt-server TEXT Hostname / IP of MQTT server (optional)
+ --mqtt-client-id TEXT MQTT client identifier
+ --mqtt-port INTEGER Port for MQTT server
+ --mqtt-topic TEXT Topic on which to send MQTT messages
+ --mqtt-filter TEXT Payload value to filter before sending as a
+ message (semicolon separated)
--help Show this message and exit.
```
+### MQTT Notifications
+
+Integration with an MQTT server provides the ability to send custom MQTT messages for all or some of the alerts that are received. MQTT requires [`paho-mqtt`](https://pypi.org/project/paho-mqtt/) to be installed separately as an optional dependency.
+
+To enable, specify at least the following parameters via the command line:
+
+- `mqtt-server` - The MQTT Server hostname or IP, e.g. `localhost`
+- `mqtt-topic` - The MQTT topic to which the MQTT message will be sent, e.g. `alarmpy/zone`
+
+Additional optional parameters for MQTT integration are:
+
+- `mqtt-client-id` - The ID of the MQTT client used by alarmpy. This will be used to connect to the MQTT server. Default: `alarmPyClient`. This only needs to be change in case you plan to have more than one instance of alarmpy running
+- `mqtt-port` - The port on which the MQTT server is listening to. Default: `1883`
+
+#### Filtering
+
+When MQTT is enabled, all alerts are sent as separate messages on the specified topic. In case there is a desire to include only specific alert, use the `mqtt-filter` parameter to provide a semicolon separated list of substrings enclosed in double quotes. Each alert city and area will be checked against all filters, and only when a match is found, will an MQTT message be sent. For example: `--mqtt-filter "gaza;negev"`.
+
## License
[GPLv3](LICENSE)
diff --git a/alarmpy/alarmpy.py b/alarmpy/alarmpy.py
index 4fbe399..248b326 100644
--- a/alarmpy/alarmpy.py
+++ b/alarmpy/alarmpy.py
@@ -8,6 +8,11 @@ from datetime import datetime
from pathlib import Path
from time import sleep, time
+try:
+ import paho.mqtt.client as mqtt
+except ImportError:
+ pass
+
class Alarm:
@@ -28,6 +33,11 @@ class Alarm:
repeat_alarms=False,
quiet=False,
desktop_notifications=False,
+ mqtt_server="",
+ mqtt_client_id="alarmPyClient",
+ mqtt_port=1883,
+ mqtt_topic="",
+ mqtt_filter=None,
):
self.language = language
self.polling_delay = polling_delay
@@ -35,23 +45,50 @@ class Alarm:
self.alarm_id = alarm_id
self.repeat_alarms = repeat_alarms
self.quiet = quiet
-
- if desktop_notifications and not os.path.exists("/usr/bin/osascript"):
- self.output_error(
- "Desktop notifications are currently only available for MacOS"
- )
- desktop_notifications = False
self.desktop_notifications = desktop_notifications
+ self.mqtt_server = mqtt_server
+ self.mqtt_client_id = mqtt_client_id
+ self.mqtt_port = mqtt_port
+ self.mqtt_topic = mqtt_topic
+ self.mqtt_filter = mqtt_filter
+
self.current_alarms = []
self.last_routine_output = 0
self.session = self.init_session()
self.labels = self.load_labels()
+ self.init_desktop_notifications()
+ self.init_mqtt()
+
def init_session(self):
return requests.Session()
+ def init_desktop_notifications(self):
+ if self.desktop_notifications and not os.path.exists("/usr/bin/osascript"):
+ self.output_error(
+ "Desktop notifications are currently only available for MacOS"
+ )
+ self.desktop_notifications = False
+
+ def init_mqtt(self):
+ try:
+ self.mqtt = mqtt.Client(self.mqtt_client_id)
+ except NameError:
+ self.mqtt = None
+
+ self.filters = None
+ if self.mqtt_server and self.mqtt:
+ if not self.mqtt:
+ self.output_error(
+ "MQTT support cannot be instantiated without the paho-mqtt library installed"
+ )
+ self.mqtt.connect(self.mqtt_server, self.mqtt_port)
+ self.mqtt.loop_start()
+ if self.mqtt_filter:
+ self.filters = self.mqtt_filter.lower().split(";")
+
def load_labels(self):
DATA_DIR = Path(__file__).parent / "data"
with open(DATA_DIR / "labels.json", "r") as f:
@@ -82,6 +119,7 @@ class Alarm:
# empty content means no alarms
return [], None
+ data = {} # To avoid warning in KeyError
try:
data = res.json()
alarm_id = data["id"]
@@ -101,6 +139,7 @@ class Alarm:
def update_alarm(self, cities, alarm_id):
if self.repeat_alarms or set(cities) != set(self.current_alarms):
self.output_alarms(cities, alarm_id)
+ self.notify_alarms(cities)
self.current_alarms = cities
def update_routine(self):
@@ -142,6 +181,21 @@ class Alarm:
if self.alarm_id:
click.secho(f"({alarm_id})")
+ def notify_alarms(self, cities):
+ if self.mqtt_server and self.mqtt_topic:
+ for city in cities:
+ labels = self.labels.get(city, {})
+ area = labels.get(f"areaname_{self.language}", "")
+ label = labels.get(f"label_{self.language}", city)
+ if self.filters is None or self.check_filter(label, area):
+ self.mqtt.publish(self.mqtt_topic, label)
+
+ def check_filter(self, city, area):
+ for flt in self.filters:
+ if flt in city.lower() or flt in area.lower():
+ return True
+ return False
+
def group_areas_and_localize(self, cities):
res = defaultdict(list)
for city in cities:
@@ -174,6 +228,19 @@ class Alarm:
@click.option("--repeat-alarms", is_flag=True, help="Do not suppress ongoing alarms")
@click.option("--quiet", is_flag=True, help="Print only active alarms")
@click.option(
+ "--mqtt-server", default=None, help="Hostname / IP of MQTT server (optional)"
+)
+@click.option(
+ "--mqtt-client-id", default="alarmPyClient", help="MQTT client identifier"
+)
+@click.option("--mqtt-port", default=1883, help="Port for MQTT server")
+@click.option("--mqtt-topic", default=None, help="Topic on which to send MQTT messages")
+@click.option(
+ "--mqtt-filter",
+ default=None,
+ help="Payload value to filter before sending as a message (semicolon separated)",
+)
+@click.option(
"--desktop-notifications",
is_flag=True,
help="Create push notifications on your desktop notification center (currently only in Mac OS)",
diff --git a/setup.py b/setup.py
index e8952c0..ab74e1e 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup(
name="alarmpy",
- version="1.2.0",
+ version="1.3.0",
author="Yuval Adam",
author_email="_@yuv.al",
description="Pikud Ha'oref Alarm Tracking",
@@ -27,5 +27,13 @@ setuptools.setup(
package_data={"alarmpy": ["data/*.json"]},
python_requires=">=3.6",
install_requires=["requests", "click"],
- entry_points={"console_scripts": ["alarmpy = alarmpy:cli"]},
+ extras_require={
+ "mqttnotify": ["paho-mqtt"],
+ },
+ entry_points={
+ "console_scripts": [
+ "alarmpy = alarmpy:cli",
+ "alarmpynotify = alarmpy:cli [mqttnotify]",
+ ]
+ },
)