2025-06-29 03:02:02 +00:00
|
|
|
# Custom Plugins
|
2025-05-30 22:50:31 +00:00
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
With the aMQTT plugins framework, one can add additional functionality to the client or broker without
|
2025-07-10 16:29:59 +00:00
|
|
|
having to rewrite any of the core logic. Plugins can receive broker or client events [events](custom_plugins.md#events),
|
|
|
|
used for [client authentication](custom_plugins.md#authentication-plugins) and controlling [topic access](custom_plugins.md#topic-filter-plugins).
|
|
|
|
|
|
|
|
## Overview
|
2025-05-30 22:50:31 +00:00
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
To create a custom plugin, subclass from `BasePlugin` (client or broker) or `BaseAuthPlugin` (broker only)
|
|
|
|
or `BaseTopicPlugin` (broker only). Each custom plugin may define settings specific to itself by creating
|
2025-07-10 16:29:59 +00:00
|
|
|
a nested (ie. inner) `dataclass` named `Config` which declares each option and a default value (if applicable). A
|
|
|
|
plugin's configuration dataclass will be type-checked and made available from within the `self.config` instance variable.
|
2025-05-30 22:50:31 +00:00
|
|
|
|
|
|
|
```python
|
2025-06-29 03:02:02 +00:00
|
|
|
from dataclasses import dataclass, field
|
|
|
|
from amqtt.plugins.base import BasePlugin
|
|
|
|
from amqtt.contexts import BaseContext
|
|
|
|
|
|
|
|
|
|
|
|
class OneClassName(BasePlugin[BaseContext]):
|
|
|
|
"""This is a plugin with no functionality"""
|
|
|
|
|
|
|
|
|
|
|
|
class TwoClassName(BasePlugin[BaseContext]):
|
|
|
|
"""This is a plugin with configuration options."""
|
|
|
|
def __init__(self, context: BaseContext):
|
|
|
|
super().__init__(context)
|
2025-07-10 16:29:59 +00:00
|
|
|
self.my_option_one: str = self.config.option1
|
|
|
|
|
|
|
|
async def on_broker_pre_start(self) -> None:
|
|
|
|
print(f"On broker pre-start, my option1 is: {self.my_option_one}")
|
2025-06-29 03:02:02 +00:00
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Config:
|
|
|
|
option1: int
|
|
|
|
option3: str = field(default="my_default_value")
|
|
|
|
```
|
2025-05-30 22:50:31 +00:00
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
This plugin class then should be added to the configuration file of the broker or client (or to the `config`
|
2025-07-10 16:29:59 +00:00
|
|
|
dictionary passed to the `Broker` or `MQTTClient`), such as `myBroker.yaml`:
|
2025-05-30 22:50:31 +00:00
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
```yaml
|
2025-07-10 16:29:59 +00:00
|
|
|
---
|
|
|
|
listeners:
|
|
|
|
default:
|
|
|
|
type: tcp
|
|
|
|
bind: 0.0.0.0:1883
|
2025-06-29 03:02:02 +00:00
|
|
|
plugins:
|
2025-07-04 20:05:55 +00:00
|
|
|
module.submodule.file.OneClassName:
|
|
|
|
module.submodule.file.TwoClassName:
|
|
|
|
option1: 123
|
2025-05-30 22:50:31 +00:00
|
|
|
```
|
|
|
|
|
2025-07-10 16:29:59 +00:00
|
|
|
and then run via `amqtt -c myBroker.yaml`.
|
|
|
|
|
|
|
|
??? note "Example: custom plugin within broker script"
|
|
|
|
The example `samples/broker_custom_plugin.py` demonstrates how to load a custom plugin
|
|
|
|
by passing a config dictionary when instantiating a `Broker`. While this example is functional,
|
|
|
|
`samples` is an invalid python module (it does not have a `__init__.py`); it is recommended
|
|
|
|
that custom plugins are placed in a python module.
|
|
|
|
|
|
|
|
```python
|
|
|
|
--8<-- "samples/broker_custom_plugin.py"
|
|
|
|
```
|
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
??? warning "Deprecated: activating plugins using `EntryPoints`"
|
|
|
|
With the aMQTT plugins framework, one can add additional functionality to the client or broker without
|
|
|
|
having to rewrite any of the core logic. To define a custom list of plugins to be loaded, add this section
|
|
|
|
to your `pyproject.toml`"
|
|
|
|
|
|
|
|
```toml
|
|
|
|
[project.entry-points."mypackage.mymodule.plugins"]
|
|
|
|
plugin_alias = "module.submodule.file:ClassName"
|
|
|
|
```
|
|
|
|
|
|
|
|
Each plugin has access to the full configuration file through the provided `BaseContext` and can define its own
|
|
|
|
variables to configure its behavior.
|
2025-05-30 22:50:31 +00:00
|
|
|
|
2025-06-01 15:03:20 +00:00
|
|
|
::: amqtt.plugins.base.BasePlugin
|
2025-05-30 22:50:31 +00:00
|
|
|
|
2025-07-10 16:29:59 +00:00
|
|
|
|
|
|
|
|
2025-06-13 11:11:49 +00:00
|
|
|
## Events
|
|
|
|
|
2025-07-10 16:29:59 +00:00
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
All plugins are notified of events if the `BasePlugin` subclass implements one or more of these methods:
|
2025-05-22 22:24:51 +00:00
|
|
|
|
2025-06-28 03:08:09 +00:00
|
|
|
### Client and Broker
|
2025-05-22 22:24:51 +00:00
|
|
|
|
2025-06-29 21:04:55 +00:00
|
|
|
- `async def on_mqtt_packet_sent(self, *, packet: MQTTPacket[MQTTVariableHeader, MQTTPayload[MQTTVariableHeader], MQTTFixedHeader], session: Session | None = None) -> None`
|
|
|
|
- `async def on_mqtt_packet_received(self, *, packet: MQTTPacket[MQTTVariableHeader, MQTTPayload[MQTTVariableHeader], MQTTFixedHeader], session: Session | None = None) -> None`
|
2025-06-15 22:34:50 +00:00
|
|
|
|
2025-06-28 03:08:09 +00:00
|
|
|
### Client Only
|
|
|
|
|
|
|
|
none
|
|
|
|
|
|
|
|
### Broker Only
|
|
|
|
|
2025-06-29 21:04:55 +00:00
|
|
|
- `async def on_broker_pre_start(self) -> None`
|
|
|
|
- `async def on_broker_post_start(self) -> None`
|
|
|
|
- `async def on_broker_pre_shutdown(self) -> None`
|
|
|
|
- `async def on_broker_post_shutdown(self) -> None`
|
2025-06-15 22:34:50 +00:00
|
|
|
|
2025-06-29 21:47:02 +00:00
|
|
|
- `async def on_broker_client_connected(self, *, client_id:str, client_session:Session) -> None`
|
|
|
|
- `async def on_broker_client_disconnected(self, *, client_id:str, client_session:Session) -> None`
|
2025-06-15 22:34:50 +00:00
|
|
|
|
2025-06-29 21:04:55 +00:00
|
|
|
- `async def on_broker_client_connected(self, *, client_id:str) -> None`
|
|
|
|
- `async def on_broker_client_disconnected(self, *, client_id:str) -> None`
|
2025-06-15 22:34:50 +00:00
|
|
|
|
2025-06-29 21:04:55 +00:00
|
|
|
- `async def on_broker_client_subscribed(self, *, client_id: str, topic: str, qos: int) -> None`
|
|
|
|
- `async def on_broker_client_unsubscribed(self, *, client_id: str, topic: str) -> None`
|
|
|
|
|
|
|
|
- `async def on_broker_message_received(self, *, client_id: str, message: ApplicationMessage) -> None`
|
2025-05-22 22:24:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
## Authentication Plugins
|
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
In addition to receiving any of the event callbacks, a plugin which subclasses from `BaseAuthPlugin`
|
|
|
|
is used by the aMQTT `Broker` to determine if a connection from a client is allowed by
|
|
|
|
implementing the `authenticate` method and returning `True` if the session is allowed or `False` otherwise.
|
2025-05-22 22:24:51 +00:00
|
|
|
|
2025-06-17 16:53:37 +00:00
|
|
|
::: amqtt.plugins.base.BaseAuthPlugin
|
2025-05-22 22:24:51 +00:00
|
|
|
|
|
|
|
## Topic Filter Plugins
|
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
In addition to receiving any of the event callbacks, a plugin which is subclassed from `BaseTopicPlugin`
|
|
|
|
is used by the aMQTT `Broker` to determine if a connected client can send (PUBLISH) or receive (SUBSCRIBE)
|
|
|
|
messages to a particular topic by implementing the `topic_filtering` method and returning `True` if allowed or
|
|
|
|
`False` otherwise.
|
2025-05-22 22:24:51 +00:00
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
::: amqtt.plugins.base.BaseTopicPlugin
|
2025-05-22 22:24:51 +00:00
|
|
|
|
|
|
|
|
2025-06-29 03:02:02 +00:00
|
|
|
!!! note
|
|
|
|
A custom plugin class can subclass from both `BaseAuthPlugin` and `BaseTopicPlugin` as long it defines
|
|
|
|
both the `authenticate` and `topic_filtering` method.
|