Merge form dev branch

This commit is contained in:
Manuel Fritsch 2022-03-13 12:52:40 +01:00
commit 036e70e4bf
18 changed files with 220 additions and 239 deletions

View file

@ -1 +1 @@
__version__ = "4.0.2.0.a8" __version__ = "4.0.2.0.a19"

View file

@ -131,7 +131,7 @@ class Fermenter:
brewname: str = None brewname: str = None
description : str = None description : str = None
props: Props = Props() props: Props = Props()
target_temp: int = 0 target_temp: float = 0
type: str = None type: str = None
steps: List[Step]= field(default_factory=list) steps: List[Step]= field(default_factory=list)
instance: str = None instance: str = None
@ -170,7 +170,7 @@ class FermenterStep:
return "name={} props={}, type={}, instance={}".format(self.name, self.props, self.type, self.instance) return "name={} props={}, type={}, instance={}".format(self.name, self.props, self.type, self.instance)
def to_dict(self): def to_dict(self):
msg = self.instance.summary if self.instance is not None else "" msg = self.instance.summary if self.instance is not None else ""
return dict(id=self.id, name=self.name, state_text=msg, type=self.type, status=self.status.value, endtime = self.endtime, props=self.props.to_dict()) return dict(id=self.id, name=self.name, state_text=msg, type=self.type, status=self.status.value, endtime=self.endtime, props=self.props.to_dict())

View file

@ -117,15 +117,6 @@ class CBPiStep(CBPiBase):
def __str__(self): def __str__(self):
return "name={} props={}, type={}".format(self.name, self.props, self.__class__.__name__) return "name={} props={}, type={}".format(self.name, self.props, self.__class__.__name__)
#class CBPiFermentationStep(CBPiStep):
# def __init__(self, cbpi, fermenter, step, props, on_done) -> None:
# self.fermenter = fermenter
# id = step.get("id")
# name=step.get("name")
# self.step=step
# super().__init__(cbpi, id, name, props, on_done)
class CBPiFermentationStep(CBPiBase): class CBPiFermentationStep(CBPiBase):
def __init__(self, cbpi, fermenter, step, props, on_done) -> None: def __init__(self, cbpi, fermenter, step, props, on_done) -> None:

View file

@ -22,7 +22,7 @@ class BasicController:
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.data = [] self.data = []
self.autostart = True self.autostart = True
self._loop = asyncio.get_event_loop() #self._loop = asyncio.get_event_loop()
self.path = os.path.join(".", 'config', file) self.path = os.path.join(".", 'config', file)
self.cbpi.app.on_cleanup.append(self.shutdown) self.cbpi.app.on_cleanup.append(self.shutdown)
@ -100,7 +100,8 @@ class BasicController:
await item.instance.start() await item.instance.start()
item.instance.running = True item.instance.running = True
item.instance.task = self._loop.create_task(item.instance._run()) item.instance.task = asyncio.get_event_loop().create_task(item.instance._run())
#item.instance.task = self._loop.create_task(item.instance._run())
logging.info("{} started {}".format(self.name, id)) logging.info("{} started {}".format(self.name, id))

View file

