mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-22 06:58:17 +01:00
Added Fermenters (development)
Added fermenter type Added fermenter logic (incl. new class) -> will require cbpi4ui -> >= 0.1.a1 Still under development, but fermentation w/o steps should be working
This commit is contained in:
parent
bee645ff96
commit
d7c1b64493
17 changed files with 651 additions and 29 deletions
|
@ -1 +1 @@
|
||||||
__version__ = "4.0.0.59"
|
__version__ = "4.0.1.a1"
|
||||||
|
|
|
@ -9,6 +9,7 @@ __all__ = ["CBPiActor",
|
||||||
"parameters",
|
"parameters",
|
||||||
"background_task",
|
"background_task",
|
||||||
"CBPiKettleLogic",
|
"CBPiKettleLogic",
|
||||||
|
"CBPiFermenterLogic",
|
||||||
"CBPiException",
|
"CBPiException",
|
||||||
"KettleException",
|
"KettleException",
|
||||||
"SensorException",
|
"SensorException",
|
||||||
|
@ -22,5 +23,6 @@ from cbpi.api.extension import *
|
||||||
from cbpi.api.property import *
|
from cbpi.api.property import *
|
||||||
from cbpi.api.decorator import *
|
from cbpi.api.decorator import *
|
||||||
from cbpi.api.kettle_logic import *
|
from cbpi.api.kettle_logic import *
|
||||||
|
from cbpi.api.fermenter_logic import *
|
||||||
from cbpi.api.step import *
|
from cbpi.api.step import *
|
||||||
from cbpi.api.exceptions import *
|
from cbpi.api.exceptions import *
|
|
@ -29,6 +29,15 @@ class CBPiBase(metaclass=ABCMeta):
|
||||||
async def set_target_temp(self,id, temp):
|
async def set_target_temp(self,id, temp):
|
||||||
await self.cbpi.kettle.set_target_temp(id, temp)
|
await self.cbpi.kettle.set_target_temp(id, temp)
|
||||||
|
|
||||||
|
def get_fermenter(self,id):
|
||||||
|
return self.cbpi.fermenter._find_by_id(id)
|
||||||
|
|
||||||
|
def get_fermenter_target_temp(self,id):
|
||||||
|
return self.cbpi.fermenter._find_by_id(id).target_temp
|
||||||
|
|
||||||
|
async def set_fermenter_target_temp(self,id, temp):
|
||||||
|
await self.cbpi.fermenter.set_target_temp(id, temp)
|
||||||
|
|
||||||
def get_sensor(self,id):
|
def get_sensor(self,id):
|
||||||
return self.cbpi.sensor.find_by_id(id)
|
return self.cbpi.sensor.find_by_id(id)
|
||||||
|
|
||||||
|
|
|
@ -8,5 +8,6 @@ class ConfigType(Enum):
|
||||||
ACTOR = "actor"
|
ACTOR = "actor"
|
||||||
SENSOR = "sensor"
|
SENSOR = "sensor"
|
||||||
STEP = "step"
|
STEP = "step"
|
||||||
|
FERMENTER = "fermenter"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Actor:
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "name={} props={}, state={}, type={}, power={}".format(self.name, self.props, self.state, self.type, self.power)
|
return "name={} props={}, state={}, type={}, power={}".format(self.name, self.props, self.state, self.type, self.power)
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state2="HELLO WORLD", state=self.instance.get_state(), power=self.power)
|
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state(), power=self.power)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -125,15 +125,30 @@ class Step:
|
||||||
class Fermenter:
|
class Fermenter:
|
||||||
id: str = None
|
id: str = None
|
||||||
name: str = None
|
name: str = None
|
||||||
|
sensor: Sensor = None
|
||||||
|
heater: Actor = None
|
||||||
|
cooler: Actor = None
|
||||||
brewname: str = None
|
brewname: str = None
|
||||||
props: Props = Props()
|
props: Props = Props()
|
||||||
target_temp: int = 0
|
target_temp: int = 0
|
||||||
|
type: str = None
|
||||||
steps: List[Step]= field(default_factory=list)
|
steps: List[Step]= field(default_factory=list)
|
||||||
|
instance: str = None
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "id={} name={} brewname={} props={} temp={} steps={}".format(self.id, self.name, self.brewname, self.props, self.target_temp, self.steps)
|
return "id={} name={} sensor={} heater={} cooler={} brewname={} props={} temp={} type={} steps={}".format(self.id, self.name, self.sensor, self.heater, self.cooler, self.brewname, self.props, self.target_temp, self.type, self.steps)
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
|
|
||||||
|
if self.instance is not None:
|
||||||
|
|
||||||
|
state = self.instance.state
|
||||||
|
|
||||||
|
else:
|
||||||
|
state = False
|
||||||
|
|
||||||
steps = list(map(lambda item: item.to_dict(), self.steps))
|
steps = list(map(lambda item: item.to_dict(), self.steps))
|
||||||
return dict(id=self.id, name=self.name, target_temp=self.target_temp, steps=steps, props=self.props.to_dict() if self.props is not None else None)
|
return dict(id=self.id, name=self.name, state=state, sensor=self.sensor, heater=self.heater, cooler=self.cooler, brewname=self.brewname, props=self.props.to_dict() if self.props is not None else None, target_temp=self.target_temp, type=self.type, steps=steps)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -162,6 +177,7 @@ class ConfigType(Enum):
|
||||||
NUMBER="number"
|
NUMBER="number"
|
||||||
SELECT="select"
|
SELECT="select"
|
||||||
STEP="step"
|
STEP="step"
|
||||||
|
FERMENTER="fermenter"
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Config:
|
class Config:
|
||||||
|
|
51
cbpi/api/fermenter_logic.py
Normal file
51
cbpi/api/fermenter_logic.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
from cbpi.api.base import CBPiBase
|
||||||
|
from cbpi.api.extension import CBPiExtension
|
||||||
|
from abc import ABCMeta
|
||||||
|
import logging
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CBPiFermenterLogic(CBPiBase, metaclass=ABCMeta):
|
||||||
|
|
||||||
|
def __init__(self, cbpi, id, props):
|
||||||
|
self.cbpi = cbpi
|
||||||
|
self.id = id
|
||||||
|
self.props = props
|
||||||
|
self.state = False
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def on_start(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def on_stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def _run(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.on_start()
|
||||||
|
self.cancel_reason = await self.run()
|
||||||
|
except asyncio.CancelledError as e:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
await self.on_stop()
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
return dict(running=self.state)
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
|
||||||
|
self.state = True
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
|
||||||
|
self.task.cancel()
|
||||||
|
await self.task
|
||||||
|
self.state = False
|
|
@ -109,3 +109,20 @@ class Property(object):
|
||||||
self.label = label
|
self.label = label
|
||||||
self.configurable = True
|
self.configurable = True
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
|
class Fermenter(PropertyType):
|
||||||
|
'''
|
||||||
|
The user select a fermenter which is available in the system. The value of this variable will be the fermenter id
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, label, description=""):
|
||||||
|
'''
|
||||||
|
|
||||||
|
:param label:
|
||||||
|
:param description:
|
||||||
|
'''
|
||||||
|
|
||||||
|
PropertyType.__init__(self)
|
||||||
|
self.label = label
|
||||||
|
self.configurable = True
|
||||||
|
self.description = description
|
|
@ -42,6 +42,11 @@ def create_config_file():
|
||||||
destfile = os.path.join(".", 'config')
|
destfile = os.path.join(".", 'config')
|
||||||
shutil.copy(srcfile, destfile)
|
shutil.copy(srcfile, destfile)
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(".", 'config', "fermenter_data.json")) is False:
|
||||||
|
srcfile = os.path.join(os.path.dirname(__file__), "config", "fermenter_data.json")
|
||||||
|
destfile = os.path.join(".", 'config')
|
||||||
|
shutil.copy(srcfile, destfile)
|
||||||
|
|
||||||
if os.path.exists(os.path.join(".", 'config', "step_data.json")) is False:
|
if os.path.exists(os.path.join(".", 'config', "step_data.json")) is False:
|
||||||
srcfile = os.path.join(os.path.dirname(__file__), "config", "step_data.json")
|
srcfile = os.path.join(os.path.dirname(__file__), "config", "step_data.json")
|
||||||
destfile = os.path.join(".", 'config')
|
destfile = os.path.join(".", 'config')
|
||||||
|
|
5
cbpi/config/fermenter_data.json
Normal file
5
cbpi/config/fermenter_data.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import cbpi
|
import cbpi
|
||||||
import copy
|
import copy
|
||||||
|
@ -9,19 +8,15 @@ from os import listdir
|
||||||
from os.path import isfile, join
|
from os.path import isfile, join
|
||||||
import shortuuid
|
import shortuuid
|
||||||
from cbpi.api.dataclasses import Fermenter, FermenterStep, Props, Step
|
from cbpi.api.dataclasses import Fermenter, FermenterStep, Props, Step
|
||||||
|
from cbpi.controller.basic_controller2 import BasicController
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
import sys, os
|
import sys, os
|
||||||
from ..api.step import CBPiStep, StepMove, StepResult, StepState
|
from ..api.step import CBPiStep, StepMove, StepResult, StepState
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(format='%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
|
|
||||||
datefmt='%Y-%m-%d:%H:%M:%S',
|
|
||||||
level=logging.INFO)
|
|
||||||
|
|
||||||
class FermentStep:
|
class FermentStep:
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, cbpi, step, on_done) -> None:
|
def __init__(self, cbpi, step, on_done) -> None:
|
||||||
self.cbpi = cbpi
|
self.cbpi = cbpi
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
@ -85,9 +80,10 @@ class FermentStep:
|
||||||
async def on_stop(self):
|
async def on_stop(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class FermenationController:
|
class FermentationController:
|
||||||
|
|
||||||
def __init__(self, cbpi):
|
def __init__(self, cbpi):
|
||||||
|
self.update_key = "fermenterupdate"
|
||||||
self.cbpi = cbpi
|
self.cbpi = cbpi
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.path = os.path.join(".", 'config', "fermenter_data.json")
|
self.path = os.path.join(".", 'config', "fermenter_data.json")
|
||||||
|
@ -96,6 +92,27 @@ class FermenationController:
|
||||||
self.types = {}
|
self.types = {}
|
||||||
self.cbpi.app.on_cleanup.append(self.shutdown)
|
self.cbpi.app.on_cleanup.append(self.shutdown)
|
||||||
|
|
||||||
|
async def init(self):
|
||||||
|
logging.info("INIT Fermentation Controller")
|
||||||
|
self.check_fermenter_file()
|
||||||
|
await self.load()
|
||||||
|
pass
|
||||||
|
|
||||||
|
def check_fermenter_file(self):
|
||||||
|
if os.path.exists(os.path.join(".", 'config', "fermenter_data.json")) is False:
|
||||||
|
logging.info("INIT fermenter_data.json file")
|
||||||
|
data = {
|
||||||
|
"data": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
destfile = os.path.join(".", 'config', "fermenter_data.json")
|
||||||
|
json.dump(data,open(destfile,'w'),indent=4, sort_keys=True)
|
||||||
|
|
||||||
|
def push_update(self):
|
||||||
|
self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
|
pass
|
||||||
|
|
||||||
async def shutdown(self, app=None):
|
async def shutdown(self, app=None):
|
||||||
self.save()
|
self.save()
|
||||||
for fermenter in self.data:
|
for fermenter in self.data:
|
||||||
|
@ -115,6 +132,10 @@ class FermenationController:
|
||||||
d = json.load(json_file)
|
d = json.load(json_file)
|
||||||
self.data = list(map(lambda item: self._create(item), d))
|
self.data = list(map(lambda item: self._create(item), d))
|
||||||
|
|
||||||
|
#for item in self.data:
|
||||||
|
# logging.info("{} Starting ".format(item.name))
|
||||||
|
# await self.start(item.id)
|
||||||
|
|
||||||
def _create_step(self, fermenter, item):
|
def _create_step(self, fermenter, item):
|
||||||
id = item.get("id")
|
id = item.get("id")
|
||||||
name = item.get("name")
|
name = item.get("name")
|
||||||
|
@ -140,20 +161,37 @@ class FermenationController:
|
||||||
def _create(self, data):
|
def _create(self, data):
|
||||||
id = data.get("id")
|
id = data.get("id")
|
||||||
name = data.get("name")
|
name = data.get("name")
|
||||||
|
sensor = data.get("sensor")
|
||||||
|
heater = data.get("heater")
|
||||||
|
cooler = data.get("cooler")
|
||||||
|
logictype = data.get("type")
|
||||||
|
temp = data.get("target_temp")
|
||||||
brewname = data.get("brewname")
|
brewname = data.get("brewname")
|
||||||
props = Props(data.get("props", {}))
|
props = Props(data.get("props", {}))
|
||||||
fermenter = Fermenter(id, name, brewname, props, 0)
|
fermenter = Fermenter(id, name, sensor, heater, cooler, brewname, props, temp, logictype)
|
||||||
fermenter.steps = list(map(lambda item: self._create_step(fermenter, item), data.get("steps", [])))
|
fermenter.steps = list(map(lambda item: self._create_step(fermenter, item), data.get("steps", [])))
|
||||||
|
self.push_update()
|
||||||
|
#self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
#self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
|
|
||||||
return fermenter
|
return fermenter
|
||||||
|
|
||||||
def _find_by_id(self, id):
|
def _find_by_id(self, id):
|
||||||
return next((item for item in self.data if item.id == id), None)
|
return next((item for item in self.data if item.id == id), None)
|
||||||
|
|
||||||
async def init(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def get_all(self):
|
async def get_all(self):
|
||||||
return self.data
|
return list(map(lambda x: x.to_dict(), self.data))
|
||||||
|
|
||||||
|
def get_types(self):
|
||||||
|
# logging.info("{} Get Types".format(self.name))
|
||||||
|
result = {}
|
||||||
|
for key, value in self.types.items():
|
||||||
|
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
# logging.info("{} Get State".format(self.name))
|
||||||
|
return {"data": list(map(lambda x: x.to_dict(), self.data)), "types":self.get_types()}
|
||||||
|
|
||||||
async def get(self, id: str ):
|
async def get(self, id: str ):
|
||||||
return self._find_by_id(id)
|
return self._find_by_id(id)
|
||||||
|
@ -162,12 +200,21 @@ class FermenationController:
|
||||||
data.id = shortuuid.uuid()
|
data.id = shortuuid.uuid()
|
||||||
self.data.append(data)
|
self.data.append(data)
|
||||||
self.save()
|
self.save()
|
||||||
|
self.push_update()
|
||||||
|
#self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
#self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def update(self, item: Fermenter ):
|
async def update(self, item: Fermenter ):
|
||||||
|
|
||||||
|
logging.info(item)
|
||||||
|
|
||||||
def _update(old_item: Fermenter, item: Fermenter):
|
def _update(old_item: Fermenter, item: Fermenter):
|
||||||
old_item.name = item.name
|
old_item.name = item.name
|
||||||
|
old_item.sensor = item.sensor
|
||||||
|
old_item.heater = item.heater
|
||||||
|
old_item.cooler = item.cooler
|
||||||
|
old_item.type = item.type
|
||||||
old_item.brewname = item.brewname
|
old_item.brewname = item.brewname
|
||||||
old_item.props = item.props
|
old_item.props = item.props
|
||||||
old_item.target_temp = item.target_temp
|
old_item.target_temp = item.target_temp
|
||||||
|
@ -175,12 +222,31 @@ class FermenationController:
|
||||||
|
|
||||||
self.data = list(map(lambda old: _update(old, item) if old.id == item.id else old, self.data))
|
self.data = list(map(lambda old: _update(old, item) if old.id == item.id else old, self.data))
|
||||||
self.save()
|
self.save()
|
||||||
|
self.push_update()
|
||||||
|
#self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
#self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
async def set_target_temp(self, id: str, target_temp):
|
||||||
|
try:
|
||||||
|
item = self._find_by_id(id)
|
||||||
|
logging.info(item.target_temp)
|
||||||
|
if item:
|
||||||
|
item.target_temp = target_temp
|
||||||
|
self.save()
|
||||||
|
self.push_update()
|
||||||
|
#self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
#self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Failed to set Target Temp {} {}".format(id, e))
|
||||||
|
|
||||||
async def delete(self, id: str ):
|
async def delete(self, id: str ):
|
||||||
item = self._find_by_id(id)
|
item = self._find_by_id(id)
|
||||||
self.data = list(filter(lambda item: item.id != id, self.data))
|
self.data = list(filter(lambda item: item.id != id, self.data))
|
||||||
self.save()
|
self.save()
|
||||||
|
self.push_update()
|
||||||
|
#self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
#self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
with open(self.path, "w") as file:
|
with open(self.path, "w") as file:
|
||||||
|
@ -191,7 +257,7 @@ class FermenationController:
|
||||||
step.id = shortuuid.uuid()
|
step.id = shortuuid.uuid()
|
||||||
item = self._find_by_id(id)
|
item = self._find_by_id(id)
|
||||||
|
|
||||||
step.instance = FermentStep( self.cbpi, step.id, step.name, None, self._done)
|
step.instance = FermentStep( self.cbpi, step, self._done)
|
||||||
|
|
||||||
item.steps.append(step)
|
item.steps.append(step)
|
||||||
self.save()
|
self.save()
|
||||||
|
@ -223,10 +289,11 @@ class FermenationController:
|
||||||
|
|
||||||
if step is None:
|
if step is None:
|
||||||
self.logger.info("No futher step to start")
|
self.logger.info("No futher step to start")
|
||||||
|
else:
|
||||||
|
await step.instance.start()
|
||||||
|
step.status = StepState.ACTIVE
|
||||||
|
self.save()
|
||||||
|
|
||||||
await step.instance.start()
|
|
||||||
step.status = StepState.ACTIVE
|
|
||||||
self.save()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
|
|
||||||
|
@ -240,6 +307,46 @@ class FermenationController:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
|
|
||||||
|
async def start_logic(self, id):
|
||||||
|
try:
|
||||||
|
item = self._find_by_id(id)
|
||||||
|
logging.info("{} Start Id {} ".format(item.name, id))
|
||||||
|
if item.instance is not None and item.instance.running is True:
|
||||||
|
logging.warning("{} already running {}".format(item.name, id))
|
||||||
|
return
|
||||||
|
if item.type is None:
|
||||||
|
logging.warning("{} No Type {}".format(item.name, id))
|
||||||
|
return
|
||||||
|
clazz = self.types[item.type]["class"]
|
||||||
|
item.instance = clazz(self.cbpi, item.id, item.props)
|
||||||
|
|
||||||
|
await item.instance.start()
|
||||||
|
item.instance.running = True
|
||||||
|
item.instance.task = self._loop.create_task(item.instance._run())
|
||||||
|
|
||||||
|
logging.info("{} started {}".format(item.name, id))
|
||||||
|
|
||||||
|
# await self.push_udpate()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("{} Cant start {} - {}".format(item.name, id, e))
|
||||||
|
|
||||||
|
async def toggle(self, id):
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = self._find_by_id(id)
|
||||||
|
#logging.info(item)
|
||||||
|
|
||||||
|
if item.instance is None or item.instance.state == False:
|
||||||
|
await self.start_logic(id)
|
||||||
|
else:
|
||||||
|
await item.instance.stop()
|
||||||
|
self.push_update()
|
||||||
|
#self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||||
|
#self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Failed to switch on FermenterLogic {} {}".format(id, e))
|
||||||
|
|
||||||
|
|
||||||
async def next(self, id):
|
async def next(self, id):
|
||||||
self.logger.info("Next {} ".format(id))
|
self.logger.info("Next {} ".format(id))
|
||||||
|
@ -284,4 +391,3 @@ class FermenationController:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ class PluginController():
|
||||||
logger.info("Trying to load plugin %s" % filename)
|
logger.info("Trying to load plugin %s" % filename)
|
||||||
data = load_config(os.path.join(
|
data = load_config(os.path.join(
|
||||||
this_directory, "../extension/%s/config.yaml" % filename))
|
this_directory, "../extension/%s/config.yaml" % filename))
|
||||||
|
|
||||||
if (data.get("active") is True and data.get("version") == 4):
|
if (data.get("active") is True and data.get("version") == 4):
|
||||||
self.modules[filename] = import_module(
|
self.modules[filename] = import_module(
|
||||||
"cbpi.extension.%s" % (filename))
|
"cbpi.extension.%s" % (filename))
|
||||||
|
@ -75,6 +74,9 @@ class PluginController():
|
||||||
if issubclass(clazz, CBPiKettleLogic):
|
if issubclass(clazz, CBPiKettleLogic):
|
||||||
self.cbpi.kettle.types[name] = self._parse_step_props(clazz, name)
|
self.cbpi.kettle.types[name] = self._parse_step_props(clazz, name)
|
||||||
|
|
||||||
|
if issubclass(clazz, CBPiFermenterLogic):
|
||||||
|
self.cbpi.fermenter.types[name] = self._parse_step_props(clazz, name)
|
||||||
|
|
||||||
if issubclass(clazz, CBPiSensor):
|
if issubclass(clazz, CBPiSensor):
|
||||||
self.cbpi.sensor.types[name] = self._parse_step_props(clazz, name)
|
self.cbpi.sensor.types[name] = self._parse_step_props(clazz, name)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ from cbpi.controller.sensor_controller import SensorController
|
||||||
from cbpi.controller.step_controller import StepController
|
from cbpi.controller.step_controller import StepController
|
||||||
from cbpi.controller.recipe_controller import RecipeController
|
from cbpi.controller.recipe_controller import RecipeController
|
||||||
from cbpi.controller.upload_controller import UploadController
|
from cbpi.controller.upload_controller import UploadController
|
||||||
|
from cbpi.controller.fermentation_controller import FermentationController
|
||||||
|
|
||||||
from cbpi.controller.system_controller import SystemController
|
from cbpi.controller.system_controller import SystemController
|
||||||
from cbpi.controller.satellite_controller import SatelliteController
|
from cbpi.controller.satellite_controller import SatelliteController
|
||||||
|
@ -49,6 +50,7 @@ from cbpi.http_endpoints.http_system import SystemHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_log import LogHttpEndpoints
|
from cbpi.http_endpoints.http_log import LogHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_notification import NotificationHttpEndpoints
|
from cbpi.http_endpoints.http_notification import NotificationHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_upload import UploadHttpEndpoints
|
from cbpi.http_endpoints.http_upload import UploadHttpEndpoints
|
||||||
|
from cbpi.http_endpoints.http_fermentation import FermentationHttpEndpoints
|
||||||
|
|
||||||
import shortuuid
|
import shortuuid
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -115,8 +117,9 @@ class CraftBeerPi:
|
||||||
self.satellite = None
|
self.satellite = None
|
||||||
if str(self.static_config.get("mqtt", False)).lower() == "true":
|
if str(self.static_config.get("mqtt", False)).lower() == "true":
|
||||||
self.satellite: SatelliteController = SatelliteController(self)
|
self.satellite: SatelliteController = SatelliteController(self)
|
||||||
|
|
||||||
self.dashboard = DashboardController(self)
|
self.dashboard = DashboardController(self)
|
||||||
|
self.fermenter : FermentationController = FermentationController(self)
|
||||||
|
|
||||||
self.http_step = StepHttpEndpoints(self)
|
self.http_step = StepHttpEndpoints(self)
|
||||||
self.http_recipe = RecipeHttpEndpoints(self)
|
self.http_recipe = RecipeHttpEndpoints(self)
|
||||||
self.http_sensor = SensorHttpEndpoints(self)
|
self.http_sensor = SensorHttpEndpoints(self)
|
||||||
|
@ -129,7 +132,7 @@ class CraftBeerPi:
|
||||||
self.http_log = LogHttpEndpoints(self)
|
self.http_log = LogHttpEndpoints(self)
|
||||||
self.http_notification = NotificationHttpEndpoints(self)
|
self.http_notification = NotificationHttpEndpoints(self)
|
||||||
self.http_upload = UploadHttpEndpoints(self)
|
self.http_upload = UploadHttpEndpoints(self)
|
||||||
|
self.http_fermenter = FermentationHttpEndpoints(self)
|
||||||
|
|
||||||
self.login = Login(self)
|
self.login = Login(self)
|
||||||
|
|
||||||
|
@ -279,7 +282,7 @@ class CraftBeerPi:
|
||||||
await self.kettle.init()
|
await self.kettle.init()
|
||||||
await self.call_initializer(self.app)
|
await self.call_initializer(self.app)
|
||||||
await self.dashboard.init()
|
await self.dashboard.init()
|
||||||
|
await self.fermenter.init()
|
||||||
|
|
||||||
self._swagger_setup()
|
self._swagger_setup()
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import os, threading, time
|
import os, threading, time, shutil
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import logging
|
import logging
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
|
import json
|
||||||
from cbpi.api import *
|
from cbpi.api import *
|
||||||
from cbpi.api.config import ConfigType
|
from cbpi.api.config import ConfigType
|
||||||
from cbpi.api.base import CBPiBase
|
from cbpi.api.base import CBPiBase
|
||||||
|
|
115
cbpi/extension/FermenterHysteresis/__init__.py
Normal file
115
cbpi/extension/FermenterHysteresis/__init__.py
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import asyncio
|
||||||
|
from asyncio import tasks
|
||||||
|
import logging
|
||||||
|
from cbpi.api import *
|
||||||
|
import aiohttp
|
||||||
|
from aiohttp import web
|
||||||
|
from cbpi.controller.fermentation_controller import FermentationController
|
||||||
|
from cbpi.api.dataclasses import Fermenter, Props, Step
|
||||||
|
from cbpi.api.base import CBPiBase
|
||||||
|
from cbpi.api.config import ConfigType
|
||||||
|
import json
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
class FermenterAutostart(CBPiExtension):
|
||||||
|
|
||||||
|
def __init__(self,cbpi):
|
||||||
|
self.cbpi = cbpi
|
||||||
|
self._task = asyncio.create_task(self.run())
|
||||||
|
self.controller : FermentationController = cbpi.fermenter
|
||||||
|
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
logging.info("Starting Fermenter Autorun")
|
||||||
|
#get all kettles
|
||||||
|
self.fermenter = self.controller.get_state()
|
||||||
|
for id in self.fermenter['data']:
|
||||||
|
try:
|
||||||
|
self.autostart=(id['props']['AutoStart'])
|
||||||
|
if self.autostart == "Yes":
|
||||||
|
fermenter_id=(id['id'])
|
||||||
|
logging.info("Enabling Autostart for Fermenter {}".format(fermenter_id))
|
||||||
|
self.fermenter=self.cbpi.fermenter._find_by_id(fermenter_id)
|
||||||
|
try:
|
||||||
|
if (self.fermenter.instance is None or self.fermenter.instance.state == False):
|
||||||
|
await self.cbpi.fermenter.toggle(self.fermenter.id)
|
||||||
|
logging.info("Successfully switched on Ferenterlogic for Fermenter {}".format(self.fermenter.id))
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Failed to switch on FermenterLogic {} {}".format(self.fermenter.id, e))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@parameters([Property.Number(label="HeaterOffsetOn", configurable=True, description="Offset as decimal number when the heater is switched on. Should be greater then 'HeaterOffsetOff'. For example a value of 2 switches on the heater if the current temperature is 2 degrees below the target temperature"),
|
||||||
|
Property.Number(label="HeaterOffsetOff", configurable=True, description="Offset as decimal number when the heater is switched off. Should be smaller then 'HeaterOffsetOn'. For example a value of 1 switches off the heater if the current temperature is 1 degree below the target temperature"),
|
||||||
|
Property.Number(label="CoolerOffsetOn", configurable=True, description="Offset as decimal number when the cooler is switched on. Should be greater then 'CoolerOffsetOff'. For example a value of 2 switches on the cooler if the current temperature is 2 degrees below the target temperature"),
|
||||||
|
Property.Number(label="CoolerOffsetOff", configurable=True, description="Offset as decimal number when the cooler is switched off. Should be smaller then 'CoolerOffsetOn'. For example a value of 1 switches off the cooler if the current temperature is 1 degree below the target temperature"),
|
||||||
|
Property.Select(label="AutoStart", options=["Yes","No"],description="Autostart Fermenter on cbpi start")])
|
||||||
|
|
||||||
|
class FermenterHysteresis(CBPiFermenterLogic):
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
try:
|
||||||
|
self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0))
|
||||||
|
self.heater_offset_max = float(self.props.get("HeaterOffsetOff", 0))
|
||||||
|
self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0))
|
||||||
|
self.cooler_offset_max = float(self.props.get("CoolerOffsetOff", 0))
|
||||||
|
|
||||||
|
self.fermenter = self.get_fermenter(self.id)
|
||||||
|
self.heater = self.fermenter.heater
|
||||||
|
self.cooler = self.fermenter.cooler
|
||||||
|
|
||||||
|
target_temp = self.get_fermenter_target_temp(self.id)
|
||||||
|
if target_temp == 0:
|
||||||
|
await self.set_fermenter_target_temp(self.id,int(self.props.get("TargetTemp", 0)))
|
||||||
|
|
||||||
|
|
||||||
|
while self.running == True:
|
||||||
|
|
||||||
|
sensor_value = self.get_sensor_value(self.fermenter.sensor).get("value")
|
||||||
|
target_temp = self.get_fermenter_target_temp(self.id)
|
||||||
|
|
||||||
|
if sensor_value + self.heater_offset_min <= target_temp:
|
||||||
|
if self.heater:
|
||||||
|
await self.actor_on(self.heater)
|
||||||
|
|
||||||
|
if sensor_value + self.heater_offset_max >= target_temp:
|
||||||
|
if self.heater:
|
||||||
|
await self.actor_off(self.heater)
|
||||||
|
|
||||||
|
if sensor_value >= self.cooler_offset_min + target_temp:
|
||||||
|
if self.cooler:
|
||||||
|
await self.actor_on(self.cooler)
|
||||||
|
|
||||||
|
if sensor_value <= self.cooler_offset_max + target_temp:
|
||||||
|
if self.cooler:
|
||||||
|
await self.actor_off(self.cooler)
|
||||||
|
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
except asyncio.CancelledError as e:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("CustomLogic Error {}".format(e))
|
||||||
|
finally:
|
||||||
|
self.running = False
|
||||||
|
if self.heater:
|
||||||
|
await self.actor_off(self.heater)
|
||||||
|
if self.cooler:
|
||||||
|
await self.actor_off(self.cooler)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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("Fermenter Hysteresis", FermenterHysteresis)
|
||||||
|
cbpi.plugin.register("Fermenter Autostart", FermenterAutostart)
|
||||||
|
|
3
cbpi/extension/FermenterHysteresis/config.yaml
Normal file
3
cbpi/extension/FermenterHysteresis/config.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
name: FermenterHysteresis
|
||||||
|
version: 4
|
||||||
|
active: true
|
285
cbpi/http_endpoints/http_fermentation.py
Normal file
285
cbpi/http_endpoints/http_fermentation.py
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
from cbpi.controller.fermentation_controller import FermentationController
|
||||||
|
from cbpi.api.dataclasses import Fermenter, Step, Props
|
||||||
|
from aiohttp import web
|
||||||
|
from cbpi.api import *
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
auth = False
|
||||||
|
|
||||||
|
class FermentationHttpEndpoints():
|
||||||
|
|
||||||
|
def __init__(self, cbpi):
|
||||||
|
self.cbpi = cbpi
|
||||||
|
self.controller : FermentationController = cbpi.fermenter
|
||||||
|
self.cbpi.register(self, "/fermenter")
|
||||||
|
|
||||||
|
@request_mapping(path="/", auth_required=False)
|
||||||
|
async def http_get_all(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Show all Fermenters
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
data= self.controller.get_state()
|
||||||
|
return web.json_response(data=data)
|
||||||
|
|
||||||
|
|
||||||
|
@request_mapping(path="/", method="POST", auth_required=False)
|
||||||
|
async def http_add(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: add one Fermenter
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Create a Fermenter
|
||||||
|
required: true
|
||||||
|
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
sensor:
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
heater:
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
cooler:
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
target_temp:
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
props:
|
||||||
|
type: object
|
||||||
|
example:
|
||||||
|
name: "Fermenter 1"
|
||||||
|
type: "CustomFermenterLogic"
|
||||||
|
sensor: "FermenterSensor"
|
||||||
|
heater: "FermenterHeater"
|
||||||
|
cooler: "FermenterCooler"
|
||||||
|
props: {}
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
data = await request.json()
|
||||||
|
fermenter = Fermenter(id=id, name=data.get("name"), sensor=data.get("sensor"), heater=data.get("heater"), cooler=data.get("cooler"), brewname=data.get("brewname"), target_temp=data.get("target_temp"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||||
|
response_data = await self.controller.create(fermenter)
|
||||||
|
return web.json_response(data=response_data.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@request_mapping(path="/{id}", method="PUT", auth_required=False)
|
||||||
|
async def http_update(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Update a Fermenter (NOT YET IMPLEMENTED)
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Fermenter ID"
|
||||||
|
required: true
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Update a Fermenter
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
config:
|
||||||
|
props: object
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
id = request.match_info['id']
|
||||||
|
data = await request.json()
|
||||||
|
fermenter = Fermenter(id=id, name=data.get("name"), sensor=data.get("sensor"), heater=data.get("heater"), cooler=data.get("cooler"), brewname=data.get("brewname"), target_temp=data.get("target_temp"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||||
|
return web.json_response(data=(await self.controller.update(fermenter)).to_dict())
|
||||||
|
|
||||||
|
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
|
||||||
|
async def http_delete_one(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Delete an actor
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Fermenter ID"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
id = request.match_info['id']
|
||||||
|
await self.controller.delete(id)
|
||||||
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
# @request_mapping(path="/{id}/on", method="POST", auth_required=False)
|
||||||
|
# async def http_on(self, request) -> web.Response:
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Switch actor on
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Actor ID"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"405":
|
||||||
|
description: invalid HTTP Met
|
||||||
|
"""
|
||||||
|
# id = request.match_info['id']
|
||||||
|
# await self.controller.start(id)
|
||||||
|
# return web.Response(status=204)
|
||||||
|
|
||||||
|
# @request_mapping(path="/{id}/off", method="POST", auth_required=False)
|
||||||
|
# async def http_off(self, request) -> web.Response:
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Switch actor on
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Actor ID"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"405":
|
||||||
|
description: invalid HTTP Met
|
||||||
|
"""
|
||||||
|
# id = request.match_info['id']
|
||||||
|
# await self.controller.off(id)
|
||||||
|
# return web.Response(status=204)
|
||||||
|
|
||||||
|
@request_mapping(path="/{id}/toggle", method="POST", auth_required=False)
|
||||||
|
async def http_toggle(self, request) -> web.Response:
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Switch actor on
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Kettle ID"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"405":
|
||||||
|
description: invalid HTTP Met
|
||||||
|
"""
|
||||||
|
id = request.match_info['id']
|
||||||
|
await self.controller.toggle(id)
|
||||||
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
# @request_mapping(path="/{id}/action", method="POST", auth_required=auth)
|
||||||
|
# async def http_action(self, request) -> web.Response:
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Toogle an actor on or off
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Actor ID"
|
||||||
|
required: true
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Update an actor
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
parameter:
|
||||||
|
type: object
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
# actor_id = request.match_info['id']
|
||||||
|
# data = await request.json()
|
||||||
|
# await self.controller.call_action(actor_id, data.get("name"), data.get("parameter"))
|
||||||
|
|
||||||
|
return web.Response(status=204)
|
||||||
|
@request_mapping(path="/{id}/target_temp", method="POST", auth_required=auth)
|
||||||
|
async def http_target(self, request) -> web.Response:
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Set Target Temp for Fermenter
|
||||||
|
tags:
|
||||||
|
- Fermenter
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Fermenter ID"
|
||||||
|
required: true
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Update Temp
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
temp:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
id = request.match_info['id']
|
||||||
|
data = await request.json()
|
||||||
|
await self.controller.set_target_temp(id,data.get("temp"))
|
||||||
|
return web.Response(status=204)
|
|
@ -29,6 +29,7 @@ class SystemHttpEndpoints:
|
||||||
"""
|
"""
|
||||||
return web.json_response(data=dict(
|
return web.json_response(data=dict(
|
||||||
actor=self.cbpi.actor.get_state(),
|
actor=self.cbpi.actor.get_state(),
|
||||||
|
fermenter=self.cbpi.fermenter.get_state(),
|
||||||
sensor=self.cbpi.sensor.get_state(),
|
sensor=self.cbpi.sensor.get_state(),
|
||||||
kettle=self.cbpi.kettle.get_state(),
|
kettle=self.cbpi.kettle.get_state(),
|
||||||
step=self.cbpi.step.get_state(),
|
step=self.cbpi.step.get_state(),
|
||||||
|
|
Loading…
Reference in a new issue