diff --git a/front/plugins/_publisher_pushover/README.md b/front/plugins/_publisher_pushover/README.md
new file mode 100755
index 00000000..2717edd4
--- /dev/null
+++ b/front/plugins/_publisher_pushover/README.md
@@ -0,0 +1,8 @@
+## Overview
+
+A plugin to publish a notification via the Pushover gateway. Enable sending notifications via Pushover.
+
+### Usage
+
+- Go to settings and fill in relevant details.
+
diff --git a/front/plugins/_publisher_pushover/config.json b/front/plugins/_publisher_pushover/config.json
new file mode 100755
index 00000000..cec4be3a
--- /dev/null
+++ b/front/plugins/_publisher_pushover/config.json
@@ -0,0 +1,409 @@
+{
+ "code_name": "_publisher_pushover",
+ "unique_prefix": "PUSHOVER",
+ "plugin_type": "publisher",
+ "enabled": true,
+ "data_source": "script",
+ "show_ui": true,
+ "localized": [
+ "display_name",
+ "description",
+ "icon"
+ ],
+ "display_name": [
+ {
+ "language_code": "en_us",
+ "string": "Pushover publisher"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Habilitar Pushover"
+ }
+ ],
+ "icon": [
+ {
+ "language_code": "en_us",
+ "string": ""
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "A plugin to publish a notification via the pushover.net"
+ }
+ ],
+ "params": [],
+ "database_column_definitions": [
+ {
+ "column": "Index",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ },
+ {
+ "language_code": "es_es",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "Plugin",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ },
+ {
+ "language_code": "es_es",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "Object_PrimaryID",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "Object_SecondaryID",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Sent when"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value1",
+ "css_classes": "col-sm-3",
+ "show": true,
+ "type": "eval",
+ "default_value": "",
+ "options": [
+ {
+ "type": "eval",
+ "param": "`${value}`"
+ }
+ ],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Notification GUID"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value2",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "textarea_readonly",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Response"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value3",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Response code"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value4",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "device_mac",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Device"
+ }
+ ]
+ },
+ {
+ "column": "UserData",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "textbox_save",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Comments"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comentarios"
+ }
+ ]
+ },
+ {
+ "column": "Status",
+ "css_classes": "col-sm-1",
+ "show": false,
+ "type": "replace",
+ "default_value": "",
+ "options": [
+ {
+ "equals": "watched-not-changed",
+ "replacement": "
"
+ },
+ {
+ "equals": "watched-changed",
+ "replacement": "
"
+ },
+ {
+ "equals": "new",
+ "replacement": "
"
+ },
+ {
+ "equals": "missing-in-last-scan",
+ "replacement": "
"
+ }
+ ],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Status"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Estado"
+ }
+ ]
+ },
+ {
+ "column": "Extra",
+ "css_classes": "col-sm-3",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Extra"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Extra"
+ }
+ ]
+ }
+ ],
+ "settings": [
+ {
+ "function": "RUN",
+ "events": [
+ "test"
+ ],
+ "type": "text.select",
+ "default_value": "disabled",
+ "options": [
+ "disabled",
+ "on_notification"
+ ],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "When to run"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Cuando ejecuta"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Enable sending notifications via
Pushover."
+ },
+ {
+ "language_code": "es_es",
+ "string": "Habilitar el envío de notificaciones a través de
Pushover."
+ }
+ ]
+ },
+ {
+ "function": "CMD",
+ "type": "readonly",
+ "default_value": "python3 /home/pi/pialert/front/plugins/_publisher_pushover/pushover.py",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Command"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comando"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Command to run"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comando a ejecutar"
+ }
+ ]
+ },
+ {
+ "function": "RUN_TIMEOUT",
+ "type": "integer",
+ "default_value": 10,
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Run timeout"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Tiempo de espera de ejecución"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Wartezeit"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
+ }
+ ]
+ },
+ {
+ "function": "USER_KEY",
+ "type": "text",
+ "default_value": "USER_KEY",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Pushover USER key"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Your secret Pushsafer USER key."
+ }
+ ]
+ },
+ {
+ "function": "APP_TOKEN",
+ "type": "text",
+ "default_value": "APP_TOKEN",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Pushover APP Token"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Your Pushover APP Token."
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/front/plugins/_publisher_pushover/pushover.py b/front/plugins/_publisher_pushover/pushover.py
new file mode 100755
index 00000000..78c8c4b3
--- /dev/null
+++ b/front/plugins/_publisher_pushover/pushover.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+import os
+import pathlib
+import sys
+import json
+import requests
+
+# Replace these paths with the actual paths to your Pi.Alert directories
+sys.path.extend(["/home/pi/pialert/front/plugins", "/home/pi/pialert/pialert"])
+
+from plugin_helper import Plugin_Objects, handleEmpty # noqa: E402
+from logger import mylog # noqa: E402
+from helper import timeNowTZ, get_setting_value, hide_string # noqa: E402
+from notification import Notification_obj # noqa: E402
+from database import DB # noqa: E402
+
+CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
+RESULT_FILE = os.path.join(CUR_PATH, "last_result.log")
+
+pluginName = "PUSHOVER"
+
+
+def main():
+ mylog("verbose", f"[{pluginName}](publisher) In script")
+
+ # Check if basic config settings supplied
+ if not validate_config():
+ mylog(
+ "none",
+ f"[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. "
+ f"Check your pialert.conf {pluginName}_* variables.",
+ )
+ return
+
+ # Create a database connection
+ db = DB() # instance of class DB
+ db.open()
+
+ # Initialize the Plugin obj output file
+ plugin_objects = Plugin_Objects(RESULT_FILE)
+
+ # Create a Notification_obj instance
+ notifications = Notification_obj(db)
+
+ # Retrieve new notifications
+ new_notifications = notifications.getNew()
+
+ # Process the new notifications
+ for notification in new_notifications:
+ # Send notification
+ response_text, response_status_code = send(notification["Text"])
+
+ # Log result
+ plugin_objects.add_object(
+ primaryId=pluginName,
+ secondaryId=timeNowTZ(),
+ watched1=notification["GUID"],
+ watched2=handleEmpty(response_text),
+ watched3=response_status_code,
+ watched4="null",
+ extra="null",
+ foreignKey=notification["GUID"],
+ )
+
+ plugin_objects.write_result_file()
+
+
+def send(text):
+ response_text = ""
+ response_status_code = ""
+
+ user_key = get_setting_value("PUSHOVER_USER_KEY")
+ app_token = get_setting_value("PUSHOVER_APP_TOKEN")
+
+ mylog("verbose", f'[{pluginName}] PUSHOVER_USER_KEY: "{hide_string(user_key)}"')
+ mylog("verbose", f'[{pluginName}] PUSHOVER_APP_TOKEN: "{hide_string(app_token)}"')
+
+ try:
+ response = requests.post(
+ "https://api.pushover.net/1/messages.json",
+ data={"token": app_token, "user": user_key, "message": text},
+ )
+
+ # Check if the request was successful (status code 200)
+ if response.status_code == 200:
+ response_text = response.text # This captures the response body/message
+ else:
+ response_text = json.dumps(response.text)
+
+ except requests.exceptions.RequestException as e:
+ mylog("none", f"[{pluginName}] ⚠ ERROR: {e}")
+ response_text = str(e)
+
+ return response_text, response_status_code
+
+
+def validate_config():
+ user_key = get_setting_value("PUSHOVER_USER_KEY")
+ app_token = get_setting_value("PUSHOVER_APP_TOKEN")
+
+ return user_key != "USER_KEY" and app_token != "APP_TOKEN"
+
+
+if __name__ == "__main__":
+ sys.exit(main())