@ -16,78 +16,6 @@ from tabulate import tabulate
import sys, os import sys, os
from ..api.step import CBPiStep, StepMove, StepResult, StepState, CBPiFermentationStep from ..api.step import CBPiStep, StepMove, StepResult, StepState, CBPiFermentationStep
class FermentStep:
def __init__(self, cbpi, step, on_done) -> None:
self.cbpi = cbpi
self.logger = logging.getLogger(__name__)
self.step = step
self.props = step.props
self._done_callback = on_done
self.task = None
self.summary = ""
def _done(self, task):
if self._done_callback is not None:
try:
result = task.result()
self._done_callback(self, result)
except Exception as e:
self.logger.error(e)
@abstractmethod
async def run(self):
while self.running:
logging.info(self.step)
await asyncio.sleep(1)
pass
async def _run(self):
try:
await self.step.instance.on_start()
await self.step.instance.run()
#await self.on_start()
#await self.run()
self.cancel_reason = StepResult.DONE
except asyncio.CancelledError as e:
pass
finally:
await self.on_stop()
return self.cancel_reason
async def start(self):
self.logger.info("Start {}".format(self.step.name))
self.running = True
self.task = asyncio.create_task(self._run())
self.task.add_done_callback(self._done)
async def next(self):
self.running = False
self.cancel_reason = StepResult.NEXT
self.task.cancel()
await self.task
async def stop(self):
try:
self.running = False
if self.task is not None and self.task.done() is False:
self.logger.info("Stopping Task")
self.cancel_reason = StepResult.STOP
self.task.cancel()
await self.task
except Exception as e:
self.logger.error(e)
async def on_start(self):
#self.props.hello = "WOOHOo"
pass
async def on_stop(self):
pass
class FermentationController: class FermentationController:
def __init__(self, cbpi): def __init__(self, cbpi):
@ -95,7 +23,6 @@ class FermentationController:
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")
self.data = [] self.data = []
self.types = {} self.types = {}
self.steptypes = {} self.steptypes = {}
@ -127,6 +54,10 @@ class FermentationController:
for step in fermenter.steps: for step in fermenter.steps:
try: try:
self.logger.info("Stop {}".format(step.name)) self.logger.info("Stop {}".format(step.name))
try:
step.instance.shutdown = True
except:
pass
await step.instance.stop() await step.instance.stop()
except Exception as e: except Exception as e:
self.logger.error(e) self.logger.error(e)
@ -136,15 +67,16 @@ class FermentationController:
for step in fermenter.steps: for step in fermenter.steps:
try: try:
self.logger.info("Stop {}".format(step.name)) self.logger.info("Stop {}".format(step.name))
try:
step.instance.shutdown = True
except:
pass
await step.instance.stop() await step.instance.stop()
except Exception as e: except Exception as e:
self.logger.error(e) self.logger.error(e)
async def load(self): async def load(self):
# if os.path.exists(self.path) is False:
# with open(self.path, "w") as file:
# json.dump(dict(basic={}, steps=[]), file, indent=4, sort_keys=True)
with open(self.path) as json_file: with open(self.path) as json_file:
data = json.load(json_file) data = json.load(json_file)
@ -155,12 +87,14 @@ class FermentationController:
id = item.get("id") id = item.get("id")
name = item.get("name") name = item.get("name")
props = Props(item.get("props")) props = Props(item.get("props"))
status = StepState(item.get("status", "I")) try:
endtime = int(item.get("endtime", 0)) endtime = int(item.get("endtime", 0))
except:
endtime=0
status = StepState(item.get("status", "I"))
if status == StepState.ACTIVE: if status == StepState.ACTIVE:
status = StepState("S") status = StepState("S")
if status != StepState.STOP:
endtime = 0
type = item.get("type") type = item.get("type")
try: try:
@ -221,7 +155,6 @@ class FermentationController:
def get_state(self): def get_state(self):
if self.data == []: if self.data == []:
#logging.info(self.data)
pass pass
return {"data": list(map(lambda x: x.to_dict(), self.data)), "types":self.get_types(), "steptypes":self.get_steptypes()} return {"data": list(map(lambda x: x.to_dict(), self.data)), "types":self.get_types(), "steptypes":self.get_steptypes()}
@ -238,7 +171,6 @@ class FermentationController:
def get_fermenter_steps(self): def get_fermenter_steps(self):
if self.data == []: if self.data == []:
#logging.info(self.data)
pass pass
fermentersteps=[] fermentersteps=[]
steplist=list(map(lambda x: x.to_dict(), self.data)) steplist=list(map(lambda x: x.to_dict(), self.data))
@ -307,16 +239,16 @@ class FermentationController:
with open(self.path, "w") as file: with open(self.path, "w") as file:
json.dump(data, file, indent=4, sort_keys=True) json.dump(data, file, indent=4, sort_keys=True)
async def create_step(self, id, item): def create_step(self, id, item):
try: try:
stepid = shortuuid.uuid() stepid = shortuuid.uuid()
props = item.get("props") item['id'] = stepid
status = StepState("I") status = StepState("I")
type = item.get("type") type = item.get("type")
name = item.get("name") name = item.get("name")
endtime = item.get("endtime", 0)
props = Props(item.get("props")) props = Props(item.get("props"))
fermenter = self._find_by_id(id) fermenter = self._find_by_id(id)
try: try:
type_cfg = self.steptypes.get(type) type_cfg = self.steptypes.get(type)
clazz = type_cfg.get("class") clazz = type_cfg.get("class")
@ -324,13 +256,8 @@ class FermentationController:
except Exception as e: except Exception as e:
logging.warning("Failed to create step instance %s - %s" % (id, e)) logging.warning("Failed to create step instance %s - %s" % (id, e))
instance = None instance = None
step = FermenterStep(id=stepid, name=name, fermenter=fermenter, props=props, type=type, status=status, instance=instance) step = FermenterStep(id=stepid, name=name, fermenter=fermenter, props=props, type=type, status=status, endtime=endtime, instance=instance)
fermenter.steps.append(step)
self.save()
self.push_update("fermenterstepupdate")
return step return step
except Exception as e: except Exception as e:
self.logger.error(e) self.logger.error(e)
@ -341,30 +268,29 @@ class FermentationController:
props = item.get("props") props = item.get("props")
status = StepState("I") status = StepState("I")
type = item.get("type") type = item.get("type")
#logging.info(type) endtime = 0
name = item.get("name") name = item.get("name")
props = Props(item.get("props")) props = Props(item.get("props"))
logging.info("update step") logging.info("update step")
try: try:
type_cfg = self.steptypes.get(type) type_cfg = self.steptypes.get(type)
#logging.info(type_cfg) logging.info(type_cfg)
clazz = type_cfg.get("class") clazz = type_cfg.get("class")
#logging.info(clazz) logging.info(clazz)
instance = clazz(self.cbpi, fermenter, item, props, self._done) instance = clazz(self.cbpi, fermenter, item, props, self._done)
logging.info(instance)
except Exception as e: except Exception as e:
logging.warning("Failed to create step instance %s - %s " % (item.id, e)) logging.warning("Failed to create step instance %s - %s " % (item.id, e))
instance = None instance = None
step = FermenterStep(id=stepid, name=name, fermenter=fermenter, props=props, type=type, status=status, instance=instance) step = FermenterStep(id=stepid, name=name, fermenter=fermenter, props=props, type=type, status=status, endtime=endtime, instance=instance)
#logging.info(step)
#logging.info(fermenter.steps)
try: try:
fermenter.steps = list(map(lambda old: step if old.id == step.id else old, fermenter.steps)) fermenter.steps = list(map(lambda old: step if old.id == step.id else old, fermenter.steps))
except Exception as e: except Exception as e:
logging.info(e) logging.info(e)
#logging.info(fermenter.steps)
self.save() self.save()
#logging.info("SAVEUPDATE")
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
@ -382,6 +308,15 @@ class FermentationController:
self.save() self.save()
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
async def update_endtime(self, id, stepid, endtime):
try:
item = self._find_by_id(id)
step = self._find_step_by_id(item.steps, stepid)
step.endtime = int(endtime)
self.save()
self.push_update("fermenterstepupdate")
except Exception as e:
self.logger.error(e)
def _find_by_status(self, data, status): def _find_by_status(self, data, status):
return next((item for item in data if item.status == status), None) return next((item for item in data if item.status == status), None)
@ -419,6 +354,7 @@ class FermentationController:
logging.info("Need to change timer") logging.info("Need to change timer")
step.status = StepState.ACTIVE step.status = StepState.ACTIVE
self.save() self.save()
self.push_update()
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
return return
@ -432,6 +368,7 @@ class FermentationController:
logging.info("Starting step {}".format(step.name)) logging.info("Starting step {}".format(step.name))
step.status = StepState.ACTIVE step.status = StepState.ACTIVE
self.save() self.save()
self.push_update()
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
except Exception as e: except Exception as e:
@ -517,7 +454,7 @@ class FermentationController:
await self.start(id) await self.start(id)
else: else:
logging.info("No Step is running") logging.info("No Step is running")
self.push_update()
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
except Exception as e: except Exception as e:
@ -531,12 +468,14 @@ class FermentationController:
for step in item.steps: for step in item.steps:
self.logger.info("Stopping Step {} {}".format(step.name, step.id)) self.logger.info("Stopping Step {} {}".format(step.name, step.id))
try: try:
await step.instance.reset()
await step.instance.stop() await step.instance.stop()
await step.instance.reset()
step.status = StepState.INITIAL step.status = StepState.INITIAL
step.endtime = 0
except Exception as e: except Exception as e:
self.logger.error(e) self.logger.error(e)
self.save() self.save()
self.push_update()
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
except Exception as e: except Exception as e:
@ -555,6 +494,7 @@ class FermentationController:
fermenter.steps[index], fermenter.steps[index+direction] = fermenter.steps[index+direction], fermenter.steps[index] fermenter.steps[index], fermenter.steps[index+direction] = fermenter.steps[index+direction], fermenter.steps[index]
self.save() self.save()
self.push_update()
self.push_update("fermenterstepupdate") self.push_update("fermenterstepupdate")
except Exception as e: except Exception as e:
@ -564,16 +504,20 @@ class FermentationController:
if key == self.update_key: if key == self.update_key:
self.cbpi.ws.send(dict(topic=key, data=list(map(lambda item: item.to_dict(), self.data)))) self.cbpi.ws.send(dict(topic=key, data=list(map(lambda item: item.to_dict(), self.data))))
#self.cbpi.push_update("cbpi/{}".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
for item in self.data: for item in self.data:
self.cbpi.push_update("cbpi/{}/{}".format(self.update_key,item.id), item.to_dict()) self.cbpi.push_update("cbpi/{}/{}".format(self.update_key,item.id), item.to_dict())
pass pass
else: else:
#logging.info("FERMENTERSTEPUPDATE {}".format(key))
fermentersteps=self.get_fermenter_steps() fermentersteps=self.get_fermenter_steps()
self.cbpi.ws.send(dict(topic=key, data=fermentersteps)) self.cbpi.ws.send(dict(topic=key, data=fermentersteps))
# send mqtt update for active femrentersteps
for fermenter in fermentersteps:
for step in fermenter['steps']:
if step['status'] == 'A':
self.cbpi.push_update("cbpi/{}/{}/{}".format(key,fermenter['id'],step['id']), step)
async def call_action(self, id, action, parameter) -> None: async def call_action(self, id, action, parameter) -> None:
logging.info("FermenterStep Controller - call Action {} {}".format(id, action)) logging.info("FermenterStep Controller - call Action {} {}".format(id, action))
try: try:
@ -583,7 +527,6 @@ class FermentationController:
except Exception as e: except Exception as e:
logging.error("FermenterStep Controller - Failed to call action on {} {} {}".format(id, action, e)) logging.error("FermenterStep Controller - Failed to call action on {} {} {}".format(id, action, e))
# todo: Sensors may need to be removed when saving the recipe -> need to be replaced when assinging later to Fermenter with 'fermenter.sensor'
async def savetobook(self, fermenterid): async def savetobook(self, fermenterid):
name = shortuuid.uuid() name = shortuuid.uuid()
path = os.path.join(".", 'config', "fermenterrecipes", "{}.yaml".format(name)) path = os.path.join(".", 'config', "fermenterrecipes", "{}.yaml".format(name))
@ -591,7 +534,7 @@ class FermentationController:
try: try:
brewname = fermenter.brewname brewname = fermenter.brewname
description = fermenter.description description = fermenter.description
# todo add escription at later point of time, once description has been added to fermenter dataclass
except: except:
brewname = "" brewname = ""
description = "" description = ""
@ -605,20 +548,38 @@ class FermentationController:
with open(path, "w") as file: with open(path, "w") as file:
yaml.dump(data, file) yaml.dump(data, file)
async def load_recipe(self, data, fermenterid): async def load_recipe(self, data, fermenterid, name):
try: try:
await self.shutdown(None, fermenterid) await self.shutdown(None, fermenterid)
except: except:
pass pass
fermenter = self._find_by_id(fermenterid) fermenter = self._find_by_id(fermenterid)
def add_runtime_data(item): def add_runtime_data(item):
item["status"] = "I" item["status"] = "I"
item["endtime"] = 0
item["id"] = shortuuid.uuid() item["id"] = shortuuid.uuid()
item["props"]["Sensor"] = fermenter.sensor item["props"]["Sensor"] = fermenter.sensor
list(map(lambda item: add_runtime_data(item), data.get("steps"))) list(map(lambda item: add_runtime_data(item), data.get("steps")))
fermenter.description = data['basic']['desc'] fermenter.description = data['basic']['desc']
if name is not None:
fermenter.brewname = name
else:
fermenter.brewname = data['basic']['name'] fermenter.brewname = data['basic']['name']
fermenter.steps=[]
await self.update(fermenter) await self.update(fermenter)
fermenter.steps=[]
for item in data.get("steps"): for item in data.get("steps"):
await self.create_step(fermenterid, item) fermenter.steps.append(self.create_step(fermenterid, item))
self.save()
self.push_update("fermenterstepupdate")
async def add_step(self, fermenterid, newstep):
fermenter = self._find_by_id(fermenterid)
step = self.create_step(fermenterid, newstep)
fermenter.steps.append(step)
self.save()
self.push_update("fermenterstepupdate")
return step

