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
@ -162,7 +162,7 @@ class FermenterStep:
props: Props = Props() props: Props = Props()
type: str = None type: str = None
status: StepState = StepState.INITIAL status: StepState = StepState.INITIAL
endtime: int = 0 # endtime if step is active and timer is running endtime: int = 0 # endtime if step is active and timer is running
instance: str = None instance: str = None
step: dict = None step: dict = 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:
@ -190,9 +181,9 @@ class CBPiFermentationStep(CBPiBase):
async def update_endtime(self): async def update_endtime(self):
await self.cbpi.fermenter.update_endtime(self.fermenter.id, self.id, self.endtime) await self.cbpi.fermenter.update_endtime(self.fermenter.id, self.id, self.endtime)
async def save_props(self): async def save_props(self):
self.cbpi.fermenter.save() self.cbpi.fermenter.save()
async def push_update(self): async def push_update(self):
self.cbpi.fermenter.push_update(self.update_key) self.cbpi.fermenter.push_update(self.update_key)

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

@ -15,79 +15,7 @@ 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, 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"))
try:
endtime = int(item.get("endtime", 0))
except:
endtime=0
status = StepState(item.get("status", "I")) status = StepState(item.get("status", "I"))
endtime = int(item.get("endtime", 0))
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.stop()
await step.instance.reset() await step.instance.reset()
await step.instance.stop()
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,15 +504,19 @@ 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))
@ -582,8 +526,7 @@ class FermentationController:
await item.instance.__getattribute__(action)(**parameter) await item.instance.__getattribute__(action)(**parameter)
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']
fermenter.brewname = data['basic']['name'] if name is not None:
fermenter.steps=[] fermenter.brewname = name
else:
fermenter.brewname = data['basic']['name']
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 = {}
@ -68,8 +68,9 @@ class StepController:
self.profile = list(map(lambda item: self.create(item), self.profile)) self.profile = list(map(lambda item: self.create(item), self.profile))
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,12 +227,31 @@ 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
if system == "Windows": if system == "Windows":
try: try:
ethernet = psutil.net_if_addrs() ethernet = psutil.net_if_addrs()
for nic, addrs in ethernet.items(): for nic, addrs in ethernet.items():
if nic == "Ethernet": if nic == "Ethernet":
for addr in addrs: for addr in addrs:
@ -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,8 +32,9 @@ 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)
await self.next(self.fermenter.id) if self.shutdown != True:
return StepResult.DONE await self.next(self.fermenter.id)
return StepResult.DONE
else: else:
self.cbpi.notify(self.name, self.props.get("Notification",""), NotificationType.INFO, action=[NotificationAction("Next Step", self.NextStep)]) self.cbpi.notify(self.name, self.props.get("Notification",""), NotificationType.INFO, action=[NotificationAction("Next Step", self.NextStep)])
await self.push_update() await self.push_update()
@ -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):
await self.next(self.fermenter.id) if self.shutdown != True:
return StepResult.DONE await self.next(self.fermenter.id)
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,8 +183,9 @@ 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)
await self.next(self.fermenter.id) if self.shutdown != True:
return StepResult.DONE await self.next(self.fermenter.id)
return StepResult.DONE
async def on_timer_update(self,timer, seconds): async def on_timer_update(self,timer, seconds):
@ -185,7 +193,8 @@ class FermenterStep(CBPiFermentationStep):
await self.push_update() await self.push_update()
async def on_start(self): async def on_start(self):
if self.endtime == 0: self.shutdown = False
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))
timeM=int(self.props.get("TimerM", 0)) timeM=int(self.props.get("TimerM", 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',