Additional CBPIFermenterStep Class

Class has been activated but is not yet used
Fix in mqtt actor power action setting
This commit is contained in:
avollkopf 2022-01-05 06:46:35 +01:00
parent 49362aa098
commit a8f7f2d92f
10 changed files with 257 additions and 31 deletions

View file

@ -1 +1 @@
__version__ = "4.0.1.a6" __version__ = "4.0.1.a7"

View file

@ -15,7 +15,8 @@ __all__ = ["CBPiActor",
"SensorException", "SensorException",
"ActorException", "ActorException",
"CBPiSensor", "CBPiSensor",
"CBPiStep"] "CBPiStep",
"CBPiFermentationStep"]
from cbpi.api.actor import * from cbpi.api.actor import *
from cbpi.api.sensor import * from cbpi.api.sensor import *

View file

@ -1,4 +1,4 @@
__all__ = ["CBPiException","KettleException","SensorException","ActorException"] __all__ = ["CBPiException","KettleException","FermenterException","SensorException","ActorException"]
class CBPiException(Exception): class CBPiException(Exception):
@ -7,6 +7,9 @@ class CBPiException(Exception):
class KettleException(CBPiException): class KettleException(CBPiException):
pass pass
class FermenterException(CBPiException):
pass
class SensorException(CBPiException): class SensorException(CBPiException):
pass pass

View file

@ -4,7 +4,7 @@ from abc import abstractmethod
from cbpi.api.base import CBPiBase from cbpi.api.base import CBPiBase
__all__ = ["StepResult", "StepState", "StepMove", "CBPiStep"] __all__ = ["StepResult", "StepState", "StepMove", "CBPiStep", "CBPiFermentationStep"]
from enum import Enum from enum import Enum

View file

@ -11,7 +11,7 @@ from cbpi.api.dataclasses import Fermenter, FermenterStep, Props, Step
from cbpi.controller.basic_controller2 import BasicController 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, CBPiFermentationStep
@ -90,6 +90,7 @@ class FermentationController:
self._loop = asyncio.get_event_loop() self._loop = asyncio.get_event_loop()
self.data = [] self.data = []
self.types = {} self.types = {}
self.steptypes = {}
self.cbpi.app.on_cleanup.append(self.shutdown) self.cbpi.app.on_cleanup.append(self.shutdown)
async def init(self): async def init(self):
@ -171,8 +172,6 @@ class FermentationController:
fermenter = Fermenter(id, name, sensor, heater, cooler, brewname, props, temp, logictype) 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.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
except: except:
return return
@ -185,18 +184,22 @@ class FermentationController:
return list(map(lambda x: x.to_dict(), self.data)) return list(map(lambda x: x.to_dict(), self.data))
def get_types(self): def get_types(self):
# logging.info("{} Get Types".format(self.name))
result = {} result = {}
for key, value in self.types.items(): for key, value in self.types.items():
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions")) result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
return result return result
def get_steptypes(self):
result = {}
for key, value in self.steptypes.items():
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
return result
def get_state(self): def get_state(self):
# logging.info("{} Get State".format(self.name))
if self.data == []: if self.data == []:
logging.info(self.data) logging.info(self.data)
return {"data": list(map(lambda x: x.to_dict(), self.data)), "types":self.get_types()} return {"data": list(map(lambda x: x.to_dict(), self.data)), "types":self.get_types(), "steptypes":self.get_steptypes()}
async def get(self, id: str ): async def get(self, id: str ):
return self._find_by_id(id) return self._find_by_id(id)
@ -206,8 +209,6 @@ class FermentationController:
self.data.append(data) self.data.append(data)
self.save() self.save()
self.push_update() 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 ):
@ -228,8 +229,6 @@ class FermentationController:
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.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): async def set_target_temp(self, id: str, target_temp):
@ -240,8 +239,6 @@ class FermentationController:
item.target_temp = target_temp item.target_temp = target_temp
self.save() self.save()
self.push_update() 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: except Exception as e:
logging.error("Failed to set Target Temp {} {}".format(id, e)) logging.error("Failed to set Target Temp {} {}".format(id, e))
@ -250,8 +247,6 @@ class FermentationController:
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.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):
data = dict(data=list(map(lambda item: item.to_dict(), self.data))) data = dict(data=list(map(lambda item: item.to_dict(), self.data)))
@ -332,7 +327,6 @@ class FermentationController:
logging.info("{} started {}".format(item.name, id)) logging.info("{} started {}".format(item.name, id))
# await self.push_udpate()
except Exception as e: except Exception as e:
logging.error("{} Cant start {} - {}".format(item.name, id, e)) logging.error("{} Cant start {} - {}".format(item.name, id, e))
@ -340,16 +334,13 @@ class FermentationController:
try: try:
item = self._find_by_id(id) item = self._find_by_id(id)
#logging.info(item)
if item.instance is None or item.instance.state == False: if item.instance is None or item.instance.state == False:
await self.start_logic(id) await self.start_logic(id)
else: else:
await item.instance.stop() await item.instance.stop()
self.push_update() 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: except Exception as e:
logging.error("Failed to switch on FermenterLogic {} {}".format(id, e)) logging.error("Failed to switch on FermenterLogic {} {}".format(id, e))