View file

@ -69,13 +69,13 @@ class FermenterRecipeController:
os.remove(path) os.remove(path)
async def brew(self, name, fermenterid): async def brew(self, recipeid, fermenterid, name):
recipe_path = os.path.join(".", 'config', "fermenterrecipes", "%s.yaml" % name) recipe_path = os.path.join(".", 'config', "fermenterrecipes", "%s.yaml" % recipeid)
logging.info(recipe_path) logging.info(recipe_path)
with open(recipe_path) as file: with open(recipe_path) as file:
data = yaml.load(file, Loader=yaml.FullLoader) data = yaml.load(file, Loader=yaml.FullLoader)
await self.cbpi.fermenter.load_recipe(data, fermenterid) await self.cbpi.fermenter.load_recipe(data, fermenterid, name)
async def clone(self, id, new_name): async def clone(self, id, new_name):
recipe_path = os.path.join(".", 'config', "fermenterrecipes", "%s.yaml" % id) recipe_path = os.path.join(".", 'config', "fermenterrecipes", "%s.yaml" % id)

View file

@ -20,7 +20,7 @@ class StepController:
self.cbpi = cbpi self.cbpi = cbpi
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.path = os.path.join(".", 'config', "step_data.json") self.path = os.path.join(".", 'config', "step_data.json")
self._loop = asyncio.get_event_loop() #self._loop = asyncio.get_event_loop()
self.basic_data = {} self.basic_data = {}
self.step = None self.step = None
self.types = {} self.types = {}
@ -69,7 +69,8 @@ class StepController:
if startActive is True: if startActive is True:
active_step = self.find_by_status("A") active_step = self.find_by_status("A")
if active_step is not None: if active_step is not None:
self._loop.create_task(self.start_step(active_step)) asyncio.get_event_loop().create_task(self.start_step(active_step))
#self._loop.create_task(self.start_step(active_step))
async def add(self, item: Step): async def add(self, item: Step):
logging.debug("Add step") logging.debug("Add step")

