Plugin Development
CraftbeerPi4 has the capability to create a plugin template for the development of your own plugin. You just need to run the following command:
Craftbeerpi4 will ask you for a plugin name.
In this example, the plugin name testplugin
has been entered. CraftbeerPi4 will add a suffix cbpi4_
as this is required to identify the plugins during startup
The software will create a folder with a template for your plugin and configures it with cbpi4_testplugin
(from this example)
Plugin folder structure
CraftbeerPi4 is creating a folder with the name cbpi4_testplugin.
It is recommended to install the plugin for testing with the -e
option
pipx runpip cbpi4 install -e ./cbpi4_testplugin
Changes of your code will immediately take effect w/o re-installation of the plugin when you restart cbpi.
You should also stop the server as service and run it in manual mode with cbpi start
to see the log outputs or errors directly on the screen.
If you navigate into the folder, you will see the following structure:
The main folder contains the LICENSE file and a MANIFEST file where you do not need to change anything. The README.md file will be seen if you upload your plugin to a github repo. This file should be edited with the required information on how to install, configure and use your plugin.
The sub-folder cbpi4_testplugin
is the folder, where the plugin code is located.
Plugin setup.py file
The setup.py file contains some information for the plugin installation such as version number and other required packages. You need to edit this file accordingly. Additional information on how to use setuptools can be found here.
The setup.py file looks like this:
You should change the version number whenever you modify/update your plugin. You should also enter some information in the description (e.g. Sensor Plugin). As author you can enter your name and in the email your email if you want. The URL should be filled with the homepage of your plugin which is typically the url of your github repo.
You can also specify required python packages that will be installed during the installation of your plugin. In addition, it is recommended to andd a few lines that will help to display the README.md file also on the pypi.org page if you decide to create also a package for pypi.org that can be installed later just via:
Below is an example of a setup.py for a plugin, which is also available on the pypi.org page and can be installed directly from there.
One section has been added to read the content of the readme file and provided it at the end of the setup.py as 'long_description'. There is also a parameter 'install_requires' which can be filled with additional packages that are required for the plugin. This plugin requires for instance in addition to cbpi the packages 'smbus2' and 'scd30_i2c'.
Creation of plugin package and upload to pypi.org
Once you have written your plugin and uploaded it to your github repo, you can also create a package on the pypi.org page. Therefore, you need to create an acoount on pypi.org and install the twine package.
In the main folder of your plugin you need to run the following command to create a distribution package:
Once the package is created, you can upload it with twine:
Plugin code folder
If you change into the sub folder of your main plugin directory, you can edit the plugin code. The code folder structure looks like this:
The config.yaml is created automatically and should not be changed. The static folder is typically not required and can be removed in most of the cases. The __init__.py
file contains the code of your plugin.
Plugin Types
CraftbeerPi4 provides flexibility as Plugins can be written for different purposes. The following plugin types are currently supported:
Type | Description | Example |
---|---|---|
Sensor | Allows addition of different sensor types besides onewire temp sensor | |
Actor | Allows addition of different actor types / hardware | |
Kettle Logic | Allows to create a kettle logic that fits for your system requirements | |
Fermenter Logic | Allows to create a fermenter logic that fits for your system requirements | No example yet Builtin FermenterHysteresis |
Mash Steps | Allows to add mash steps that users may require / adapt to their needs | |
Notifications | Allows users to define callback functions that forward cbpi notifications to other message services or a buzzer | |
Automated Recipe Creation | Allows users to adapt the automated recipe creation process (xml, kbh or brewfather) to their requirements. | |
Other Functions | Allows users to add other functionalities such as an LCD Display |
Plugin Classes
CraftbeerPi4 has defined different classes, that you need to create a Sensor, Actor, Logic,.....
The following table will describe show you the classes.
Class | Properties | Usage |
---|---|---|
CBPiSensor | Yes | Required for all type of sensors. For some functions the class CBPiExtension might be required in addition |
CBPiActor | Yes | Required for all type of actors. For some functions the class CBPiExtension might be required in addition |
CBPiKettleLogic | Yes | Required to create a new Kettle Logic type |
CBPiFermenterLogic | Yes | Required to create a new Fermenter Logic type |
CBPiStep | Yes | Required to create new MashSteps |
CBPiExtension | No | Required for various purposes such as addition of cbpi setting parameters, definition of http endpoints or startup of additional hardware |
Plugin Properties
If you want to add for instance a new onewire sensor, you need to select the sensor id, define a name, an Interval and an offset, if required. This is done via so called properties.
The Sensor Name and Sensor Type will be always required, even if you don't add properties to your plugin. However, the other properties need to be added via @parameters right in front of your class. Properties can be added to all plugins except for the CBPiExtension.
If you want to add multiple properties, you need to separate them with a ,
. If you don't want to add properties for your sensor leave the @ parameters empty:
The following properties are available:
Property | Description |
---|---|
Property.Select | The user can select some pre-defined options |
Property.Number | The user can enter a number (e.g. offset value for a sensor) |
Property.Text | The user can enter a string / text |
Property.Sensor | The user can select a sensor that should be used for the plugin |
Property.Actor | The user can select an actor that should be used in the plugin (e.g. in a step) |
Property.Kettle | The user can select a kettle that should be used in the plugin (e.g. in a step) |
Property.Fermenter | The user can select a fermenter that should be used in the plugin |
Some properties will be used as default for the different plugin classes.
A Kettle will for instance always require a kettle logic, a sensor, a heater (actor) and an agitator (actor).
A Fermenter will always require a logic, a heater, a cooler, a sensor, a brew name and a target temp.
Only the properties you want to use in addition have to be specified in the @parameters.
Examples for Property types
Although you can specify a default_value inside the property, it is currently not used. You need specify the default value in the self.props.get
function (see below).
Property.Select
Property.Select("Type", options=["Temperature", "Gravity/Angle", "Battery", "RSSI"], description="Select which type of data to register for this sensor. For Angle, Polynomial has to be left empty")
Property.Number
Property.Number(label="offset",configurable = True, default_value = 0, description="Sensor Offset (Default is 0)")
Property.Text
Property.Text(label="iSpindle", configurable=True, description="Enter the name of your iSpindel")
Property.Sensor
Property.Sensor("FermenterTemp",description="Select Fermenter Temp Sensor that you want to provide to TCP Server")
Property.Actor
Property.Actor(label="Actor", description="Actor can trigger a valve for the cooldown to target temperature")
Property.Kettle
Property.Kettle(label="Kettle")
Property.Fermenter
Property.Fermenter(label="Fermenter")
You can define the label content to your requirements. Labels are used to access the property in the plugin.
If you have for instance specified a parameter with the label ´offset´ in your parameters:
Property.Number(label="offset",configurable = True, default_value = 0, description="Sensor Offset (Default is 0)")
You can retrieve this parameter for the particular instance of your plugin via:
self.offset = float(self.props.get("offset",0))
The variable self.offset
will be set to the offset parameter, if defined. Otherwise it will be 0 as default.
More details will be shown in a few examples at a later point of time.
Plugin registration
Every Plugin that you create needs t be registered during cbpi startup. This requires a few lines of code at the end of each plugin. If you create for instance one plugin that requires for instance a CBPiExtension class to check if setting parameters are available or have been added to the cbpi config or needs to start sensor hardware, you need to register this extension as well, as the sensor plugin itself.
One example is the cbpi4-scd30-co2-sensor. Before the sensor can be started / used, the hardware needs to be initialized which is done with the CBPiExtension class:
While SCD30_Config is initializing, it creates a task self.init_sensor()
which is initializing the hardware and starts a routine ReadSensor
that is reading the sensor with the frequency defined in the plugin settings. This routine is writing data into a cache including a timestamp.
The sensor module itself is just reading the cache and if data with a new timestamp is available, it will update the sensor value and pushes it to the user interface and mqtt and logs the data.
When initializing the module with super
you need to pay attention that you also adapt the name for your module. In this case it is SCD30Sensor
-> super(SCD30Sensor, self).__init__(cbpi, id, props)
As mentioned, you need to register both Plugin classes to run the sensor plugin as the sensor will require the CBPiExtension to retrieve data and this needs to be started during cbpi startup. The registration is done at the end of the plugin with the setup function:
Here, both plugin classes will be registered during cbpi startup.
The registration requires two strings. The first is the name or label you will see in the software when you select the Sensor. In this case it is for instance 'SCD30 Sensor'. It should be a unique name for your plugin. The second one has to match the class you have defined for your module. In this example is is SCD30Sensor
.
You can also put multiple 'modules' into one plugin. Below is a step plugin shown as example. In this case, various steps have been put together.
Each of the steps need to be registered individually:
This comes in handy as you just need to create one plugin with multiple modules. It is just important that you register every module.
Adding cbpi settings from plugins
Some plugins will require global cbpi settings. You can add them during the startup of your plugin. The plugin can check if the setting is available. If not, the plugin can add the global setting to cbpi. You will need to add the class CBPiExtension to your plugin and handle that task within this class.
Don't forget to register your extension separately from your plugin as described above
The example below shows an example, where the plugin starts with the CBPiExtension class to add an interval to the global cbpi settings. In this case, a task will be created the initializes the senor. During the initialization, the task will run the function scd30_interval. This function could be also directly included into the initialization task / function.
The function is checking, if the parameter scd30_interval
is already existing in the global cbpi settings. If not, the function self.cbpi.config.get("scd30_intervall", None)
will return None
.
In this case, the function will add this parameter with the function self.cbpi.config.add(....)
.
The first parameter in this function will define the parameter name
"scd30_interval"
.The second parameter will define the initial value
The third parameter will define the type of the parameter (They are like the plugin property types)
The next parameter specifies the explanation for this parameter that is shown on the settings page.
In case of
ConfigType.SELECT
you will also specify the options as another parameter.
You can see plenty of examples on how to add global cbpi settings in the ConfigUpdate
extension, that comes with cbpi. This can be found here.
Last updated