Plugins¶
Plugins are included using stevedore This means that any package installed with setup.py can be turned into a plugin.
Creating A Plugin¶
To create a simple plugin, you first need two files:
- setup.py
- plugin_name/__init__.py
setup.py holds the instructions on what your plugin needs to work, as well as where entrypoints live.
For our simple plugin we will use the following code
import os
from setuptools import setup, find_packages
setup(
name="SimplePlugin",
version='0.0.1',
entry_points={
'blueweather.plugins.plugin': [
# The left side of the equals sign is the internal name of our
# plugin, it can't contain spaces, and must be unique. It does
# not have to be pretty.
'simplePlugin = simplePlugin:SimplePlugin'
],
'blueweather.plugins.startup': [
# The right side of the equals sign is the location of the class.
# The format is module.names:ClassName
'simplePlugin = simplePlugin:SimplePlugin'
]
}
)
The most import part of defining your plugin is its entrypoints. In the setup function, the parameter entry_points tells BlueWeather what type of plugins you are creating, as well as where its code lives.
For this example, we have defined two extensions:
- blueweather.plugins.plugin
- blueweather.plugins.startup
The Plugin extension gives usefull
information about the plugin, and is required for all plugins. The
Startup extension requests a message
for when the server is up and running.
Next we will create the file simplePlugin/__init__.py
from blueweather.plugin import base
class SimplePlugin(base.Plugin, base.Startup):
def get_plugin_name(self):
return "Simple Plugin"
def get_plugin_description(self):
return "This is a simple plugin that prints 'Everything works!' when the server starts up"
def on_startup(self):
print("Everything works!")
This is where the code for the plugin lives. We have defined two required
functions for the Plugin extension (get_plugin_name(),
and get_plugin_description())
This simply gives information about our plugin to the user. Then from the Startup
extension, we implement on_startup() where we print to the console that everything
is working.
This is an exceedingly simple plugin, but it should be enough to get you started in creating your own plugins.
API Reference¶
Plugin |
The base plugin configuration. |
Startup |
Gets a message when the server is up and running |
API |
Allows for custom ReST APIs |
Settings() |
Be able to interact with the settings for the plugin |
Weather |
Plugin that Collects the weather |
UnitConversion |
An Extension that can facilitate conversions |
blueweather.plugins.hooks.get_hook(name) |
Create or retrieve a hook |
blueweather.plugins.hooks.Hook(name, signature) |
Holds all the functions that are subscribed to this hook. |
Plugin¶
-
class
blueweather.plugins.base.Plugin¶ The base plugin configuration.
All Plugins must implement this extension
It gives usefull information to the user about the plugin
Get the Author(s) of the plugin
Returns: authors
-
get_plugin_description() → str¶ Get the description for the plugin
Returns: plugin description
-
get_plugin_name() → str¶ Get the human readable name of the plugin
Returns: plugin name
-
get_plugin_url() → str¶ Get the plugin’s url
Returns: url
Startup¶
API¶
-
class
blueweather.plugins.base.API¶ Allows for custom ReST APIs
-
get_api_urlpatterns() → List[Tuple[str, callable, str]]¶ get a list of url patterns for the api
the patterns should use django.urls patterns such as path
from . import views return [ ("data/", views.data, "data") ]
In each path, there are 3 parameters:
- endpoint (the url path) “data/” -> api/pluginName/data
- view (the function that can process the path)
- name (the name of the path) “data” -> api:pluginName:data
The namespace of the urls will be api:<pluginName>, so the name of a path will be api:<pluginName>:<pathName> (api:simplePlugin:data)
Returns: list of paths
-
Settings¶
-
class
blueweather.plugins.base.Settings¶ Be able to interact with the settings for the plugin
-
on_settings_initialized()¶ Called after the settings have been initialized
After this has been called, the settings are garenteed to be available in self._settings as the deserialized object given by settings_deserialize()
-
settings_deserialize(data: dict) → object¶ Deserialize the settings from a dictionary into a custom settings object.
By default no changes are made, so the default settings object is a dict of primitives.
I recommend using Marshmallow to deserialize your settings.
Parameters: data – settings dict Returns: deserialized settings object
-
settings_migrate(version: int, settings: dict) → Tuple[int, dict, bool]¶ Migrate the settings from an older version to the current version
Note
This function is always run, it is your job to determine if a migration is required.
Parameters: - version – the version of the settings
- settings – the primative loaded settings
Returns: version, updated settings, and whether the data has been changed
-
settings_serialize(obj) → dict¶ Serialize the settings object into a json serializeable dict.
This must undo whatever settings_deserialize does
Parameters: obj – settings object Returns: settings primitives
-
Weather¶
-
class
blueweather.plugins.base.Weather¶ Plugin that Collects the weather
-
on_weather_request() → Dict[str, Tuple[str, float]]¶ Collect the current weather
This should be returned as a dictionary of tuples. Each tuple contains the unit and value in that order:
{ "temperature": ("c", 25.3), "wind_speed": ("m/s", 5.2) }
While a unit does not need to be a standardized unit, it is important that it is a unit accessable to conversions through the UnitConversion extension. If you choose to use your own custom units, it is important that you make those units convertable by supplying your own conversion extension.
The following list contains units that are garanteed to be convertable
- temperature: c
- distance: m
- mass: kg
- time: s
- speed: m/s
- acceleration: m/s/s
- force: N
- pressure: Pa
- energy: J
- power: W
- current: A
- luminous: cd
Note
units are case-sensitive
Returns: weather data
-
UnitConversion¶
-
class
blueweather.plugins.base.UnitConversion¶ An Extension that can facilitate conversions
This allows for custom conversion between units.
-
conversion_request(data: float, from_type: str, to_type: str) → float¶ Convert from one type to another.
When converting values, the first successful conversion will stop
Parameters: - data – value in the unit (from_type)
- from_type – type to convert from
- to_type – type to convert to
Returns: converted value (if the conversion is not available, return None)
-
get_conversion_types() → List[Tuple[str, str]]¶ Get a list of available conversions in the form of tuples
Each conversion is in the form: from_type -> to_type
Returns: list of conversions
-
Hooks¶
Hooks are ways that plugins can interact with eachother. They should not be confused with stevedore’s hooks though.
A hook is a list of functions that can be called. At any point in time, anyone can create or subscribe to a hook. When the hook is called, all subscribed functions will be called.
get_hook¶
-
blueweather.plugins.hooks.get_hook(name) → blueweather.plugins.hooks.Hook¶ Create or retrieve a hook
With hooks, you can interact with other plugins in one of two ways
- Send a message to other subscribed plugins
- Subscribe to a hook that anyone can call.
Names should always be unique, so to prevent the possibility of clashes, the name of the plugin should be used as part of the hook name, such as “simplePlugin_hookName”
Returns: hook
Hook¶
-
class
blueweather.plugins.hooks.Hook(name: str, signature: inspect.Signature)¶ Holds all the functions that are subscribed to this hook.
-
call(*args, **kwargs)¶ Call all the hooked functions without retreiving the return value
Parameters: - args – positional arguments
- kwargs – keyword arguments
-
name¶ Name of the hook
-
request(*args, **kwargs) → list¶ Call all the hooked functions, and return a list of returned values
Parameters: - args – positional arguments
- kwargs – keyword arguments
Returns: list of return values
-
subscribe(name: str, func: callable)¶ Subscribe to the hook
When the hook is called, the function will be called
Note
The name should be unique, so to help prevent collisions, add/use the plugin name (simplePlugin)
Parameters: name – name of the subscriber Parm func: function to subscribe
-
unsubscribe(name: str)¶ Unsubscribe from the hook
Parameters: name – name of the subscriber
-