Notification plugin
You may be interested in getting a notification when certain printer events happen, such as when a print is done, or a possible print failure is detected. But different people have their own preferred way to get notified: Email, Mobile Push Notification, Telegram, Discord, etc.
This is why we created the notification plugin structure in the Obico Server. It allows these events to be sent to almost any channel you prefer. The Obico Server is shipped with quite a few built-in notification channels. But if your preferred channel is not there, you can always build one.
Following this tutorial is the quickest way to have a new notification plugin up and running in your self-hosted Obico Server.
The structure of a notification plugin
A notification plugin consists of 3 parts. 2 are required and 1 optional.
- The backend (required). The backend is written in python. The Obico Server will call the backend when a printer event happens. The backend will then send a notification for this event, or simply ignore it.
- The frontend for the plugin preference page (required). The frontend is written in Vue.js. It will become a section on the user preference page. The frontend is responsible for taking user's input. For instance, a Telegram plugin will need the user to authorize it to send notifications to the user's telegram account. Hence a Telegram plugin's frontend will need to show a "Link Telegram" button to the user.
- The environment variables (optional). Some notification plugins will need some server-side configuration. For instance, a Pushover plugin will need an Pushover App Token in order to send a notification through the Pushover server. These configurations are done via the environment variables. Some notification plugins, such as the Discord plugin, don't need any server-side configurations. Hence this part is optional.
A notification plugin should also have a unique ID so that the Obico Server can tell it apart from other plugins. In this document, this ID will be represented as \{plugin_id\}
.
The plugin backend
Directory structure
All plugin backend files should be located in the folder backend/notifications/plugins/\{plugin_id\}
.
backend/notifications/plugins/\{plugin_id\}/__init__.py
Required. This is the entry point for the plugin backend.
Other python files in backend/notifications/plugins/\{plugin_id\}/
Optional. If present, they must be imported in the __init__.py
. Otherwise they won't be loaded successfully.
Class BaseNotificationPlugin
The base class from which the plugin backend needs to extend from.
Example:
from notifications.plugin import BaseNotificationPlugin
class PushOverNotificationPlugin(BaseNotificationPlugin):
...
BaseNotificationPlugin
defines the following methods that can be overridden by the plugin class. Most of them have a reasonable default and hence they are not optional in the plugin class.
validate_config
Validate the form data submitted by the user from the plugin's preference page.
This method is optional if your plugin doesn't need any configuration. This is rare.
Signature
def validate_config(self, data: Dict) -> Dict:
Parameters
data
: ADict
that contains the form data submitted by the user from the plugin's preference page. For instance, the Pushover plugin's preference page asks the user for a user_key. In this case,data
will look like{'user_key': 'xxx-xxxx-xxxxx'}
.
Return value
- A
Dict
: The form data that has been cleaned up. For instance, you may want to trim the leading/trailing white spaces. The return value will be saved to the database. It will be retrieved from the database and passed to the plugin when a notification needs to be sent.
The Obico Server has already sanitized the form data against common attacks before passing it to this method.
Exceptions
rest_framework.serializers.ValidationError
: Throw this exception ifdata
failed in validation.
env_vars
The method that tells the Obico Server what environment variables this plugin needs.
This method is optional if your plugin doesn't require any environment variables.
Signature
def env_vars(self) -> Dict:
Parameters
- None.
Return value
- A
Dict
. This return value will also be passed to the plugin frontend in case the frontend needs some of them on the preference page.
Example:
def env_vars(self) -> Dict:
return \{
'SLACK_CLIENT_ID': \{
'is_required': True,
'is_set': 'SLACK_CLIENT_ID' in os.environ,
'value': os.environ.get('SLACK_CLIENT_ID'),
\},
'SLACK_CLIENT_SECRET': \{
'is_required': True,
'is_set': 'SLACK_CLIENT_SECRET' in os.environ,
\},
\}
Never return the value of an environment variable that is supposed to be kept secret, such as SLACK_CLIENT_SECRET
. Whatever returned from this call will be exposed to the app users.
Exceptions
- None.
supported_features
The method that tells the Obico Server what features this plugin supports.
This method is optional if your plugin supports all features.
Signature
def supported_features(self) -> Set[Feature]:
Parameters
- None.
Return value
- A
Set
ofFeature
s.
Exceptions
- None.
send_failure_alert
The method the Obico Server will call when a possible failure is detected.
This method is optional if your plugin doesn't support the Feature.notify_on_failure_alert
feature.
Signature
def send_failure_alert(self, context: FailureAlertContext) -> None:
Parameters
context
: AFailureAlertContext
that contains the data for the detected failure.
Return value
- None.
Exceptions
- None.
send_printer_notification
The method the Obico Server will call when a printer notification needs to be sent, in general but not always when a printer event happens.
This method is optional if your plugin doesn't support any of the following features.
- notify_on_print_done
- notify_on_print_cancelled
- notify_on_filament_change
- notify_on_heater_status
- notify_on_print_start
- notify_on_print_pause
- notify_on_print_resume
Signature
def send_printer_notification(self, context: PrinterNotificationContext) -> None:
Parameters
context
: APrinterNotificationContext
that contains the data for the notification
Return value
- None.
Exceptions
- None.
send_test_message
The method the Obico Server will call when the user press the "Test notification" button on the plugin's preference page.
Signature
def send_test_message(self, context: TestMessageContext) -> None:
Parameters
context
: ATestMessageContext
that contains the data for the test notification.
Return value
- None.
Exceptions
- None.
Class FailureAlertContext
Properties
config
:Dict
. The same as what was previously returned fromvalidate_config
and saved in the database.user
:UserContext
.printer
:PrinterContext
.print
:PrintContext
.extra_context
:Dict
. Reserved for internal use.img_url
:str
. The url for the webcam image. If no webcam image is available, this will be an empty string (notNone
).is_warning
:bool
. If the detected failure is a "warning".print_paused
:bool
. If the print was paused as the result of the detected failure.
Class PrinterNotificationContext
Properties
config
:Dict
. The same as what was previously returned fromvalidate_config
and saved in the database.user
:UserContext
.printer
:PrinterContext
.print
:PrintContext
.extra_context
:Dict
. Reserved for internal use.img_url
:str
. The url for the webcam image. If no webcam image is available, this will be an empty string (notNone
).feature
:Feature
.notification_type
:str
. The type of this notification.