diff --git a/docs/web_api.md b/docs/web_api.md index ded0e03..cb3921b 100644 --- a/docs/web_api.md +++ b/docs/web_api.md @@ -3375,6 +3375,65 @@ Returns: Test results in the following format } ``` +### Notifier APIs +The following APIs are available to view and tests notifiers. + +#### List Notifiers + +HTTP request: +```http +GET /server/notifiers/list +``` +JSON-RPC request: +```json +{ + "jsonrpc": "2.0", + "method": "server.notifiers.list", + "id": 4654 +} +``` + +Returns: + +A list of configured notifiers: + +```json +{ + "notifiers": [ + { + "name": "print_start", + "url": "tgram://{bottoken}/{ChatID}", + "events": [ + "started" + ], + "body": "Your printer started printing '{event_args[1].filename}'", + "title": null, + "attach": null + }, + { + "name": "print_complete", + "url": "tgram://{bottoken}/{ChatID}", + "events": [ + "complete" + ], + "body": "Your printer completed printing '{event_args[1].filename}", + "title": null, + "attach": "http://192.168.1.100/webcam/?action=snapshot" + }, + { + "name": "print_error", + "url": "tgram://{bottoken}/{ChatID}", + "events": [ + "error" + ], + "body": "{event_args[1].message}", + "title": null, + "attach": "http://192.168.1.100/webcam/?action=snapshot" + } + ] +} +``` + ### Update Manager APIs The following endpoints are available when the `[update_manager]` component has been configured: @@ -5064,6 +5123,46 @@ JSON-RPC request: } ``` +#### Test a notifier (debug) + +You can trigger a notifier manually using this endpoint. + +HTTP request: +```http +POST /server/notifiers/test?name=notifier_name +``` +JSON-RPC request: +```json +{ + "jsonrpc": "2.0", + "method": "server.notifiers.test", + "params": { + "name": "notifier_name" + }, + "id": 4654 +} +``` + +Parameters: + +- `name`: The name of the notifier to test. + +Returns: Test results in the following format + +```json +{ + "status": "success", + "stats": { + "print_duration": 0.0, + "total_duration": 0.0, + "filament_used": 0.0, + "filename": "notifier_test.gcode", + "state": "standby", + "message": "" + } +} +``` + ### Websocket notifications Printer generated events are sent over the websocket as JSON-RPC 2.0 notifications. These notifications are sent to all connected clients diff --git a/moonraker/components/notifier.py b/moonraker/components/notifier.py index c7f4bed..cb9dfbc 100644 --- a/moonraker/components/notifier.py +++ b/moonraker/components/notifier.py @@ -22,6 +22,8 @@ from typing import ( if TYPE_CHECKING: from confighelper import ConfigHelper + from websockets import WebRequest + from .http_client import HttpClient from . import klippy_apis APIComp = klippy_apis.KlippyAPI @@ -54,6 +56,8 @@ class Notifier: continue self.notifiers[notifier.get_name()] = notifier + self.register_endpoints(config) + def register_remote_actions(self): self.server.register_remote_method("notify", self.notify_action) @@ -96,6 +100,45 @@ class Notifier: "job_state:resumed", config) + def register_endpoints(self, config: ConfigHelper): + self.server.register_endpoint( + "/server/notifiers/list", ["GET"], self._handle_notifier_list + ) + self.server.register_debug_endpoint( + "/debug/notifiers/test", ["POST"], self._handle_notifier_test + ) + + async def _handle_notifier_list( + self, web_request: WebRequest + ) -> Dict[str, Any]: + return {"notifiers": self._list_notifiers()} + + def _list_notifiers(self) -> List[Dict[str, Any]]: + return [notifier.as_dict() for notifier in self.notifiers.values()] + + async def _handle_notifier_test( + self, web_request: WebRequest + ) -> Dict[str, Any]: + + name = web_request.get_str("name") + if name not in self.notifiers: + raise self.server.error(f"Notifier '{name}' not found", 404) + client: HttpClient = self.server.lookup_component("http_client") + notifier = self.notifiers[name] + + kapis: APIComp = self.server.lookup_component('klippy_apis') + result: Dict[str, Any] = await kapis.query_objects( + {'print_stats': None}, default={}) + print_stats = result.get('print_stats', {}) + print_stats["filename"] = "notifier_test.gcode" # Mock the filename + + await notifier.notify(notifier.events[0], [print_stats, print_stats]) + + return { + "status": "success", + "stats": print_stats + } + class NotifierEvent: def __init__(self, identifier: str, event_name: str, config: ConfigHelper): @@ -156,9 +199,20 @@ class NotifierInstance: self.apprise.add(self.url) + def as_dict(self): + return { + "name": self.name, + "url": self.config.get("url"), + "title": self.config.get("title", None), + "body": self.config.get("body", None), + "events": self.events, + "attach": self.attach + } + async def notify( self, event_name: str, event_args: List, message: str = "" ) -> None: + context = { "event_name": event_name, "event_args": event_args,