# Development

## Development Setup

Custom Plugins are shipped as standard Python packages. Just create a python virtual env and add `cbpi`as dependency

{% hint style="info" %}
How to create virtual env in Python\
<https://docs.python.org/3/tutorial/venv.html>
{% endhint %}

### 1. Create a virtual env with CBPi

```bash
python3 -m venv venv
source venv/bin/activate
python3 -m pip insatll cbpi
```

### 2. Create Plugin

```python
cbpi create <PLUGIN_NAME>
```

### 3. Add Custom Code

### 4. Install your plugin locally

```python
pip install ./<PLUGIN_NAME>
cbpi add <PLUGIN_NAME>
```

### 3. Build plugin

To build your package run the following command in the root folder of your plugin

```python
python3 setup.py sdist
```

### 4. Upload the plugin

#### Create a PyPi Account

PyPi is a Python package index. You can create an accout for free and upload your plugin to this platform.

<https://pypi.org>

#### Upload your package

```python
twine upload dist/*
```

{% hint style="info" %}
Make sure that you have intalled twine. `pip install twine`
{% endhint %}

### Full Example

<https://github.com/Manuel83/cbpi4-ui-plugin>

## &#x20; Sensor

```python
# -*- coding: utf-8 -*-
import asyncio
import random
import re
import random
from aiohttp import web
from cbpi.api import *

'''
Make sure to extend CBPiSensor
'''

@parameters([Property.Number(label="Param1", configurable=True), 
             Property.Text(label="Param2", configurable=True, default_value="HALLO"), 
             Property.Select(label="Param3", options=[1,2,4]), 
             Property.Sensor(label="Param4"), 
             Property.Actor(label="Param5")])
class CustomSensor(CBPiSensor):
    
    def __init__(self, cbpi, id, props):
    
        super(CustomSensor, self).__init__(cbpi, id, props)
        self.value = 0

    
    @action(key="Test", parameters=[])
    async def action1(self, **kwargs):
        '''
        A custom action. Which can be called from the user interface
        '''
        print("ACTION!", kwargs)

    async def run(self):
        '''
        This method is executed asynchronousely 
        In this example the code is executed every second
        '''
        while self.running is True:
            self.value = random.randint(0,50)
            self.push_update(self.value)
            await asyncio.sleep(1)
    
    def get_state(self):
        # return the current state of the sensor
        return dict(value=self.value)


def setup(cbpi):

    '''
    This method is called by the server during startup 
    Here you need to register your plugins at the server
    
    :param cbpi: the cbpi core 
    :return: 
    '''
    cbpi.plugin.register("CustomSensor", CustomSensor)
```

## Actor

```python

import logging
from unittest.mock import MagicMock, patch

from cbpi.api import *


logger = logging.getLogger(__name__)

try:
    import RPi.GPIO as GPIO
except Exception:
    logger.error("Failed to load RPi.GPIO. Using Mock")
    MockRPi = MagicMock()
    modules = {
        "RPi": MockRPi,
        "RPi.GPIO": MockRPi.GPIO
    }
    patcher = patch.dict("sys.modules", modules)
    patcher.start()
    import RPi.GPIO as GPIO


@parameters([Property.Number(label="Param1", configurable=True), 
             Property.Text(label="Param2", configurable=True, default_value="HALLO"), 
             Property.Select(label="Param3", options=[1,2,4]), 
             Property.Sensor(label="Param4"), 
             Property.Actor(label="Param5")])
class CustomActor(CBPiActor):
    my_name = ""

    # Custom property which can be configured by the user
    @action("test", parameters={})
    async def action1(self, **kwargs):
        print("ACTION !", kwargs)
        self.my_name = kwargs.get("name")
        pass
    
    def init(self):
        print("INIT")
        
        self.state = False
        pass

    async def on(self, power=0):
        logger.info("ACTOR 1111 %s ON" % self.id)
        self.state = True

    async def off(self):
        logger.info("ACTOR %s OFF " % self.id)
        self.state = False

    def get_state(self):
        
        return self.state
    
    async def run(self):
        pass

def setup(cbpi):

    '''
    This method is called by the server during startup 
    Here you need to register your plugins at the server
    
    :param cbpi: the cbpi core 
    :return: 
    '''

    cbpi.plugin.register("CustomActor", CustomActor)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://craftbeerpi.gitbook.io/craftbeerpi4/development.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