View file

@ -178,6 +178,8 @@ class SystemController:
mempercent = 0 mempercent = 0
eth0IP = "N/A" eth0IP = "N/A"
wlan0IP = "N/A" wlan0IP = "N/A"
eth0speed = "N/A"
wlan0speed = "N/A"
TEMP_UNIT=self.cbpi.config.get("TEMP_UNIT", "C") TEMP_UNIT=self.cbpi.config.get("TEMP_UNIT", "C")
FAHRENHEIT = False if TEMP_UNIT == "C" else True FAHRENHEIT = False if TEMP_UNIT == "C" else True
@ -225,6 +227,25 @@ class SystemController:
if str(addr.family) == "AddressFamily.AF_INET": if str(addr.family) == "AddressFamily.AF_INET":
if addr.address: if addr.address:
wlan0IP = addr.address wlan0IP = addr.address
info = psutil.net_if_stats()
try:
for nic in info:
if nic == 'eth0':
if info[nic].isup == True:
if info[nic].speed:
eth0speed = info[nic].speed
else:
eth0speed = "down"
if nic == 'wlan0':
if info[nic].isup == True:
ratestring = os.popen('iwlist wlan0 rate | grep Rate').read()
start = ratestring.find("=") + 1
end = ratestring.find(" Mb/s")
wlan0speed = ratestring[start:end]
else:
wlan0speed = "down"
except Exception as e:
logging.info(e)
except: except:
pass pass
@ -242,6 +263,23 @@ class SystemController:
if str(addr.family) == "AddressFamily.AF_INET": if str(addr.family) == "AddressFamily.AF_INET":
if addr.address: if addr.address:
wlan0IP = addr.address wlan0IP = addr.address
info = psutil.net_if_stats()
try:
for nic in info:
if nic == 'Ethernet':
if info[nic].isup == True:
if info[nic].speed:
eth0speed = info[nic].speed
else:
eth0speed = "down"
if nic == 'WLAN':
if info[nic].isup == True:
if info[nic].speed:
wlan0speed = info[nic].speed
else:
wlan0speed = "down"
except Exception as e:
logging.info(e)
except: except:
pass pass
@ -258,7 +296,9 @@ class SystemController:
'temp': temp, 'temp': temp,
'temp_unit': TEMP_UNIT, 'temp_unit': TEMP_UNIT,
'eth0': eth0IP, 'eth0': eth0IP,
'wlan0': wlan0IP} 'wlan0': wlan0IP,
'eth0speed': eth0speed,
'wlan0speed': wlan0speed}
return systeminfo return systeminfo

