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.
info
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
{#backendnotificationspluginsplugin_idinitpy}​
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.
info
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,
},
}
danger
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.
Class UserContext
​
Properties​
id
:int
email
:str
first_name
:str
last_name
:str
unsub_token
:str
dh_balance
:float
is_pro
:bool
Class PrinterContext
​
Properties​
id
:int
name
:str
pause_on_failure
:bool
watching_enabled
:bool
Class PrintContext
​
Properties​
id
:int
filename
:str
started_at
:Optional[datetime.datetime]
ended_at
:Optional[datetime.datetime]
alerted_at
:Optional[datetime.datetime]
alert_overwrite
:str
Class TestMessageContext
​
Properties​
config
:Dict
user
:UserContext
extra_context
:Dict
Class Feature
​
An Enum that tells what feature(s) a plugin supports.
When a feature is declared supported by a plugin, a toggle will be shown on its preference page so that the user can toggle on/off that feature. Also the Obico Server will try to call a corresponding method of that plugin when there is a notification classified under that feature.
List of Feature
s​
notify_on_failure_alert
notify_on_print_done
notify_on_print_cancelled
notify_on_filament_change
notify_on_print_start
notify_on_print_pause
notify_on_print_resume
notify_on_heater_status
module notification_types
​
Example:
from notifications import notification_types
...
list of notification_types
​
PrintStarted
PrintDone
PrintCancelled
PrintPaused
PrintResumed
FilamentChange
HeaterCooledDown
HeaterTargetReached
The plugin frontend​
Directory structure​
All plugin frontend files should be located in the folder frontend/src/notifications/
.
backend/notifications/plugins/{plugin_id}.vue
​
Required. The Vue component that will be shown to the user. It should contain the following:
- A form input to let the user enter the config data necessary to receive a notification. For instance, this is how the Pushover plugin lets the user enter a user_key:
...
<notification-channel-template
:errorMessages="errorMessages"
:saving="saving"
:notificationChannel="notificationChannel"
configVariableTitle="User Key"
configVariablePlaceholder="Pushover User Key"
configVariableName="user_key"
@createNotificationChannel="(channel, config) => $emit('createNotificationChannel', channel, config)"
@updateNotificationChannel="(channel, changedProps) => $emit('updateNotificationChannel', channel, changedProps)"
@deleteNotificationChannel="(channel) => $emit('deleteNotificationChannel', channel)"
@clearErrorMessages="(settingKey) => $emit('clearErrorMessages', settingKey)"
>
...
- A paragraph to explain to the user about how to set up to receive the notification, such as where to download the app, how to install it, and how to obtain the config data (if necessary). For instance:
...
<small class="form-text text-muted">
If you have a Pushover account, you can
<a href="https://support.pushover.net/i7-what-is-pushover-and-how-do-i-use-it" target="_blank">get your User Key</a>
and enter it here.
</small>
...
caution
The {plugin_id}
should match the value used in the plugin backend. Otherwise the plugin's preference page can't be displayed correctly.
Add a section to frontend/src/notifications/plugins.js
​
{plugin_id}: {
displayName: 'Plugin name',
},
caution
The {plugin_id}
should match the value used in the plugin backend. Otherwise the plugin's preference page can't be displayed correctly.
The environment variables​
Skip this part if your plugin doesn't need any new environment variables.
Set up environment variables​
Follow this guide to add new environment variables to the self-hosted Obico Server.
Declare the environment variable requirements in the plugin​
Add env_vars
method to your plugin to declare the environment variable requirements.
Compile the plugin and load it in your Obico Server​
Every time you make a change to the plugin frontend, you need to re-compile the plugin and restart your server to test the change.
cd frontend
yarn
yarn build
cd ..
docker-compose restart
Contribute your plugin back to the Obico project​
info
This step is completely optional. You won't violate the Obico license if you just want to keep the plugin to yourself without contributing back.
Please read this contributor's guide for how you can contribute the plugin you developed back to the Obico project.