View file

@ -31,7 +31,7 @@ class PluginController():
try: try:
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))
@ -83,6 +83,9 @@ class PluginController():
if issubclass(clazz, CBPiStep): if issubclass(clazz, CBPiStep):
self.cbpi.step.types[name] = self._parse_step_props(clazz, name) self.cbpi.step.types[name] = self._parse_step_props(clazz, name)
if issubclass(clazz, CBPiFermentationStep):
self.cbpi.fermenter.steptypes[name] = self._parse_step_props(clazz, name)
if issubclass(clazz, CBPiExtension): if issubclass(clazz, CBPiExtension):
self.c = clazz(self.cbpi) self.c = clazz(self.cbpi)

View file

@ -0,0 +1,231 @@
import asyncio
from cbpi.api import parameters, Property, action
from cbpi.api.step import StepResult, CBPiFermentationStep
from cbpi.api.timer import Timer
from datetime import datetime
import time
from voluptuous.schema_builder import message
from cbpi.api.dataclasses import NotificationAction, NotificationType
from cbpi.api.dataclasses import Kettle, Props, Fermenter
from cbpi.api import *
import logging
from socket import timeout
from typing import KeysView
from cbpi.api.config import ConfigType
from cbpi.api.base import CBPiBase
import numpy as np
import warnings
@parameters([Property.Text(label="Notification",configurable = True, description = "Text for notification"),
Property.Select(label="AutoNext",options=["Yes","No"], description="Automatically move to next step (Yes) or pause after Notification (No)")])
class NotificationStep(CBPiFermentationStep):
async def NextStep(self, **kwargs):
await self.next()
async def on_timer_done(self,timer):
self.summary = self.props.get("Notification","")
if self.AutoNext == True:
self.cbpi.notify(self.name, self.props.get("Notification",""), NotificationType.INFO)
await self.next()
else:
self.cbpi.notify(self.name, self.props.get("Notification",""), NotificationType.INFO, action=[NotificationAction("Next Step", self.NextStep)])
await self.push_update()
async def on_timer_update(self,timer, seconds):
await self.push_update()
async def on_start(self):
self.summary=""
self.AutoNext = False if self.props.get("AutoNext", "No") == "No" else True
if self.timer is None:
self.timer = Timer(1 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
await self.push_update()
async def on_stop(self):
await self.timer.stop()
self.summary = ""
await self.push_update()
async def run(self):
while self.running == True:
await asyncio.sleep(1)
if self.timer.is_running is not True:
self.timer.start()
self.timer.is_running = True
return StepResult.DONE
@parameters([Property.Number(label="Temp", configurable=True),
Property.Sensor(label="Sensor"),
Property.Kettle(label="Kettle"),
Property.Text(label="Notification",configurable = True, description = "Text for notification when Temp is reached"),
Property.Select(label="AutoMode",options=["Yes","No"], description="Switch Kettlelogic automatically on and off -> Yes")])
class TargetTempStep(CBPiFermentationStep):
async def NextStep(self, **kwargs):
await self.next()
async def on_timer_done(self,timer):
self.summary = ""
self.kettle.target_temp = 0
await self.push_update()
if self.AutoMode == True:
await self.setAutoMode(False)
self.cbpi.notify(self.name, self.props.get("Notification","Target Temp reached. Please add malt and klick next to move on."), action=[NotificationAction("Next Step", self.NextStep)])
async def on_timer_update(self,timer, seconds):
await self.push_update()
async def on_start(self):
self.AutoMode = True if self.props.get("AutoMode","No") == "Yes" else False
self.kettle=self.get_kettle(self.props.get("Kettle", None))
if self.kettle is not None:
self.kettle.target_temp = int(self.props.get("Temp", 0))
if self.AutoMode == True:
await self.setAutoMode(True)
self.summary = "Waiting for Target Temp"
if self.cbpi.kettle is not None and self.timer is None:
self.timer = Timer(1 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
await self.push_update()
async def on_stop(self):
await self.timer.stop()
self.summary = ""
if self.AutoMode == True:
await self.setAutoMode(False)
await self.push_update()
async def run(self):
while self.running == True:
await asyncio.sleep(1)
sensor_value = self.get_sensor_value(self.props.get("Sensor", None)).get("value")
if sensor_value >= int(self.props.get("Temp",0)) and self.timer.is_running is not True:
self.timer.start()
self.timer.is_running = True
await self.push_update()
return StepResult.DONE
async def reset(self):
self.timer = Timer(1 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
async def setAutoMode(self, auto_state):
try:
if (self.kettle.instance is None or self.kettle.instance.state == False) and (auto_state is True):
await self.cbpi.kettle.toggle(self.kettle.id)
elif (self.kettle.instance.state == True) and (auto_state is False):
await self.cbpi.kettle.stop(self.kettle.id)
await self.push_update()
except Exception as e:
logging.error("Failed to switch on KettleLogic {} {}".format(self.kettle.id, e))
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
Property.Number(label="Temp", configurable=True),
Property.Sensor(label="Sensor"),
Property.Kettle(label="Kettle"),
Property.Select(label="AutoMode",options=["Yes","No"], description="Switch Kettlelogic automatically on and off -> Yes")])
class FermentationStep(CBPiFermentationStep):
@action("Start Timer", [])
async def start_timer(self):
if self.timer.is_running is not True:
self.cbpi.notify(self.name, 'Timer started', NotificationType.INFO)
self.timer.start()
self.timer.is_running = True
else:
self.cbpi.notify(self.name, 'Timer is already running', NotificationType.WARNING)
@action("Add 5 Minutes to Timer", [])
async def add_timer(self):
if self.timer.is_running == True:
self.cbpi.notify(self.name, '5 Minutes added', NotificationType.INFO)
await self.timer.add(300)
else:
self.cbpi.notify(self.name, 'Timer must be running to add time', NotificationType.WARNING)
async def on_timer_done(self,timer):
self.summary = ""
self.kettle.target_temp = 0
if self.AutoMode == True:
await self.setAutoMode(False)
self.cbpi.notify(self.name, 'Step finished', NotificationType.SUCCESS)
await self.next()
async def on_timer_update(self,timer, seconds):
self.summary = Timer.format_time(seconds)
await self.push_update()
async def on_start(self):
self.AutoMode = True if self.props.get("AutoMode", "No") == "Yes" else False
self.kettle=self.get_kettle(self.props.Kettle)
if self.kettle is not None:
self.kettle.target_temp = int(self.props.get("Temp", 0))
if self.AutoMode == True:
await self.setAutoMode(True)
await self.push_update()
if self.cbpi.kettle is not None and self.timer is None:
self.timer = Timer(int(self.props.get("Timer",0)) *60 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
elif self.cbpi.kettle is not None:
try:
if self.timer.is_running == True:
self.timer.start()
except:
pass
self.summary = "Waiting for Target Temp"
await self.push_update()
async def on_stop(self):
await self.timer.stop()
self.summary = ""
if self.AutoMode == True:
await self.setAutoMode(False)
await self.push_update()
async def reset(self):
self.timer = Timer(int(self.props.get("Timer",0)) *60 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
async def run(self):
while self.running == True:
await asyncio.sleep(1)
sensor_value = self.get_sensor_value(self.props.get("Sensor", None)).get("value")
if sensor_value >= int(self.props.get("Temp",0)) and self.timer.is_running is not True:
self.timer.start()
self.timer.is_running = True
estimated_completion_time = datetime.fromtimestamp(time.time()+ (int(self.props.get("Timer",0)))*60)
self.cbpi.notify(self.name, 'Timer started. Estimated completion: {}'.format(estimated_completion_time.strftime("%H:%M")), NotificationType.INFO)
return StepResult.DONE
async def setAutoMode(self, auto_state):
try:
if (self.kettle.instance is None or self.kettle.instance.state == False) and (auto_state is True):
await self.cbpi.kettle.toggle(self.kettle.id)
elif (self.kettle.instance.state == True) and (auto_state is False):
await self.cbpi.kettle.stop(self.kettle.id)
await self.push_update()
except Exception as e:
logging.error("Failed to switch on KettleLogic {} {}".format(self.kettle.id, e))
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("NotificationStep", NotificationStep)
cbpi.plugin.register("TargetTempStep", TargetTempStep)
cbpi.plugin.register("FermentationStep", FermentationStep)

View file

@ -0,0 +1,3 @@
name: FermentationStep
version: 4
active: true

View file

@ -9,7 +9,7 @@ class MQTTActor(CBPiActor):
# Custom property which can be configured by the user # Custom property which can be configured by the user
@action("Set Power", parameters=[Property.Number(label="Power", configurable=True, description="Power Setting [0-100]")]) @action("Set Power", parameters=[Property.Number(label="Power", configurable=True, description="Power Setting [0-100]")])
async def setpower(self,Power = 100 ,**kwargs): async def setpower(self,Power = 100 ,**kwargs):
self.power=round(Power) self.power=int(Power)
if self.power < 0: if self.power < 0:
self.power = 0 self.power = 0
if self.power > 100: if self.power > 100:

View file

@ -27,12 +27,6 @@ class SystemHttpEndpoints:
"200": "200":
description: successful operation description: successful operation
""" """
try:
sensor=self.cbpi.sensor.get_state()
logging.info(sensor)
except:
logging.info("!!!!!!!!!!!!!!!!!!!!!!!!!Error get sensor state!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
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(), fermenter=self.cbpi.fermenter.get_state(),