View file

@ -1,5 +1,10 @@
import asyncio import asyncio
import sys
try:
from asyncio import set_event_loop_policy, WindowsSelectorEventLoopPolicy
except ImportError:
pass
import json import json
from voluptuous.schema_builder import message from voluptuous.schema_builder import message
from cbpi.api.dataclasses import NotificationType from cbpi.api.dataclasses import NotificationType
@ -82,6 +87,11 @@ async def error_middleware(request, handler):
class CraftBeerPi: class CraftBeerPi:
def __init__(self): def __init__(self):
operationsystem= sys.platform
if operationsystem.startswith('win'):
set_event_loop_policy(WindowsSelectorEventLoopPolicy())
self.path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1]) # The path to the package dir self.path = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-1]) # The path to the package dir
self.version = __version__ self.version = __version__

View file

@ -32,6 +32,7 @@ class FermenterNotificationStep(CBPiFermentationStep):
if self.AutoNext == True: if self.AutoNext == True:
self.cbpi.notify(self.name, self.props.get("Notification",""), NotificationType.INFO) self.cbpi.notify(self.name, self.props.get("Notification",""), NotificationType.INFO)
if self.shutdown != True:
await self.next(self.fermenter.id) await self.next(self.fermenter.id)
return StepResult.DONE return StepResult.DONE
else: else:
@ -42,6 +43,7 @@ class FermenterNotificationStep(CBPiFermentationStep):
await self.push_update() await self.push_update()
async def on_start(self): async def on_start(self):
self.shutdown = False
self.summary="" self.summary=""
self.AutoNext = False if self.props.get("AutoNext", "No") == "No" else True self.AutoNext = False if self.props.get("AutoNext", "No") == "No" else True
if self.timer is None: if self.timer is None:
@ -69,13 +71,12 @@ class FermenterNotificationStep(CBPiFermentationStep):
class FermenterTargetTempStep(CBPiFermentationStep): class FermenterTargetTempStep(CBPiFermentationStep):
async def NextStep(self, **kwargs): async def NextStep(self, **kwargs):
if self.shutdown != True:
await self.next(self.fermenter.id) await self.next(self.fermenter.id)
return StepResult.DONE return StepResult.DONE
async def on_timer_done(self,timer): async def on_timer_done(self,timer):
self.summary = "" self.summary = ""
#self.fermenter.target_temp = 0
await self.push_update() await self.push_update()
if self.AutoMode == True: if self.AutoMode == True:
await self.setAutoMode(False) await self.setAutoMode(False)
@ -88,10 +89,10 @@ class FermenterTargetTempStep(CBPiFermentationStep):
await self.push_update() await self.push_update()
async def on_start(self): async def on_start(self):
self.shutdown = False
self.AutoMode = True if self.props.get("AutoMode","No") == "Yes" else False self.AutoMode = True if self.props.get("AutoMode","No") == "Yes" else False
self.starttemp= self.get_sensor_value(self.props.get("Sensor", None)).get("value")
if self.fermenter is not None: if self.fermenter is not None:
self.fermenter.target_temp = int(self.props.get("Temp", 0)) self.fermenter.target_temp = float(self.props.get("Temp", 0))
if self.AutoMode == True: if self.AutoMode == True:
await self.setAutoMode(True) await self.setAutoMode(True)
self.summary = "Waiting for Target Temp" self.summary = "Waiting for Target Temp"
@ -107,6 +108,9 @@ class FermenterTargetTempStep(CBPiFermentationStep):
await self.push_update() await self.push_update()
async def run(self): async def run(self):
while self.get_sensor_value(self.props.get("Sensor", None)).get("value") > 900:
await asyncio.sleep(1)
self.starttemp= self.get_sensor_value(self.props.get("Sensor", None)).get("value")
if self.fermenter.target_temp >= self.starttemp: if self.fermenter.target_temp >= self.starttemp:
logging.info("warmup") logging.info("warmup")
while self.running == True: while self.running == True:
@ -128,6 +132,7 @@ class FermenterTargetTempStep(CBPiFermentationStep):
async def reset(self): async def reset(self):
self.timer = Timer(1 ,on_update=self.on_timer_update, on_done=self.on_timer_done) self.timer = Timer(1 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
self.timer.is_running == False
async def setAutoMode(self, auto_state): async def setAutoMode(self, auto_state):
try: try:
@ -162,13 +167,15 @@ class FermenterStep(CBPiFermentationStep):
else: else:
self.cbpi.notify(self.name, 'Timer is already running', NotificationType.WARNING) self.cbpi.notify(self.name, 'Timer is already running', NotificationType.WARNING)
#@action("Add 5 Minutes to Timer", []) # @action("Add 1 Day to Timer", [])
#async def add_timer(self): # async def add_timer(self):
# if self.timer.is_running == True: # if self.timer.is_running == True:
# self.cbpi.notify(self.name, '5 Minutes added', NotificationType.INFO) # self.cbpi.notify(self.name, '1 Day added', NotificationType.INFO)
# await self.timer.add(300) # await self.timer.add(86400)
# else: # self.endtime = self.endtime +86400
# self.cbpi.notify(self.name, 'Timer must be running to add time', NotificationType.WARNING) # await self.update_endtime()
# else:
# self.cbpi.notify(self.name, 'Timer must be running to add time', NotificationType.WARNING)
async def on_timer_done(self,timer): async def on_timer_done(self,timer):
@ -176,6 +183,7 @@ class FermenterStep(CBPiFermentationStep):
if self.AutoMode == True: if self.AutoMode == True:
await self.setAutoMode(False) await self.setAutoMode(False)
self.cbpi.notify(self.name, 'Step finished', NotificationType.SUCCESS) self.cbpi.notify(self.name, 'Step finished', NotificationType.SUCCESS)
if self.shutdown != True:
await self.next(self.fermenter.id) await self.next(self.fermenter.id)
return StepResult.DONE return StepResult.DONE
@ -185,6 +193,7 @@ class FermenterStep(CBPiFermentationStep):
await self.push_update() await self.push_update()
async def on_start(self): async def on_start(self):
self.shutdown = False
if self.endtime == 0: if self.endtime == 0:
timeD=int(self.props.get("TimerD", 0)) timeD=int(self.props.get("TimerD", 0))
timeH=int(self.props.get("TimerH", 0)) timeH=int(self.props.get("TimerH", 0))
@ -194,9 +203,8 @@ class FermenterStep(CBPiFermentationStep):
self.fermentationtime = self.endtime - time.time() self.fermentationtime = self.endtime - time.time()
self.AutoMode = True if self.props.get("AutoMode", "No") == "Yes" else False self.AutoMode = True if self.props.get("AutoMode", "No") == "Yes" else False
self.starttemp= self.get_sensor_value(self.props.get("Sensor", None)).get("value")
if self.fermenter is not None: if self.fermenter is not None:
self.fermenter.target_temp = int(self.props.get("Temp", 0)) self.fermenter.target_temp = float(self.props.get("Temp", 0))
if self.AutoMode == True: if self.AutoMode == True:
await self.setAutoMode(True) await self.setAutoMode(True)
await self.push_update() await self.push_update()
@ -217,6 +225,9 @@ class FermenterStep(CBPiFermentationStep):
if self.endtime != 0 and self.timer is not None and self.timer.is_running == False: if self.endtime != 0 and self.timer is not None and self.timer.is_running == False:
self.timer.start() self.timer.start()
self.timer.is_running = True self.timer.is_running = True
estimated_completion_time = datetime.fromtimestamp(time.time()+ self.fermentationtime)
self.cbpi.notify(self.name, 'Timer restarted. Estimated completion: {}'.format(estimated_completion_time.strftime("%d.%m, %H:%M")), NotificationType.INFO)
self.summary = "Waiting for Target Temp" self.summary = "Waiting for Target Temp"
await self.push_update() await self.push_update()
@ -229,17 +240,19 @@ class FermenterStep(CBPiFermentationStep):
await self.push_update() await self.push_update()
async def reset(self): async def reset(self):
await self.timer.stop()
timeD=int(self.props.get("TimerD", 0)) timeD=int(self.props.get("TimerD", 0))
timeH=int(self.props.get("TimerH", 0)) timeH=int(self.props.get("TimerH", 0))
timeM=int(self.props.get("TimerM", 0)) timeM=int(self.props.get("TimerM", 0))
self.fermentationtime=(timeM+(60*timeH)+(1440*timeD)) *60 self.fermentationtime=(timeM+(60*timeH)+(1440*timeD)) *60
self.timer = Timer(self.fermentationtime ,on_update=self.on_timer_update, on_done=self.on_timer_done) self.timer = Timer(self.fermentationtime ,on_update=self.on_timer_update, on_done=self.on_timer_done)
self.endtime = 0 self.endtime = 0
await self.update_endtime() self.timer.is_running == False
async def run(self): async def run(self):
while self.get_sensor_value(self.props.get("Sensor", None)).get("value") > 900:
await asyncio.sleep(1)
self.starttemp= self.get_sensor_value(self.props.get("Sensor", None)).get("value")
if self.fermenter.target_temp >= self.starttemp: if self.fermenter.target_temp >= self.starttemp:
logging.info("warmup") logging.info("warmup")
while self.running == True: while self.running == True:
@ -278,43 +291,6 @@ class FermenterStep(CBPiFermentationStep):
except Exception as e: except Exception as e:
logging.error("Failed to switch on FermenterLogic {} {}".format(self.fermenter.id, e)) logging.error("Failed to switch on FermenterLogic {} {}".format(self.fermenter.id, e))
@parameters([Property.Number(label="TimerD", description="Timer Days", configurable=True),
Property.Number(label="TimerH", description="Timer Hours", configurable=True),
Property.Number(label="TimerM", description="Timer Minutes", configurable=True)
])
class FermenterWaitStep(CBPiFermentationStep):
async def on_timer_done(self, timer):
self.summary = ""
await self.next(self.fermenter.id)
return StepResult.DONE
async def on_timer_update(self, timer, seconds):
self.summary = Timer.format_time(seconds)
await self.push_update()
async def on_start(self):
timeD=int(self.props.get("TimerD", 0))
timeH=int(self.props.get("TimerH", 0))
timeM=int(self.props.get("TimerM", 0))
self.fermentationtime=(timeM+(60*timeH)+(1440*timeD)) *60
if self.timer is None:
self.timer = Timer(self.fermentationtime, on_update=self.on_timer_update, on_done=self.on_timer_done)
self.timer.start()
async def on_stop(self):
await self.timer.stop()
self.summary = ""
await self.push_update()
async def reset(self):
self.timer = Timer(self.fermentationtime, on_update=self.on_timer_update, on_done=self.on_timer_done)
async def run(self):
while self.running == True:
await asyncio.sleep(1)
return StepResult.DONE
def setup(cbpi): def setup(cbpi):
''' '''
@ -328,4 +304,3 @@ def setup(cbpi):
cbpi.plugin.register("FermenterNotificationStep", FermenterNotificationStep) cbpi.plugin.register("FermenterNotificationStep", FermenterNotificationStep)
cbpi.plugin.register("FermenterTargetTempStep", FermenterTargetTempStep) cbpi.plugin.register("FermenterTargetTempStep", FermenterTargetTempStep)
cbpi.plugin.register("FermenterStep", FermenterStep) cbpi.plugin.register("FermenterStep", FermenterStep)
#cbpi.plugin.register("FermenterWaitStep", FermenterWaitStep)

View file

@ -33,7 +33,7 @@ class FermenterAutostart(CBPiExtension):
try: try:
if (self.fermenter.instance is None or self.fermenter.instance.state == False): if (self.fermenter.instance is None or self.fermenter.instance.state == False):
await self.cbpi.fermenter.start(self.fermenter.id) await self.cbpi.fermenter.start(self.fermenter.id)
logging.info("Successfully switched on Fermenterlogic for Fermenter {}".format(self.fermenter.id)) logging.info("Successfully switched on Ferenterlogic for Fermenter {}".format(self.fermenter.id))
except Exception as e: except Exception as e:
logging.error("Failed to switch on FermenterLogic {} {}".format(self.fermenter.id, e)) logging.error("Failed to switch on FermenterLogic {} {}".format(self.fermenter.id, e))
except: except:

View file

@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
try: try:
import RPi.GPIO as GPIO import RPi.GPIO as GPIO
except Exception: except Exception:
logger.warning("Failed to load RPi.GPIO. Using Mock") logger.warning("Failed to load RPi.GPIO. Using Mock instead")
MockRPi = MagicMock() MockRPi = MagicMock()
modules = { modules = {
"RPi": MockRPi, "RPi": MockRPi,

View file

@ -22,6 +22,8 @@ class MQTTActor(CBPiActor):
async def on_start(self): async def on_start(self):
self.topic = self.props.get("Topic", None) self.topic = self.props.get("Topic", None)
self.power = 100 self.power = 100
await self.off()
self.state = False
async def on(self, power=None): async def on(self, power=None):
if power is not None: if power is not None:

View file

@ -18,7 +18,7 @@ class MQTTSensor(CBPiSensor):
if self.payload_text != None: if self.payload_text != None:
self.payload_text = self.payload_text.split('.') self.payload_text = self.payload_text.split('.')
self.mqtt_task = self.cbpi.satellite.subcribe(self.Topic, self.on_message) self.mqtt_task = self.cbpi.satellite.subcribe(self.Topic, self.on_message)
self.value: int = 0 self.value: float = 999
async def on_message(self, message): async def on_message(self, message):
val = json.loads(message) val = json.loads(message)

View file

@ -313,8 +313,8 @@ class FermentationHttpEndpoints():
data = await request.json() data = await request.json()
fermenterid= request.match_info['id'] fermenterid= request.match_info['id']
newstep = {"name": data.get("name"), "props": data.get("props", {}), "type": data.get("type")} newstep = {"name": data.get("name"), "props": data.get("props", {}), "endtime": 0, "type": data.get("type")}
response_data = await self.controller.create_step(fermenterid,newstep) response_data = await self.controller.add_step(fermenterid,newstep)
return web.json_response(data=response_data.to_dict()) return web.json_response(data=response_data.to_dict())
@request_mapping(path="/{fermenterid}/{stepid}", method="PUT", auth_required=False) @request_mapping(path="/{fermenterid}/{stepid}", method="PUT", auth_required=False)
@ -352,7 +352,7 @@ class FermentationHttpEndpoints():
data = await request.json() data = await request.json()
stepid = request.match_info['stepid'] stepid = request.match_info['stepid']
fermenterid = request.match_info['fermenterid'] fermenterid = request.match_info['fermenterid']
updatedstep = {"id": stepid, "name": data.get("name"), "props": data.get("props", {}), "type": data.get("type")} updatedstep = {"id": stepid, "name": data.get("name"), "endtime": 0, "props": data.get("props", {}), "type": data.get("type")}
#step = FermenterStep(stepid, data.get("name"), None, Props(data.get("props", {})), data.get("type")) #step = FermenterStep(stepid, data.get("name"), None, Props(data.get("props", {})), data.get("type"))
await self.controller.update_step(fermenterid,updatedstep) await self.controller.update_step(fermenterid,updatedstep)
return web.Response(status=200) return web.Response(status=200)

View file

@ -117,7 +117,7 @@ class FermenterRecipeHttpEndpoints():
await self.controller.remove(name) await self.controller.remove(name)
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/{name}/{fermenterid}/brew", method="POST", auth_required=False) @request_mapping(path="/{recipeid}/{fermenterid}/{name}/brew", method="POST", auth_required=False)
async def http_brew(self, request): async def http_brew(self, request):
""" """
@ -137,9 +137,10 @@ class FermenterRecipeHttpEndpoints():
"200": "200":
description: successful operation description: successful operation
""" """
recipeid = request.match_info['recipeid']
name = request.match_info['name'] name = request.match_info['name']
fermenterid = request.match_info['fermenterid'] fermenterid = request.match_info['fermenterid']
await self.controller.brew(name,fermenterid) await self.controller.brew(recipeid,fermenterid,name)
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/{id}/clone", method="POST", auth_required=False) @request_mapping(path="/{id}/clone", method="POST", auth_required=False)

View file

@ -1,24 +1,24 @@
aiohttp==3.7.4 aiohttp==3.8.1
aiohttp-auth==0.1.1 aiohttp-auth==0.1.1
aiohttp-route-decorator==0.1.4 aiohttp-route-decorator==0.1.4
aiohttp-security==0.4.0 aiohttp-security==0.4.0
aiohttp-session==2.9.0 aiohttp-session==2.11.0
aiohttp-swagger==1.0.15 aiohttp-swagger==1.0.16
aiojobs==0.3.0 aiojobs==1.0.0
aiosqlite==0.16.0 aiosqlite==0.17.0
cryptography==3.3.2 cryptography==36.0.1
requests==2.25.1 requests==2.27.1
voluptuous==0.12.1 voluptuous==0.12.2
pyfiglet==0.8.post1 pyfiglet==0.8.post1
pandas==1.4.0 pandas==1.4.1
shortuuid==1.0.1 shortuuid==1.0.8
tabulate==0.8.7 tabulate==0.8.9
numpy==1.22.0 numpy==1.22.2
cbpi4ui cbpi4ui
click==7.1.2 click==8.0.4
importlib_metadata==4.8.2 importlib_metadata==4.11.1
asyncio-mqtt asyncio-mqtt
psutil==5.8.0 psutil==5.9.0
zipp>=0.5 zipp>=0.5
PyInquirer==1.0.3 PyInquirer==1.0.3
colorama==0.4.4 colorama==0.4.4

View file

@ -27,34 +27,33 @@ setup(name='cbpi',
'': ['*.txt', '*.rst', '*.yaml'], '': ['*.txt', '*.rst', '*.yaml'],
'cbpi': ['*','*.txt', '*.rst', '*.yaml']}, 'cbpi': ['*','*.txt', '*.rst', '*.yaml']},
python_requires='>=3', python_requires='>=3.9',
install_requires=[ install_requires=[
"aiohttp==3.7.4", "aiohttp==3.8.1",
"aiohttp-auth==0.1.1", "aiohttp-auth==0.1.1",
"aiohttp-route-decorator==0.1.4", "aiohttp-route-decorator==0.1.4",
"aiohttp-security==0.4.0", "aiohttp-security==0.4.0",
"aiohttp-session==2.9.0", "aiohttp-session==2.11.0",
"aiohttp-swagger==1.0.15", "aiohttp-swagger==1.0.16",
"aiojobs==0.3.0", "aiojobs==1.0.0 ",
"aiosqlite==0.16.0", "aiosqlite==0.17.0",
"cryptography==3.3.2", "cryptography==36.0.1",
"requests==2.25.1", "requests==2.27.1",
"voluptuous==0.12.1", "voluptuous==0.12.2",
"pyfiglet==0.8.post1", "pyfiglet==0.8.post1",
'click==7.1.2', 'click==8.0.4',
'shortuuid==1.0.1', 'shortuuid==1.0.8',
'tabulate==0.8.7', 'tabulate==0.8.9',
'asyncio-mqtt', 'asyncio-mqtt',
'psutil==5.8.0',
'colorama==0.4.4',
'PyInquirer==1.0.3', 'PyInquirer==1.0.3',
'colorama==0.4.4',
'psutil==5.9.0',
'cbpi4ui', 'cbpi4ui',
'importlib_metadata'] + ( 'importlib_metadata',
['RPi.GPIO==0.7.1'] if raspberrypi else [] ) + 'numpy==1.22.2',
(['numpy==1.22.0'] if (int(platform.python_version_tuple()[1]) >= 9) and (int(platform.python_version_tuple()[0]) == 3) else ['numpy==1.20.3'] ) + 'pandas==1.4.1'] + (
(['pandas==1.4.0'] if (int(platform.python_version_tuple()[1]) >= 9) and (int(platform.python_version_tuple()[0]) == 3) else ['pandas==1.1.5'] ), ['RPi.GPIO==0.7.1'] if raspberrypi else [] ),
dependency_links=[ dependency_links=[
'https://testpypi.python.org/pypi', 'https://testpypi.python.org/pypi',