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.