diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index c2bc057..4451d4c 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -104,7 +104,6 @@ class UploadController: async def FileUpload(self, data): fileData = data['File'] - logging.info(fileData) filename = fileData.filename recipe_file = fileData.file content_type = fileData.content_type @@ -141,260 +140,159 @@ class UploadController: self.cbpi.notify("Error", "Wrong content type. Upload failed", NotificationType.ERROR) async def kbh_recipe_creation(self, Recipe_ID): - self.kettle = None - - #Define MashSteps - self.mashin = self.cbpi.config.get("steps_mashin", "MashInStep") - self.mash = self.cbpi.config.get("steps_mash", "MashStep") - self.mashout = self.cbpi.config.get("steps_mashout", None) # Currently used only for the Braumeister - self.boil = self.cbpi.config.get("steps_boil", "BoilStep") - self.whirlpool="Waitstep" - self.cooldown = self.cbpi.config.get("steps_cooldown", "WaitStep") - - #get default boil temp from settings - self.BoilTemp = self.cbpi.config.get("steps_boil_temp", 98) - - #get default cooldown temp alarm setting - self.CoolDownTemp = self.cbpi.config.get("steps_cooldown_temp", 25) - - #get server port from settings and define url for api calls -> adding steps - self.port = str(self.cbpi.static_config.get('port',8000)) - self.url="http://127.0.0.1:" + self.port + "/step2/" - - - # get default Kettle from Settings - self.id = self.cbpi.config.get('MASH_TUN', None) - try: - self.kettle = self.cbpi.kettle.find_by_id(self.id) - except: - self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) - if self.id is not None or self.id != '': + config = self.get_config_values() + if self.kettle is not None: # load beerxml file located in upload folder self.path = os.path.join(".", 'config', "upload", "kbh.db") if os.path.exists(self.path) is False: self.cbpi.notify("File Not Found", "Please upload a kbh V2 databsel file", NotificationType.ERROR) try: + # Get Recipe Nmae conn = sqlite3.connect(self.path) c = conn.cursor() c.execute('SELECT Sudname FROM Sud WHERE ID = ?', (Recipe_ID,)) row = c.fetchone() name = row[0] - # Create recipe in recipe Book with name of first recipe in xml file - self.recipeID = await self.cbpi.recipe.create(name) - - # send recipe to mash profile - await self.cbpi.recipe.brew(self.recipeID) - - # remove empty recipe from recipe book - await self.cbpi.recipe.remove(self.recipeID) - - #MashIn Step + # get MashIn Temp + mashin_temp = None c.execute('SELECT Temp FROM Rasten WHERE Typ = 0 AND SudID = ?', (Recipe_ID,)) row = c.fetchone() + try: + if self.cbpi.config.get("TEMP_UNIT", "C") == "C": + mashin_temp = str(int(row[0])) + else: + mashin_temp = str(round(9.0 / 5.0 * int(row[0]) + 32)) + except: + pass - step_kettle = self.id - step_name = "MashIn" - step_timer = "0" - step_temp = str(int(row[0])) - sensor = self.kettle.sensor - step_type = self.mashin if self.mashin != "" else "MashInStep" - AutoMode = "Yes" if step_type == "MashInStep" else "No" - Notification = "Target temperature reached. Please add malt." - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor, Notification) + # get the hop addition times + c.execute('SELECT Zeit FROM Hopfengaben WHERE Vorderwuerze = 0 AND SudID = ?', (Recipe_ID,)) + hops = c.fetchall() - for row in c.execute('SELECT Name, Temp, Dauer FROM Rasten WHERE Typ <> 0 AND SudID = ?', (Recipe_ID,)): - step_name = str(row[0]) - step_temp = str(int(row[1])) - step_timer = str(int(row[2])) - step_type = self.mash if self.mash != "" else "MashStep" - AutoMode = "Yes" if step_type == "MashStep" else "No" - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor) - - # MashOut -> Notification step that sends notification and waits for user input to move to next step (AutoNext=No) - if self.mashout == "NotificationStep": - step_kettle = self.id - step_type = self.mashout - step_name = "Lautering" - step_timer = "" - step_temp = "" - AutoMode = "" - sensor = "" - Notification = "Mash Process completed. Please start lautering and press next to start boil." - AutoNext = "No" - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor, Notification, AutoNext) + # get the misc addition times + c.execute('SELECT Zugabedauer FROM WeitereZutatenGaben WHERE Zeitpunkt = 1 AND SudID = ?', (Recipe_ID,)) + miscs = c.fetchall() + try: + c.execute('SELECT Zeit FROM Hopfengaben WHERE Vorderwuerze = 1 AND SudID = ?', (Recipe_ID,)) + FW_Hops = c.fetchall() + FirstWort = self.getFirstWort(FW_Hops,"kbh") + except: + FirstWort = "No" c.execute('SELECT Kochdauer FROM Sud WHERE ID = ?', (Recipe_ID,)) row = c.fetchone() - step_time = str(int(row[0])) + BoilTime = str(int(row[0])) - logging.info("Boil Time: {}".format(step_time)) - FirstWortFlag = self.getFirstWortKBH(Recipe_ID) - logging.info(FirstWortFlag) + await self.create_recipe(name) - BoilTimeAlerts = self.getBoilAlertsKBH(Recipe_ID) + if mashin_temp is not None: + step_type = self.mashin if self.mashin != "" else "MashInStep" + step_string = { "name": "MashIn", + "props": { + "AutoMode": "Yes" if step_type == "MashInStep" else "No", + "Kettle": self.id, + "Sensor": self.kettle.sensor, + "Temp": mashin_temp, + "Timer": "0", + "Notification": "Target temperature reached. Please add malt." + }, + "status_text": "", + "status": "I", + "type": step_type + } + await self.create_step(step_string) - logging.info(BoilTimeAlerts) + for row in c.execute('SELECT Name, Temp, Dauer FROM Rasten WHERE Typ <> 0 AND SudID = ?', (Recipe_ID,)): + step_type = self.mash if self.mash != "" else "MashStep" + step_string = { "name": str(row[0]), + "props": { + "AutoMode": "Yes" if step_type == "MashStep" else "No", + "Kettle": self.id, + "Sensor": self.kettle.sensor, + "Temp": str(int(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * int(row[1]) + 32)), + "Timer": str(int(row[2])) + }, + "status_text": "", + "status": "I", + "type": step_type + } + await self.create_step(step_string) - step_kettle = self.id + # MashOut -> Notification step that sends notification and waits for user input to move to next step (AutoNext=No) + if self.mashout == "NotificationStep": + step_string = { "name": "Lautering", + "props": { + "AutoNext": "No", + "Kettle": self.id, + "Notification": "Mash Process completed. Please start lautering and press next to start boil." + + }, + "status_text": "", + "status": "I", + "type": self.mashout + } + await self.create_step(step_string) + + + Hops = self.getBoilAlerts(hops, miscs, "kbh") step_type = self.boil if self.boil != "" else "BoilStep" - step_name = "Boil Step" - step_temp = int(self.BoilTemp) - AutoMode = "Yes" if step_type == "BoilStep" else "No" - sensor = self.kettle.sensor - Notification = "" - AutoNext = "" - LidAlert = "Yes" - FirstWort = 'Yes' if FirstWortFlag == True else 'No' - Hop1 = str(int(BoilTimeAlerts[0])) if len(BoilTimeAlerts) >= 1 else None - Hop2 = str(int(BoilTimeAlerts[1])) if len(BoilTimeAlerts) >= 2 else None - Hop3 = str(int(BoilTimeAlerts[2])) if len(BoilTimeAlerts) >= 3 else None - Hop4 = str(int(BoilTimeAlerts[3])) if len(BoilTimeAlerts) >= 4 else None - Hop5 = str(int(BoilTimeAlerts[4])) if len(BoilTimeAlerts) >= 5 else None - Hop6 = str(int(BoilTimeAlerts[5])) if len(BoilTimeAlerts) >= 6 else None + step_string = { "name": "Boil Step", + "props": { + "AutoMode": "Yes" if step_type == "BoilStep" else "No", + "Kettle": self.id, + "Sensor": self.kettle.sensor, + "Temp": int(self.BoilTemp), + "Timer": BoilTime, + "First_Wort": FirstWort, + "LidAlert": "Yes", + "Hop_1": Hops[0], + "Hop_2": Hops[1], + "Hop_3": Hops[2], + "Hop_4": Hops[3], + "Hop_5": Hops[4], + "Hop_6": Hops[5] + }, + "status_text": "", + "status": "I", + "type": step_type + } + await self.create_step(step_string) - - await self.create_step(step_type, step_name, step_kettle, step_time, step_temp, AutoMode, sensor, Notification, AutoNext, LidAlert, FirstWort, Hop1, Hop2, Hop3, Hop4, Hop5, Hop6) + await self.create_Whirlpool_Cooldown() - # Add Waitstep as Whirlpool - if self.cooldown != "WaiStep" and self.cooldown !="": - step_type = "WaitStep" - step_name = "Whirlpool" - cooldown_sensor = "" - step_timer = "15" - step_temp = "" - AutoMode = "" - - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, cooldown_sensor) - - # CoolDown step is sending a notification when cooldowntemp is reached - step_type = self.cooldown if self.cooldown != "" else "WaitStep" - step_name = "CoolDown" - cooldown_sensor = "" - step_timer = "15" - step_temp = "" - AutoMode = "" - if step_type == "CooldownStep": - cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None) - if cooldown_sensor is None or cooldown_sensor == '': - cooldown_sensor = self.kettle.sensor # fall back to kettle sensor if no other sensor is specified - step_kettle = self.id - step_timer = "" - step_temp = int(self.CoolDownTemp) - - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, cooldown_sensor) + self.cbpi.notify('KBH Recipe created', name, NotificationType.INFO) except: + self.cbpi.notify('KBH Recipe creation failure', name, NotificationType.ERROR) pass - - self.cbpi.notify('KBH Recipe created', name, NotificationType.INFO) - - def getFirstWortKBH(self, id): - alert = False - try: - conn = sqlite3.connect(self.path) - c = conn.cursor() - c.execute('SELECT Zeit FROM Hopfengaben WHERE Vorderwuerze = 1 AND SudID = ?', (id,)) - row = c.fetchall() - if len(row) != 0: - alert = True - except Exception as e: - self.cbpi.notify("Failed to create Recipe", e.message, NotificationType.ERROR) - return ('', 500) - finally: - if conn: - conn.close() - - return alert - - - def getBoilAlertsKBH(self, id): - alerts = [] - try: - conn = sqlite3.connect(self.path) - c = conn.cursor() - # get the hop addition times - c.execute('SELECT Zeit FROM Hopfengaben WHERE Vorderwuerze = 0 AND SudID = ?', (id,)) - rows = c.fetchall() - - for row in rows: - alerts.append(float(row[0])) - - # get any misc additions if available - c.execute('SELECT Zugabedauer FROM WeitereZutatenGaben WHERE Zeitpunkt = 1 AND SudID = ?', (id,)) - rows = c.fetchall() - - for row in rows: - alerts.append(float(row[0])) - - ## Dedupe and order the additions by their time, to prevent multiple alerts at the same time - alerts = sorted(list(set(alerts))) - - ## CBP should have these additions in reverse - alerts.reverse() - - except Exception as e: - self.cbpi.notify("Failed to create Recipe", e.message, NotificationType.ERROR) - return ('', 500) - finally: - if conn: - conn.close() - - return alerts + else: + self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) async def xml_recipe_creation(self, Recipe_ID): - self.kettle = None + config = self.get_config_values() - #Define MashSteps - self.mashin = self.cbpi.config.get("steps_mashin", "MashStep") - self.mash = self.cbpi.config.get("steps_mash", "MashStep") - self.mashout = self.cbpi.config.get("steps_mashout", None) # Currently used only for the Braumeister - self.boil = self.cbpi.config.get("steps_boil", "BoilStep") - self.cooldown = self.cbpi.config.get("steps_cooldown", "WaitStep") - - #get default boil temp from settings - self.BoilTemp = self.cbpi.config.get("steps_boil_temp", 98) - - #get default cooldown temp alarm setting - self.CoolDownTemp = self.cbpi.config.get("steps_cooldown_temp", 25) - - #get server port from settings and define url for api calls -> adding steps - self.port = str(self.cbpi.static_config.get('port',8000)) - self.url="http://127.0.0.1:" + self.port + "/step2/" - - - # get default Kettle from Settings - self.id = self.cbpi.config.get('MASH_TUN', None) - try: - self.kettle = self.cbpi.kettle.find_by_id(self.id) - except: - self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) - if self.id is not None or self.id != '': + if self.kettle is not None: # load beerxml file located in upload folder self.path = os.path.join(".", 'config', "upload", "beer.xml") if os.path.exists(self.path) is False: self.cbpi.notify("File Not Found", "Please upload a Beer.xml File", NotificationType.ERROR) + e = xml.etree.ElementTree.parse(self.path).getroot() + recipe = e.find('./RECIPE[%s]' % (str(Recipe_ID))) + hops = recipe.findall('./HOPS/HOP') + miscs = recipe.findall('MISCS/MISC[USE="Boil"]') + name = e.find('./RECIPE[%s]/NAME' % (str(Recipe_ID))).text + boil_time = float(e.find('./RECIPE[%s]/BOIL_TIME' % (str(Recipe_ID))).text) + FirstWort= self.getFirstWort(hops, "xml") - result = [] - for idx, val in enumerate(e.findall('RECIPE')): - result.append(val.find("NAME").text) - - # Create recipe in recipe Book with name of first recipe in xml file - self.recipeID = await self.cbpi.recipe.create(self.getRecipeName(Recipe_ID)) - - # send recipe to mash profile - await self.cbpi.recipe.brew(self.recipeID) - - # remove empty recipe from recipe book - await self.cbpi.recipe.remove(self.recipeID) - + await self.create_recipe(name) # Mash Steps -> first step is different as it heats up to defined temp and stops with notification to add malt # AutoMode is yes to start and stop automatic mode or each step MashIn_Flag = True @@ -406,135 +304,82 @@ class UploadController: sensor = self.kettle.sensor if MashIn_Flag == True and row.get("timer") == 0: step_type = self.mashin if self.mashin != "" else "MashInStep" - AutoMode = "Yes" if step_type == "MashInStep" else "No" Notification = "Target temperature reached. Please add malt." MashIn_Flag = False else: step_type = self.mash if self.mash != "" else "MashStep" - AutoMode = "Yes" if step_type == "MashStep" else "No" Notification = "" - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor, Notification) + step_string = { "name": step_name, + "props": { + "AutoMode": "Yes" if step_type == "MashInStep" else "No", + "Kettle": self.id, + "Sensor": self.kettle.sensor, + "Temp": step_temp, + "Timer": step_timer, + "Notification": Notification + }, + "status_text": "", + "status": "I", + "type": step_type + } + + await self.create_step(step_string) # MashOut -> Simple step that sends notification and waits for user input to move to next step (AutoNext=No) if self.mashout == "NotificationStep": - step_kettle = self.id - step_type = self.mashout - step_name = "Lautering" - step_timer = "" - step_temp = "" - AutoMode = "" - sensor = "" - Notification = "Mash Process completed. Please start lautering and press next to start boil." - AutoNext = "No" - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor, Notification, AutoNext) - + step_string = { "name": "Lautering", + "props": { + "AutoNext": "No", + "Kettle": self.id, + "Notification": "Mash Process completed. Please start lautering and press next to start boil." + }, + "status_text": "", + "status": "I", + "type": self.mashout + } + await self.create_step(step_string) + # Boil step including hop alarms and alarm for first wort hops -> Automode is set tu yes - FirstWortFlag = len(str(self.getFirstWortAlert(Recipe_ID))) - self.BoilTimeAlerts = self.getBoilAlerts(Recipe_ID) - + Hops = self.getBoilAlerts(hops, miscs, "xml") step_kettle = self.id - step_time = str(int(self.getBoilTime(Recipe_ID))) step_type = self.boil if self.boil != "" else "BoilStep" - step_name = "Boil Step" + step_time = str(int(boil_time)) step_temp = self.BoilTemp - AutoMode = "Yes" if step_type == "BoilStep" else "No" sensor = self.kettle.sensor - Notification = "" - AutoNext = "" LidAlert = "Yes" - FirstWort = 'Yes' if FirstWortFlag != 0 else 'No' - Hop1 = str(int(self.BoilTimeAlerts[0])) if len(self.BoilTimeAlerts) >= 1 else None - Hop2 = str(int(self.BoilTimeAlerts[1])) if len(self.BoilTimeAlerts) >= 2 else None - Hop3 = str(int(self.BoilTimeAlerts[2])) if len(self.BoilTimeAlerts) >= 3 else None - Hop4 = str(int(self.BoilTimeAlerts[3])) if len(self.BoilTimeAlerts) >= 4 else None - Hop5 = str(int(self.BoilTimeAlerts[4])) if len(self.BoilTimeAlerts) >= 5 else None - Hop6 = str(int(self.BoilTimeAlerts[5])) if len(self.BoilTimeAlerts) >= 6 else None + + step_string = { "name": "Boil Step", + "props": { + "AutoMode": "Yes" if step_type == "BoilStep" else "No", + "Kettle": step_kettle, + "Sensor": sensor, + "Temp": step_temp, + "Timer": step_time, + "First_Wort": FirstWort, + "LidAlert": LidAlert, + "Hop_1": Hops[0], + "Hop_2": Hops[1], + "Hop_3": Hops[2], + "Hop_4": Hops[3], + "Hop_5": Hops[4], + "Hop_6": Hops[5] + }, + "status_text": "", + "status": "I", + "type": step_type + } + + await self.create_step(step_string) + + await self.create_Whirlpool_Cooldown() + + self.cbpi.notify('BeerXML Recipe created ', name, NotificationType.INFO) + else: + self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) - await self.create_step(step_type, step_name, step_kettle, step_time, step_temp, AutoMode, sensor, Notification, AutoNext, LidAlert, FirstWort, Hop1, Hop2, Hop3, Hop4, Hop5, Hop6) - - # Add Waitstep as Whirlpool - if self.cooldown != "WaiStep" and self.cooldown !="": - step_type = "WaitStep" - step_name = "Whirlpool" - cooldown_sensor = "" - step_timer = "15" - step_temp = "" - AutoMode = "" - - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, cooldown_sensor) - - # CoolDown step is sending a notification when cooldowntemp is reached - step_type = self.cooldown if self.cooldown != "" else "WaitStep" - step_name = "CoolDown" - cooldown_sensor = "" - step_timer = "15" - step_temp = "" - AutoMode = "" - if step_type == "CooldownStep": - cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None) - if cooldown_sensor is None or cooldown_sensor == '': - cooldown_sensor = self.kettle.sensor # fall back to kettle sensor if no other sensor is specified - step_kettle = self.id - step_timer = "" - step_temp = self.CoolDownTemp - - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, cooldown_sensor) - - self.cbpi.notify('BeerXML Recipe created ', result, NotificationType.INFO) - - # XML functions to retrieve xml repice parameters (if multiple recipes are stored in one xml file, id could be used) - - def getRecipeName(self, id): - e = xml.etree.ElementTree.parse(self.path).getroot() - return e.find('./RECIPE[%s]/NAME' % (str(id))).text - - def getBoilTime(self, id): - e = xml.etree.ElementTree.parse(self.path).getroot() - return float(e.find('./RECIPE[%s]/BOIL_TIME' % (str(id))).text) - - def getBoilAlerts(self, id): - e = xml.etree.ElementTree.parse(self.path).getroot() - - recipe = e.find('./RECIPE[%s]' % (str(id))) - alerts = [] - for e in recipe.findall('./HOPS/HOP'): - use = e.find('USE').text - ## Hops which are not used in the boil step should not cause alerts - if use != 'Aroma' and use != 'Boil': - continue - - alerts.append(float(e.find('TIME').text)) - ## There might also be miscelaneous additions during boild time - for e in recipe.findall('MISCS/MISC[USE="Boil"]'): - alerts.append(float(e.find('TIME').text)) - - ## Dedupe and order the additions by their time, to prevent multiple alerts at the same time - alerts = sorted(list(set(alerts))) - ## CBP should have these additions in reverse - alerts.reverse() - - return alerts - - def getFirstWortAlert(self, id): - e = xml.etree.ElementTree.parse(self.path).getroot() - recipe = e.find('./RECIPE[%s]' % (str(id))) - alerts = [] - for e in recipe.findall('./HOPS/HOP'): - use = e.find('USE').text - ## Hops which are not used in the boil step should not cause alerts - if use != 'First Wort': - continue - - alerts.append(float(e.find('TIME').text)) - - ## Dedupe and order the additions by their time, to prevent multiple alerts at the same time - alerts = sorted(list(set(alerts))) - ## CBP should have these additions in reverse - alerts.reverse() - - return alerts + # XML functions to retrieve xml repice parameters (if multiple recipes are stored in one xml file, id could be used) def getSteps(self, id): e = xml.etree.ElementTree.parse(self.path).getroot() @@ -549,34 +394,9 @@ class UploadController: return steps async def bf_recipe_creation(self, Recipe_ID): - self.kettle = None + config = self.get_config_values() - #Define MashSteps - self.mashin = self.cbpi.config.get("steps_mashin", "MashStep") - self.mash = self.cbpi.config.get("steps_mash", "MashStep") - self.mashout = self.cbpi.config.get("steps_mashout", None) # Currently used only for the Braumeister - self.boil = self.cbpi.config.get("steps_boil", "BoilStep") - self.cooldown = self.cbpi.config.get("steps_cooldown", "WaitStep") - - #get default boil temp from settings - self.BoilTemp = self.cbpi.config.get("steps_boil_temp", 98) - - #get default cooldown temp alarm setting - self.CoolDownTemp = self.cbpi.config.get("steps_cooldown_temp", 25) - - #get server port from settings and define url for api calls -> adding steps - self.port = str(self.cbpi.static_config.get('port',8000)) - self.url="http://127.0.0.1:" + self.port + "/step2/" - - self.TEMP_UNIT=self.cbpi.config.get("TEMP_UNIT", "C") - - # get default Kettle from Settings - self.id = self.cbpi.config.get('MASH_TUN', None) - try: - self.kettle = self.cbpi.kettle.find_by_id(self.id) - except: - self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) - if self.id is not None or self.id != '': + if self.kettle is not None: brewfather = True result=[] @@ -609,19 +429,9 @@ class UploadController: except: miscs = None - FirstWort = "No" - for hop in hops: - if hop['use'] == "First Wort": - FirstWort="Yes" + FirstWort = self.getFirstWort(hops, "bf") - # Create recipe in recipe Book with name of first recipe in xml file - self.recipeID = await self.cbpi.recipe.create(RecipeName) - - # send recipe to mash profile - await self.cbpi.recipe.brew(self.recipeID) - - # remove empty recipe from recipe book - await self.cbpi.recipe.remove(self.recipeID) + await self.create_recipe(RecipeName) # Mash Steps -> first step is different as it heats up to defined temp and stops with notification to add malt # AutoMode is yes to start and stop automatic mode or each step @@ -646,126 +456,221 @@ class UploadController: step_type = self.mash if self.mash != "" else "MashStep" AutoMode = "Yes" if step_type == "MashStep" else "No" Notification = "" - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor, Notification) + + step_string = { "name": step_name, + "props": { + "AutoMode": "Yes" if step_type == "MashInStep" else "No", + "Kettle": self.id, + "Sensor": self.kettle.sensor, + "Temp": step_temp, + "Timer": step_timer, + "Notification": Notification + }, + "status_text": "", + "status": "I", + "type": step_type + } + + await self.create_step(step_string) # MashOut -> Simple step that sends notification and waits for user input to move to next step (AutoNext=No) + if self.mashout == "NotificationStep": - step_kettle = self.id - step_type = self.mashout - step_name = "Lautering" - step_timer = "" - step_temp = "" - AutoMode = "" - sensor = "" - Notification = "Mash Process completed. Please start lautering and press next to start boil." - AutoNext = "No" - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, sensor, Notification, AutoNext) + step_string = { "name": "Lautering", + "props": { + "AutoNext": "No", + "Kettle": self.id, + "Notification": "Mash Process completed. Please start lautering and press next to start boil." + }, + "status_text": "", + "status": "I", + "type": self.mashout + } + await self.create_step(step_string) # Boil step including hop alarms and alarm for first wort hops -> Automode is set tu yes - self.BoilTimeAlerts = self.getBoilAlertsBF(hops,miscs) + Hops = self.getBoilAlerts(hops , miscs, "bf") step_kettle = self.id step_time = str(int(BoilTime)) step_type = self.boil if self.boil != "" else "BoilStep" - step_name = "Boil Step" step_temp = self.BoilTemp - AutoMode = "Yes" if step_type == "BoilStep" else "No" sensor = self.kettle.sensor - Notification = "" - AutoNext = "" LidAlert = "Yes" - Hop1 = str(int(self.BoilTimeAlerts[0])) if len(self.BoilTimeAlerts) >= 1 else None - Hop2 = str(int(self.BoilTimeAlerts[1])) if len(self.BoilTimeAlerts) >= 2 else None - Hop3 = str(int(self.BoilTimeAlerts[2])) if len(self.BoilTimeAlerts) >= 3 else None - Hop4 = str(int(self.BoilTimeAlerts[3])) if len(self.BoilTimeAlerts) >= 4 else None - Hop5 = str(int(self.BoilTimeAlerts[4])) if len(self.BoilTimeAlerts) >= 5 else None - Hop6 = str(int(self.BoilTimeAlerts[5])) if len(self.BoilTimeAlerts) >= 6 else None + step_string = { "name": "Boil Step", + "props": { + "AutoMode": "Yes" if step_type == "BoilStep" else "No", + "Kettle": step_kettle, + "Sensor": sensor, + "Temp": step_temp, + "Timer": step_time, + "First_Wort": FirstWort, + "LidAlert": LidAlert, + "Hop_1": Hops[0], + "Hop_2": Hops[1], + "Hop_3": Hops[2], + "Hop_4": Hops[3], + "Hop_5": Hops[4], + "Hop_6": Hops[5] + }, + "status_text": "", + "status": "I", + "type": step_type + } - await self.create_step(step_type, step_name, step_kettle, step_time, step_temp, AutoMode, sensor, Notification, AutoNext, LidAlert, FirstWort, Hop1, Hop2, Hop3, Hop4, Hop5, Hop6) + await self.create_step(step_string) - - # Add Waitstep as Whirlpool - if self.cooldown != "WaiStep" and self.cooldown !="": - step_type = "WaitStep" - step_name = "Whirlpool" - cooldown_sensor = "" - step_timer = "15" - step_temp = "" - AutoMode = "" - - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, cooldown_sensor) - - # CoolDown step is sending a notification when cooldowntemp is reached - step_type = self.cooldown if self.cooldown != "" else "WaitStep" - step_name = "CoolDown" - cooldown_sensor = "" - step_timer = "15" - step_temp = "" - AutoMode = "" - if step_type == "CooldownStep": - cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None) - if cooldown_sensor is None or cooldown_sensor == '': - cooldown_sensor = self.kettle.sensor # fall back to kettle sensor if no other sensor is specified - step_kettle = self.id - step_timer = "" - step_temp = self.CoolDownTemp - - await self.create_step(step_type, step_name, step_kettle, step_timer, step_temp, AutoMode, cooldown_sensor) + await self.create_Whirlpool_Cooldown() self.cbpi.notify('Brewfather App Recipe created: ', RecipeName, NotificationType.INFO) + else: + self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) - - def getBoilAlertsBF(self, hops, miscs): + def getBoilAlerts(self, hops, miscs, recipe_type): alerts = [] for hop in hops: - use = hop['use'] - ## Hops which are not used in the boil step should not cause alerts - if use != 'Aroma' and use != 'Boil': - continue - - alerts.append(float(hop['time'])) - #There might also be miscelaneous additions during boild time - if miscs is not None: - for misc in miscs: - use = misc['use'] + if recipe_type == "xml": + use = hop.find('USE').text + ## Hops which are not used in the boil step should not cause alerts if use != 'Aroma' and use != 'Boil': continue - - alerts.append(float(misc['time'])) - + alerts.append(float(hop.find('TIME').text)) + elif recipe_type == "bf": + use = hop['use'] + if use != 'Aroma' and use != 'Boil': + continue + alerts.append(float(hop['time'])) + elif recipe_type == "kbh": + alerts.append(float(hop[0])) + ## There might also be miscelaneous additions during boild time + if miscs is not None: + for misc in miscs: + if recipe_type == "xml": + alerts.append(float(misc.find('TIME').text)) + elif recipe_type == "bf": + use = misc['use'] + if use != 'Aroma' and use != 'Boil': + continue + alerts.append(float(misc['time'])) + elif recipe_type == "kbh": + alerts.append(float(misc[0])) ## Dedupe and order the additions by their time, to prevent multiple alerts at the same time alerts = sorted(list(set(alerts))) ## CBP should have these additions in reverse alerts.reverse() - - return alerts + hop_alerts = [] + for i in range(0,6): + try: + hop_alerts.append(str(int(alerts[i]))) + except: + hop_alerts.append(None) + return hop_alerts + def getFirstWort(self, hops, recipe_type): + alert = "No" + if recipe_type == "kbh": + if len(hops) != 0: + alert = "Yes" + elif recipe_type == "xml": + for hop in hops: + use = hop.find('USE').text + ## Hops which are not used in the boil step should not cause alerts + if use != 'First Wort': + continue + alert = "Yes" + elif recipe_type == "bf": + for hop in hops: + if hop['use'] == "First Wort": + alert="Yes" + return alert - # function to create json to be send to api to add a step to the current mash profile. Currently all properties are send to each step which does not cuase an issue - async def create_step(self, type, name, kettle, timer, temp, AutoMode, sensor, Notification = "", AutoNext = "", LidAlert = "", FirstWort = "", Hop1 = "", Hop2 = "", Hop3 = "", Hop4 = "", Hop5 = "", Hop6=""): - step_string = { "name": name, + async def create_Whirlpool_Cooldown(self): + # Add Waitstep as Whirlpool + if self.cooldown != "WaiStep" and self.cooldown !="": + step_string = { "name": "Whirlpool", "props": { - "AutoMode": AutoMode, - "Kettle": kettle, - "Sensor": sensor, - "Temp": temp, - "Timer": timer, - "Notification": Notification, - "AutoNext": AutoNext, - "First_Wort": FirstWort, - "LidAlert": LidAlert, - "Hop_1": Hop1, - "Hop_2": Hop2, - "Hop_3": Hop3, - "Hop_4": Hop4, - "Hop_5": Hop5, - "Hop_6": Hop6 + "Kettle": self.id, + "Timer": "15" }, "status_text": "", "status": "I", - "type": type + "type": "WaitStep" } + await self.create_step(step_string) + + # CoolDown step is sending a notification when cooldowntemp is reached + step_type = self.cooldown if self.cooldown != "" else "WaitStep" + step_name = "CoolDown" + cooldown_sensor = "" + step_temp = "" + step_timer = "15" + if step_type == "CooldownStep": + cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None) + if cooldown_sensor is None or cooldown_sensor == '': + cooldown_sensor = self.kettle.sensor # fall back to kettle sensor if no other sensor is specified + step_timer = "" + step_temp = int(self.CoolDownTemp) + step_string = { "name": "Cooldown", + "props": { + "Kettle": self.id, + "Timer": step_timer, + "Temp": step_temp, + "Sensor": cooldown_sensor + }, + "status_text": "", + "status": "I", + "type": step_type + } + await self.create_step(step_string) + + def get_config_values(self): + self.kettle = None + #Define MashSteps + self.TEMP_UNIT = self.cbpi.config.get("TEMP_UNIT", "C") + self.mashin = self.cbpi.config.get("steps_mashin", "MashInStep") + self.mash = self.cbpi.config.get("steps_mash", "MashStep") + self.mashout = self.cbpi.config.get("steps_mashout", None) # Currently used only for the Braumeister + self.boil = self.cbpi.config.get("steps_boil", "BoilStep") + self.whirlpool="Waitstep" + self.cooldown = self.cbpi.config.get("steps_cooldown", "WaitStep") + #get default boil temp from settings + self.BoilTemp = self.cbpi.config.get("steps_boil_temp", 98) + #get default cooldown temp alarm setting + self.CoolDownTemp = self.cbpi.config.get("steps_cooldown_temp", 25) + # get default Kettle from Settings + self.id = self.cbpi.config.get('MASH_TUN', None) + try: + self.kettle = self.cbpi.kettle.find_by_id(self.id) + except: + self.cbpi.notify('Recipe Upload', 'No default Kettle defined. Please specify default Kettle in settings', NotificationType.ERROR) + config_values = { "kettle": self.kettle, + "kettle_id": str(self.id), + "mashin": str(self.mashin), + "mash": str(self.mash), + "mashout": str(self.mashout), + "boil": str(self.boil), + "whirlpool": str(self.whirlpool), + "cooldown": str(self.cooldown), + "boiltemp": str(self.BoilTemp), + "cooldowntemp": str(self.CoolDownTemp), + "temp_unit": str(self.TEMP_UNIT) + } + return config_values + + async def create_recipe(self, name): + # Create recipe in recipe Book with name of first recipe in xml file + self.recipeID = await self.cbpi.recipe.create(name) + # send recipe to mash profile + await self.cbpi.recipe.brew(self.recipeID) + # remove empty recipe from recipe book + await self.cbpi.recipe.remove(self.recipeID) + + # function to create json to be send to api to add a step to the current mash profile. Currently all properties are send to each step which does not cuase an issue + async def create_step(self, step_string): + #get server port from settings and define url for api calls -> adding steps + self.port = str(self.cbpi.static_config.get('port',8000)) + self.url="http://127.0.0.1:" + self.port + "/step2/" # convert step:string to json required for api call. step = json.dumps(step_string) headers={'Content-Type': 'application/json', 'Accept': 'application/json'}