class MashStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, description="Target Temperature of Mash Step") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the mashing takes place") timer = Property.Number( "Timer in Minutes", configurable=True, description="Timer is started when the target temperature is reached") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Mash Step Completed!", "Starting the next step", timeout=None) # if you dont want a beep sound comment out like : # cbpi.MashStepEndBeep() cbpi.MashStepEndBeep() self.next()
class Chill_CG(StepBase): kettle = StepProperty.Kettle("Kettle", description="Kettle in which the chilling takes place") chiller = StepProperty.Actor("Chiller", description="Actor that controls the Chiller") chillerPump = StepProperty.Actor("chillerPump", description="Actor that controls the chillerPump") chillerTemp = StepProperty.Sensor("Chiller Temp", description="Sensor that shows the chiller temperature") cutoutvariance = Property.Number("Variance between kettle and chiller for end", configurable=True, default_value=0.3, description="The step will end when the kettle temp falls to within this much of the chiller temp.") def init(self): self.actor_on(int(self.chillerPump)) self.actor_on(int(self.chiller)) self.set_target_temp(0, self.kettle) def start(self): pass def reset(self): pass def finish(self): self.actor_off(int(self.chillerPump)) self.actor_off(int(self.chiller)) def execute(self): if self.get_kettle_temp(self.kettle) <= (self.get_sensor_value(self.chillerTemp)+float(self.cutoutvariance)): self.notify("Chill Stage Complete", "Kettle reached: " + str(self.get_kettle_temp(self.kettle)), type="success", timeout=None) self.actor_off(int(self.chiller)) self.actor_off(int(self.chillerPump)) self.next()
class MashInStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True) kettle = StepProperty.Kettle("Kettle") s = False def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.s = False self.set_target_temp(self.temp, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= int( self.temp) and self.s is False: self.s = True self.notify("Step Temp Reached!", "Please press the next button to continue", timeout=None)
class AutoSwitch(StepBase): # Properties a_id = StepProperty.Kettle("Kettle") b_auto = Property.Select("Auto Setting", options=["On", "Off"]) def init(self): if isinstance(self.a_id, unicode) and self.a_id: self.id = (int(self.a_id)) self.auto_type = self.b_auto try: kettle = cbpi.cache.get("kettle").get(self.id) if (kettle.state is False) and (self.auto_type = "On"): # Start controller if kettle.logic is not None: cfg = kettle.config.copy() cfg.update(dict(api=cbpi, kettle_id=kettle.id, heater=kettle.heater, sensor=kettle.sensor)) instance = cbpi.get_controller(kettle.logic).get("class")(**cfg) instance.init() kettle.instance = instance def run(instance): instance.run() t = self.api.socketio.start_background_task(target=run, instance=instance) kettle.state = True cbpi.emit("UPDATE_KETTLE", kettle) else (kettle.state is True) and (self.auto_type = "Off"): # Stop controller kettle.instance.stop() kettle.state = False cbpi.emit("UPDATE_KETTLE", kettle)
class MashInStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, description="Target Temperature of Mash Step") kettle = StepProperty.Kettle("Kettle", description="Kettle in which the mashing takes place") s = False @cbpi.action("Change Power") def change_power(self): self.actor_power(1, 50) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target step self.s = False self.set_target_temp(self.temp, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp) and self.s is False: self.s = True #DBK_added self.actor_off(1) self.notify("Step Temp Reached!", "Please press the next button to continue", timeout=None)
class Cooling(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, description="Target Temperature") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the Chilling takes place") timer = Property.Number( "Timer in Seconds", configurable=True, description="Timer is started when the target temperature is reached") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer)) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) <= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer)) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify( "Cooling Step Completed! Target temp is: %s C" % (self.temp), "Current temp is: %s C" % (self.get_kettle_temp(self.kettle)), timeout=10000) self.next()
class SimpleTargetStep(StepBase): # Properties auto_mode = Property.Select( "Auto Mode", options=["Set to ON", "Set to OFF", "No Change"]) kettle = StepProperty.Kettle("Kettle") target = Property.Number("Target Temp", configurable=True) #------------------------------------------------------------------------------- def init(self): self.set_target_temp(float(self.target), int(self.kettle)) if self.auto_mode == "Set to ON": self.setAutoMode(True) elif self.auto_mode == "Set to OFF": self.setAutoMode(False) next(self) #------------------------------------------------------------------------------- def setAutoMode(self, auto_state): try: kettle = cbpi.cache.get("kettle")[int(self.kettle)] if (kettle.state is False) and (auto_state is True): # turn on if kettle.logic is not None: cfg = kettle.config.copy() cfg.update( dict(api=cbpi, kettle_id=kettle.id, heater=kettle.heater, sensor=kettle.sensor)) instance = cbpi.get_controller( kettle.logic).get("class")(**cfg) instance.init() kettle.instance = instance def run(instance): instance.run() t = cbpi.socketio.start_background_task(target=run, instance=instance) kettle.state = not kettle.state cbpi.emit("UPDATE_KETTLE", cbpi.cache.get("kettle")[int(self.kettle)]) elif (kettle.state is True) and (auto_state is False): # turn off kettle.instance.stop() kettle.state = not kettle.state cbpi.emit("UPDATE_KETTLE", cbpi.cache.get("kettle")[int(self.kettle)]) except Exception as e: cbpi.notify("Error", "Failed to set Auto mode {}".format(["OFF", "ON" ][auto_state]), type="danger", timeout=None) cbpi.app.logger.error(e)
class Rast(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperatur", configurable=True, description="Zieltemperatur des Schritts") kettle = StepProperty.Kettle("Kessel", description="Auswahl des Braukessels") timer = Property.Number( "Timer in Minuten", configurable=True, description="Timer startet, wenn die Zieltemperatur erreicht wurde") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) # @cbpi.action("Timer manuell starten") # def start(self): # if self.is_timer_finished() is None: # self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): pass def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Rast beendet!", "Beginne nächsten Schritt", timeout=None) self.next()
class ChangeKettleTargetTemp(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, description="Target Temperature") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the target temperature will change") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target temp self.set_target_temp(self.temp, self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer)) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(self.temp, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' kettles = cbpi.cache["kettle"] kettle_name = kettles[int(self.kettle)].name self.notify("Changed target temp for %s " % (kettle_name), "New target temp is: %s C" % (self.temp), timeout=10000) self.next()
class BoilStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties power = Property.Number("Power", configurable=True) kettle = StepProperty.Kettle("Kettle") timer = Property.Number("Timer in Minutes", configurable=True) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep print(("class BoilStep(StepBase): power = {}".format(self.kettle))) self.actor_power(1, self.power) #self.set_target_temp(self.power, self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.actor_power(0, self.power) #self.set_target_temp(self.temp, self.kettle) def finish(self): self.actor_power(0, 0) self.set_target_temp(0, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Check if timer finished and go to next step if self.is_timer_finished() == True: next(self)
class TwoKettleLogic(StepBase): ''' Just put the decorator @cbpi.step on top of a method. The class name must be unique in the system ''' # Properties kettle1 = StepProperty.Kettle("Kettle 1") temp1 = Property.Number("Temperature 1", configurable=True) kettle2 = StepProperty.Kettle("Kettle 2") temp2 = Property.Number("Temperature 2", configurable=True) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp1, self.kettle1) self.set_target_temp(self.temp2, self.kettle2) def finish(self): pass def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached for both kettles if self.get_kettle_temp(self.kettle1) >= int( self.temp1) and self.get_kettle_temp(self.kettle2) >= int( self.temp2): self.notify("Kettle Temps Reached!", "Starting the next step.", timeout=None) self.next()
class KettleVolumeStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method. The class name must be unique in the system ''' # Properties temp = Property.Number("Temperature", configurable=True) kettle = StepProperty.Kettle("Kettle") timer = Property.Number("Timer in Minutes", configurable=True) sensor = StepProperty.Sensor("Sensor") volume = Property.Number("Volume", configurable=True) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep #self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' for key, value in cbpi.cache.get("sensors").iteritems(): if key == int(self.sensor): sensorValue = value.instance.last_value # Check if timer finished and go to next step if float(sensorValue) <= float(self.volume): self.set_target_temp(self.temp, self.kettle) kettle_state = cbpi.cache.get("kettle")[int(self.kettle)].state if kettle_state is True: Kettle2View().toggle(int(self.kettle)) self.notify("Kettle Update", "Auto is off. Timer started.", timeout=None) if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: self.notify("Mash-in Complete!", "Starting the next step.", timeout=None) self.next()
class SimpleMashInStep(StepBase): # Properties a_kettle_prop = StepProperty.Kettle( "Kettle", description="Kettle in which the mashing takes place") b_target_prop = Property.Number( "Temperature", configurable=True, description="Target Temperature of Mash Step") c_agitator_prop = Property.Select("Run agitator while heating?", options=["Yes", "No"]) d_kill_heat_prop = Property.Select("Turn off heater when target reached?", options=["Yes", "No"]) #------------------------------------------------------------------------------- def init(self): self.kettle = int(self.a_kettle_prop) self.target = float(self.b_target_prop) self.agitator_run = self.c_agitator_prop == "Yes" self.kill_heat = self.d_kill_heat_prop == "Yes" self.done = False self.agitator = getAgitator( cbpi.cache.get("kettle")[self.kettle].agitator) # set target temp self.set_target_temp(self.target, self.kettle) if self.agitator and self.agitator_run: self.actor_on(self.agitator) #------------------------------------------------------------------------------- def finish(self): self.set_target_temp(0, self.kettle) #------------------------------------------------------------------------------- def execute(self): # Check if Target Temp is reached if (self.get_kettle_temp(self.kettle) >= self.target) and (self.done is False): self.done = True if self.kill_heat: self.set_target_temp(0, self.kettle) if self.agitator: self.actor_off(self.agitator) self.notify("{} complete".format(self.name), "Press next button to continue", type='warning', timeout=None)
class ToggleAuto(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, description="Target Temperature of HLT") kettle = StepProperty.Kettle("Kettle", description="Set this to your HLT") toggle_type = Property.Select("Toggle Type", options=["On", "Off"]) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' #self.notify("props", cbpi.cache.get("kettle")[int(self.kettle)].state, timeout=None) self.set_target_temp(self.temp, self.kettle) kettle_state = kettle = cbpi.cache.get("kettle")[int( self.kettle)].state if kettle_state is False and self.toggle_type == "On": Kettle2View().toggle(int(self.kettle)) self.notify("Kettle Update", "Auto is on. Starting the next step.", timeout=None) self.next() else: if kettle_state is False and self.toggle_type == "Off": self.notify( "Kettle Error", "Auto is already off, please adjust your brew step!", type="danger", timeout=None) else: if kettle_state is True: if self.toggle_type == "On": self.notify( "Kettle Error", "Auto is already on, please adjust your brew step!", type="danger", timeout=None) else: Kettle2View().toggle(int(self.kettle)) self.notify("Kettle Update", "Auto is Off. Starting the next step.", timeout=None) self.next()
class Maischen(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number( "Temperatur", configurable=True, description="Benachrichtigung bei Erreichen der Ein/Abmaischtemperatur." ) kettle = StepProperty.Kettle("Kessel", description="Auswahl des Braukessels") s = False @cbpi.action("Weiter") def next_step(self): self.set_target_temp(0, self.kettle) self.next() def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.s = False self.set_target_temp(self.temp, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float( self.temp) and self.s is False: self.s = True self.notify( "Maischtemperatur erreicht!", "Bitte maischen und anschließend bestätigen, um fortzufahren.", timeout=None)
class SimpleCoolToTemp(StepBase): kettle = StepProperty.Kettle("Kettle", description="Kettle") c_target = Property.Number("Target", configurable=True) c_offset = Property.Number("Offset", configurable=True) def init(self): self.target = float(self.c_target) self.offset = float(self.c_offset) self.set_target_temp(self.target, self.kettle) def reset(self): self.set_target_temp(self.target, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def execute(self): if abs(self.get_kettle_temp(self.kettle) - self.target) <= self.offset: next(self)
class ToBoilStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Power", configurable=True) kettle = StepProperty.Kettle("Kettle") s = False def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.s = False print(("ToBoilStep init: {}".format(int(self.temp)))) self.set_target_temp(self.temp, self.kettle) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached #if self.get_kettle_temp(self.kettle) >= int(self.temp) and self.s is False: # self.s = True # self.notify("Step Temp Reached!", "Please press the next button to continue", timeout=None) #self.actor_power(self.get_target_temp) pass
class SimpleBoilStep(StepBase): # Properties textDesc = "Brief description of the addition" timeDesc = "Time in minutes before end of boil" add_1_text = Property.Text("Addition 1 Name", configurable=True, description=textDesc) add_1_time = Property.Number("Addition 1 Time", configurable=True, description=timeDesc) add_2_text = Property.Text("Addition 2 Name", configurable=True, description=textDesc) add_2_time = Property.Number("Addition 2 Time", configurable=True, description=timeDesc) add_3_text = Property.Text("Addition 3 Name", configurable=True, description=textDesc) add_3_time = Property.Number("Addition 3 Time", configurable=True, description=timeDesc) add_4_text = Property.Text("Addition 4 Name", configurable=True, description=textDesc) add_4_time = Property.Number("Addition 4 Time", configurable=True, description=timeDesc) add_5_text = Property.Text("Addition 5 Name", configurable=True, description=textDesc) add_5_time = Property.Number("Addition 5 Time", configurable=True, description=timeDesc) add_6_text = Property.Text("Addition 6 Name", configurable=True, description=textDesc) add_6_time = Property.Number("Addition 6 Time", configurable=True, description=timeDesc) add_7_text = Property.Text("Addition 7 Name", configurable=True, description=textDesc) add_7_time = Property.Number("Addition 7 Time", configurable=True, description=timeDesc) add_8_text = Property.Text("Addition 8 Name", configurable=True, description=textDesc) add_8_time = Property.Number("Addition 8 Time", configurable=True, description=timeDesc) kettle_prop = StepProperty.Kettle( "Kettle", description="Kettle in which the boiling step takes place") target_prop = Property.Number("Temperature", configurable=True, description="Target temperature for boiling") timer_prop = Property.Number( "Timer in Minutes", configurable=True, default_value=90, description="Timer is started when target temperature is reached") warning_addition_prop = Property.Number( "Addition Warning", configurable=True, default_value=30, description="Time in seconds to warn before each addition") warning_boil_prop = Property.Number( "Boil Warning", configurable=True, default_value=1, description="Degrees below target to warn of impending boil") #------------------------------------------------------------------------------- def init(self): self.target = float(self.target_prop) self.kettle = int(self.kettle_prop) self.timer = float(self.timer_prop) * 60.0 self.warn_add = float(self.warning_addition_prop) self.warn_boil = float(self.warning_boil_prop) self.done_boil_warn = False self.done_boil_alert = False # set the additions dictionary self.additions = dict() for i in range(1, 9): additionTime = self.__getattribute__("add_{}_time".format(i)) additionText = self.__getattribute__("add_{}_text".format(i)) try: if additionText is None: additionText = "Addition {}".format(i) self.additions[i] = { 'text': additionText, 'time': float(additionTime) * 60.0, 'mins': int(additionTime), 'done': False, 'warn': False, } except: # empty or invalid addition pass # set target temp self.set_target_temp(self.target, self.kettle) #------------------------------------------------------------------------------- @cbpi.action("Start Timer Now") def start(self): if self.is_timer_finished() is None: self.start_timer(self.timer) #------------------------------------------------------------------------------- def reset(self): self.stop_timer() self.set_target_temp(self.target, self.kettle) #------------------------------------------------------------------------------- def finish(self): self.set_target_temp(0, self.kettle) #------------------------------------------------------------------------------- def execute(self): # Check if Target Temp is reached if self.is_timer_finished() is None: self.check_boil_warnings() if self.get_kettle_temp(self.kettle) >= self.target: self.start_timer(self.timer) elif self.is_timer_finished() is True: self.notify("{} complete".format(self.name), "Starting the next step", type='success', timeout=None) next(self) else: self.check_addition_timers() #------------------------------------------------------------------------------- def check_addition_timers(self): for i in self.additions: addition_time = self.timer_end - self.additions[i]['time'] warning_time = addition_time - self.warn_add now = time.time() if not self.additions[i]['warn'] and now > warning_time: self.additions[i]['warn'] = True self.notify("Warning: {} min Additions".format( self.additions[i]['mins']), "Add {} in {} seconds".format( self.additions[i]['text'], self.warn_add), type='info', timeout=(self.warn_add - 1) * 1000) if not self.additions[i]['done'] and now > addition_time: self.additions[i]['done'] = True self.notify("Alert: {} min Additions".format( self.additions[i]['mins']), "Add {} now".format(self.additions[i]['text']), type='warning', timeout=None) #------------------------------------------------------------------------------- def check_boil_warnings(self): if (not self.done_boil_warn) and (self.get_kettle_temp(self.kettle) >= self.target - self.warn_boil): self.notify("Warning: Boil Approaching", "Current Temp {:.1f}".format( self.get_kettle_temp(self.kettle)), type="info", timeout=self.warn_add * 1000) self.done_boil_warn = True if (not self.done_boil_alert) and (self.get_kettle_temp(self.kettle) >= self.target): self.notify("Alert: Boil Imminent", "Current Temp {:.1f}".format( self.get_kettle_temp(self.kettle)), type="warning", timeout=None) self.done_boil_alert = True
class SimpleMashStep(StepBase): # Properties a_kettle_prop = StepProperty.Kettle( "Kettle", description="Kettle in which the mashing takes place") b_target_prop = Property.Number( "Temperature", configurable=True, description="Target Temperature of Mash Step") c_timer_prop = Property.Number( "Timer in minutes", configurable=True, description="Amount of time to maintain taget temperature in this step" ) d_offset_prop = Property.Number( "Target timer offset", configurable=True, default_value=0, description= "Start timer when temperature is this close to target. Useful for PID heaters that approach target slowly." ) e_agitator_start_prop = Property.Select("Turn agitator on at start?", options=["Yes", "No"]) f_agitator_stop_prop = Property.Select("Turn agitator off at end?", options=["Yes", "No"]) #------------------------------------------------------------------------------- def init(self): self.kettle = int(self.a_kettle_prop) self.target = float(self.b_target_prop) self.timer = float(self.c_timer_prop) self.offset = float(self.d_offset_prop) self.agitator_start = self.e_agitator_start_prop == "Yes" self.agitator_stop = self.f_agitator_stop_prop == "Yes" self.agitator = getAgitator( cbpi.cache.get("kettle")[self.kettle].agitator) # set target temp self.set_target_temp(self.target, self.kettle) if self.agitator and self.agitator_start: self.actor_on(self.agitator) #------------------------------------------------------------------------------- @cbpi.action("Start Timer Now") def start(self): if self.is_timer_finished() is None: self.start_timer(self.timer * 60) #------------------------------------------------------------------------------- def reset(self): self.stop_timer() self.set_target_temp(self.target, self.kettle) #------------------------------------------------------------------------------- def finish(self): self.set_target_temp(0, self.kettle) if self.agitator and self.agitator_stop: self.actor_off(self.agitator) #------------------------------------------------------------------------------- def execute(self): # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= self.target - self.offset: # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(self.timer * 60) # Check if timer finished and go to next step if self.is_timer_finished() is True: self.notify("{} complete".format(self.name), "Starting the next step", type='success', timeout=None) next(self)
class BoilStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, default_value=100, description="Target temperature for boiling") kettle = StepProperty.Kettle("Kettle", description="Kettle in which the boiling step takes place") timer = Property.Number("Timer in Minutes", configurable=True, default_value=90, description="Timer is started when target temperature is reached") hop_1 = Property.Number("Hop 1 Addition", configurable=True, description="First Hop alert (minutes before finish)") hop_1_added = Property.Number("",default_value=None) hop_2 = Property.Number("Hop 2 Addition", configurable=True, description="Second Hop alert (minutes before finish)") hop_2_added = Property.Number("", default_value=None) hop_3 = Property.Number("Hop 3 Addition", configurable=True, description="Third Hop alert (minutes before finish)") hop_3_added = Property.Number("", default_value=None) hop_4 = Property.Number("Hop 4 Addition", configurable=True, description="Fourth Hop alert (minutes before finish)") hop_4_added = Property.Number("", default_value=None) hop_5 = Property.Number("Hop 5 Addition", configurable=True, description="Fifth Hop alert (minutes before finish)") hop_5_added = Property.Number("", default_value=None) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def check_hop_timer(self, number, value): if (value != "" and self.__getattribute__("hop_%s_added" % number) is not True and (time.time() > (self.timer_end - int(value)*60))): self.__setattr__("hop_%s_added" % number, True) self.notify("Hop Alert", "Please add Hop %s" % number, timeout=None) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Check if hop time reached if self.is_timer_finished() == False: self.check_hop_timer(1, self.hop_1) self.check_hop_timer(2, self.hop_2) self.check_hop_timer(3, self.hop_3) self.check_hop_timer(4, self.hop_4) self.check_hop_timer(5, self.hop_5) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Boil Step Completed!", "Starting the next step", timeout=None) self.next()
class ChillerToTemp(StepBase): ''' Just put the decorator @cbpi.step on top of a method. The class name must be unique in the system ''' # Properties temp = Property.Number("Desired Temperature", configurable=True) kettle = StepProperty.Kettle("Kettle") prime_pump = StepProperty.Actor("Chiller Primary Pump") sec_pump = StepProperty.Actor("Secondary Pump") timer = Property.Number( "Upper Bound in Minutes", description="The time after which this step will conclude," " regardless of the current temp", configurable=True) # num of times to check target temp has been reached (in order to rule out bad readings) Samples = Property.Number( "Number of Temp Readings before ShutDown", default_value=DEF_SAMPLES, description="A safety against false positive detection", configurable=True) sample_streak = 0 def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) self.start_timer(int(self.timer) * 60) # turns pump on if self.sec_pump is not None: self.actor_on(int(self.sec_pump)) self.actor_on(int(self.prime_pump)) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) self.start_timer(int(self.timer) * 60) def finish(self): self.set_target_temp(0, self.kettle) # turns pump off at finish self.actor_off(int(self.prime_pump)) if self.sec_pump is not None: self.actor_off(int(self.sec_pump)) def execute(self): ''' This method is executed in intervals :return: ''' # Check if Target Temp has been reached if self.get_kettle_temp(self.kettle) <= float(self.temp): self.sample_streak += 1 # Checks if Target Amount of Samples is reached if self.sample_streak == self.Samples: self.notify("Yeast Pitch Temp Reached!", "Move to fermentation tank", timeout=None) # turns pump off at finish self.actor_off(int(self.prime_pump)) if self.sec_pump is not None: self.actor_off(int(self.sec_pump)) self.next() else: # Nullifies The samples streak self.sample_streak = 0 # Check if timer finished and go to next step if self.is_timer_finished(): self.notify("Step Temp Wasn't Reached!", "Good luck:(", timeout=None) # turns pump off at finish self.actor_off(int(self.prime_pump)) if self.sec_pump is not None: self.actor_off(int(self.sec_pump)) self.next()
class BoilStepWithCountdownReminders(StepBase): ''' BoilStep with reminders that are set relative to end of boil ''' REMINDER_NAMES = [ "Hop 1 Addition", "Hop 2 Addition", "Hop 3 Addition", "Hop 4 Addition", "Hop 5 Addition", "Prepare yeast", "Prepare finings", "Prepare cooling system", "Dose finings", "Start hot-side cooling loop", "Prepare fermenter" ] # Properties temp = Property.Number("Temperature", configurable=True, default_value=100, description="Target temperature for boiling") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the boiling step takes place") timer = Property.Number( "Timer in Minutes", configurable=True, default_value=90, description="Timer is started when target temperature is reached") reminder_00 = Property.Number(REMINDER_NAMES[0], configurable=True) reminder_00_displayed = Property.Number( "", default_value=None, description="Reminder displayed status") reminder_01 = Property.Number(REMINDER_NAMES[1], configurable=True) reminder_01_displayed = Property.Number("", default_value=None) reminder_02 = Property.Number(REMINDER_NAMES[2], configurable=True) reminder_02_displayed = Property.Number("", default_value=None) reminder_03 = Property.Number(REMINDER_NAMES[3], configurable=True) reminder_03_displayed = Property.Number("", default_value=None) reminder_04 = Property.Number(REMINDER_NAMES[4], configurable=True) reminder_04_displayed = Property.Number("", default_value=None) reminder_05 = Property.Number(REMINDER_NAMES[5], configurable=True) reminder_05_displayed = Property.Number("", default_value=None) reminder_06 = Property.Number(REMINDER_NAMES[6], configurable=True) reminder_06_displayed = Property.Number( "", default_value=None, description="Reminder displayed status") reminder_07 = Property.Number(REMINDER_NAMES[7], configurable=True) reminder_07_displayed = Property.Number( "", default_value=None, description="Reminder displayed status") reminder_08 = Property.Number(REMINDER_NAMES[8], configurable=True) reminder_08_displayed = Property.Number( "", default_value=None, description="Reminder displayed status") reminder_09 = Property.Number(REMINDER_NAMES[9], configurable=True) reminder_09_displayed = Property.Number( "", default_value=None, description="Reminder displayed status") reminder_10 = Property.Number(REMINDER_NAMES[10], configurable=True) reminder_10_displayed = Property.Number( "", default_value=None, description="Reminder displayed status") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' self.set_target_temp(self.temp, self.kettle) self._logger = logging.getLogger(type(self).__name__) @cbpi.action("Start Timer Now") def start(self): ''' :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def check_reminder(self, number): raw_value = self.__getattribute__("reminder_%02d" % number) if isinstance(raw_value, unicode) and raw_value != '' and self.__getattribute__( "reminder_%02d_displayed" % number) is not True: value = int(raw_value) if self.countdown_time_has_expired(value): self.__setattr__("reminder_%02d_displayed" % number, True) reminder = self.REMINDER_NAMES[number] self.notify("Countdown Reminder", reminder, timeout=None) def countdown_time_has_expired(self, value): return time.time() > (self.timer_end - (value * 60)) def execute(self): ''' This method is executed in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) else: for i in range(11): self.check_reminder(i) if self.is_timer_finished() == True: self.notify("Boil Step Completed!", "Starting the next step", timeout=None) self.next()
class KRimsMashStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Kettle Temperature", configurable=True, description="Target Temperature of the Kettle") mash_temp = Property.Number( "Mash Temperature", configurable=True, description="Target Temperature of the Mash Step") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the mashing takes place") timer = Property.Number( "Timer in Minutes", configurable=True, description="Timer is started when the target temperature is reached") sensor = StepProperty.Sensor("Sensor", description="Selectable secondary sensor") pump = StepProperty.Actor("Pump, etc.") mode = Property.Select("Mode", ["Timer", "User input"], "Selects how the step operates") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) self.actor_on(int(self.pump)) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) self.actor_off(int(self.pump)) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.mode == "Timer": if cbpi.get_sensor_value(int(self.sensor)) >= float( self.mash_temp): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Mash Step Completed!", "Starting the next step", timeout=None) self.next() if self.mode == "User input": if cbpi.get_sensor_value(int(self.sensor)) >= float( self.mash_temp): self.notify("Mash Temp reached!", "User input needed to proceed to next step", timeout=None) self.mode = "All done. Wait for user"
class SimpleChillToTemp(StepBase): # Properties actor1 = StepProperty.Actor( "Actor 1", description="Actor to turn on until target temp is reached") actor2 = StepProperty.Actor( "Actor 2", description="Actor to turn on until target temp is reached") kettle_prop = StepProperty.Kettle( "Kettle", description="Kettle in which the chilling takes place") target_prop = Property.Number( "Temperature", configurable=True, description="Target temperature of chill step") #------------------------------------------------------------------------------- def init(self): self.actors = [self.actor1, self.actor2] self.target = float(self.target_prop) self.kettle = int(self.kettle_prop) # set target temp if self.kettle: self.set_target_temp(self.target, self.kettle) self.start_time = time.time() self.actors_on() else: cbpi.notify("No kettle defined", "Starting the next step", type="danger", timeout=None) next(self) #------------------------------------------------------------------------------- def reset(self): self.set_target_temp(self.target, self.kettle) #------------------------------------------------------------------------------- def finish(self): self.actors_off() #------------------------------------------------------------------------------- def execute(self): # Check if Target Temp is reached if float(self.get_kettle_temp(self.kettle)) <= self.target: elapsed_time = int(time.time() - self.start_time) hours, remainder = divmod(elapsed_time, 3600) minutes, seconds = divmod(remainder, 60) if hours: elapsed_text = '{}:{:0>2d}:{:0>2d}'.format( hours, minutes, seconds) else: elapsed_text = '{}:{:0>2d}'.format(minutes, seconds) self.notify("{} complete".format(self.name), "Chill temp reached in {}".format(elapsed_text), timeout=None) next(self) #------------------------------------------------------------------------------- def actors_on(self): for actor in self.actors: try: self.actor_on(int(actor)) except: pass def actors_off(self): for actor in self.actors: try: self.actor_off(int(actor)) except: pass
class PumpMash(StepBase): ''' A step replacing the default mash step. Adds the functionality of temp overshoot and pump activation. ''' # Properties temp = Property.Number("Temperature", configurable=True, description="Target Temperature of Mash Step") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the mashing takes place") pump = StepProperty.Actor("Pump", description="Please select the agitator") timer = Property.Number( "Timer in Minutes", configurable=True, description="Timer is started when the target temperature is reached") # the diff in celsius degrees from mash temp to desired strike temp overshoot = Property.Number( "Overshoot Temperature Difference", configurable=True, default_value=1, description= "The difference between the initial heating temp and the mash temp." "Reset doesn't use overshoot") pump_work_time = Property.Number( "Mash pump work time", True, 600, description="Rest the pump after this many seconds during the mash.") pump_rest_time = Property.Number( "Mash pump rest time", True, 60, description="Rest the pump for this many seconds every rest interval.") # the temp above which the pump starts its working cycles pump_min_temp = Property.Number( "The Min Temperature", True, 40, description="The temperature in which the pump starts working.") pump_on = False # current state of the pump last_toggle_time = None pump_cycle = None def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' self.pump_cycle = { True: float(self.pump_work_time), False: float(self.pump_rest_time) } self.actor_off(int(self.pump)) self.last_toggle_time = time.time() # set target temp self.set_target_temp( float(self.temp) + float(self.overshoot), self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) self.actor_off(int(self.pump)) # turns pump off def toggle_pump(self): ''' Toggles between pump states (on/off) ''' if self.pump_on: self.pump_on = False self.actor_off(int(self.pump)) else: self.pump_on = True self.actor_on(int(self.pump)) self.last_toggle_time = time.time() def execute(self): ''' This method is execute in an interval :return: ''' # Pump action if self.get_kettle_temp(self.kettle) >= float(self.pump_min_temp) and time.time() >= self.last_toggle_time + \ self.pump_cycle[self.pump_on]: self.toggle_pump() # Check if Target Temp is reached if self.get_kettle_temp( self.kettle) >= float(self.temp) + float(self.overshoot): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # sets target temp to mash temp self.set_target_temp(self.temp, self.kettle) self.notify("Mash Temp Reached!", "Insert Grain", timeout=None) # Check if timer finished and go to next step if self.is_timer_finished(): self.actor_off(int(self.pump)) # turns pump off self.notify("Mash Step Completed!", "Starting the next step", timeout=None) self.next()
class PumpBoil(StepBase): ''' A step replacing the default boil step. Adds the functionality of pump activation. ''' # Properties temp = Property.Number("Temperature", configurable=True, default_value=100, description="Target temperature for boiling") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the boiling step takes place") pump = StepProperty.Actor("Pump", description="Please select the agitator") timer = Property.Number( "Timer in Minutes", configurable=True, default_value=90, description="Timer is started when target temperature is reached") hop_1 = Property.Number("Hop 1 Addition", configurable=True, description="Fist Hop alert") hop_1_added = Property.Number("", default_value=None) hop_2 = Property.Number("Hop 2 Addition", configurable=True, description="Second Hop alert") hop_2_added = Property.Number("", default_value=None) hop_3 = Property.Number("Hop 3 Addition", configurable=True) hop_3_added = Property.Number("", default_value=None, description="Third Hop alert") hop_4 = Property.Number("Hop 4 Addition", configurable=True) hop_4_added = Property.Number("", default_value=None, description="Fourth Hop alert") hop_5 = Property.Number("Hop 5 Addition", configurable=True) hop_5_added = Property.Number("", default_value=None, description="Fives Hop alert") pump_work_time = Property.Number( "Mash pump work time", True, 200, description="Rest the pump after this many seconds during the mash.") pump_rest_time = Property.Number( "Mash pump rest time", True, 100, description="Rest the pump for this many seconds every rest interval.") # the pump above which the pump stops working pump_max_temp = Property.Number( "The Max Temperature", True, 100, description="The temperature in which the pump stops its cycles.") pump_on = False # the current state of the pump pump_time = None pump_cycle = None def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) self.pump_cycle = { True: int(self.pump_work_time), False: int(self.pump_rest_time) } self.pump_time = time.time() self.actor_off(int(self.pump)) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) self.actor_off(int(self.pump)) def check_hop_timer(self, number, value): if self.__getattribute__( "hop_%s_added" % number) is not True and time.time() > ( self.timer_end - (int(self.timer) * 60 - int(value) * 60)): self.__setattr__("hop_%s_added" % number, True) self.notify("Hop Alert", "Please add Hop %s" % number, timeout=None) def toggle_pump(self): """ Toggles between pump states (on/off) """ if self.pump_on: self.pump_on = False self.actor_off(int(self.pump)) else: self.pump_on = True self.actor_on(int(self.pump)) self.pump_time = time.time() def execute(self): ''' This method is execute in an interval :return: ''' # Pump action if self.get_kettle_temp(self.kettle) > float(self.pump_max_temp): self.actor_off(int(self.pump)) elif time.time() >= self.pump_time + self.pump_cycle[self.pump_on]: self.toggle_pump() # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) else: self.check_hop_timer(1, self.hop_1) self.check_hop_timer(2, self.hop_2) self.check_hop_timer(3, self.hop_3) self.check_hop_timer(4, self.hop_4) self.check_hop_timer(5, self.hop_5) # Check if timer finished and go to next step if self.is_timer_finished(): self.actor_off(int(self.pump)) # turns pump off self.notify("Boil Step Completed!", "Starting the next step", timeout=None) self.next()
class SimpleWhirlpoolStep(StepBase): kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the chilling takes place") chiller = StepProperty.Actor("Chiller", description="Actor that controls the Chiller") chillerPump = StepProperty.Actor( "chillerPump", description="Actor that controls the chillerPump") temp = Property.Number("Whirlpool Temperature", configurable=True, default_value=80, description="Target Temperature of Whirlpool") timer = Property.Number( "Total Whirlpool Timer in Minutes (incl. Santise Time)", configurable=True, default_value=30, description="Timer is started immediately") sanitiseTimer = Property.Number("Sanitise Timer in Minutes", configurable=True, default_value=5, description="Time at sanitisation temp") sanitiseTemp = Property.Number( "Sanitise Temperature", configurable=True, default_value=95, description="Target Temperature for sanitisation") s_end = 1 stage = "init" #This process goes through the following stages: init, waithookup, sanitise, whirlpool c_cut = 0 def init(self): self.stage = "init" self.actor_off(int(self.chillerPump)) self.actor_off(int(self.chiller)) self.set_target_temp(self.sanitiseTemp, self.kettle) self.s_end = 1 self.c_cut = 0 def start(self): pass def reset(self): self.stop_timer() def finish(self): self.actor_off(int(self.chillerPump)) self.actor_off(int(self.chiller)) @cbpi.action("Chiller Connected") def chiller_connected(self): if self.stage == "waithookup": self.stage = "sanitise" self.actor_on(int(self.chillerPump)) self.s_end = (self.timer_remaining() - (60 * int(self.sanitiseTimer))) else: self.notify( "No Action Taken", "Function only works in \"waithookup\" sub-stage. Current stage: " + self.stage, type="info", timeout=5000) def execute(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: if self.stage != "whirlpool": self.notify( "ERROR - Whirlpool incomplete", "Step completed without reaching internal whirlpool stage", type="danger", timeout=None) self.actor_off(int(self.chiller)) self.actor_off(int(self.chillerPump)) next(self) else: if self.get_kettle_temp(self.kettle) >= ( self.get_target_temp(self.kettle) + 10): #This option determines when the chiller is full on self.actor_on(int(self.chiller)) elif self.get_kettle_temp(self.kettle) >= self.get_target_temp( self.kettle ): #This option specifies partial activation - alternate 3secs on and off self.c_cut = int((self.timer_remaining() / 3)) if self.c_cut % 2: self.actor_on(int(self.chiller)) else: self.actor_off(int(self.chiller)) else: self.actor_off(int(self.chiller)) if self.stage == "init": self.notify("Put on the lid and connect the chiller", "Please select \"Chiller Connected\" to continue", type="warning", timeout=None) self.stage = "waithookup" elif self.stage == "sanitise": if self.s_end >= self.timer_remaining(): self.stage = "whirlpool" self.set_target_temp(self.temp, self.kettle)
class BoilStepWithHopDropper(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties temp = Property.Number("Temperature", configurable=True, default_value=100, description="Target temperature for boiling") kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the boiling step takes place") timer = Property.Number( "Timer in Minutes", configurable=True, default_value=90, description="Timer is started when target temperature is reached") hop_dropper = StepProperty.Actor( "Hop Dropper", description="Please select the hop dropper actor") hop_1 = Property.Number("Hop 1 Addition", default_value="9999", configurable=True, description="First Hop alert") hop_1_added = Property.Number("", default_value=None) hop_1_desc = Property.Text( label="Hop 1 Description", configurable=True, default_value="", description="Enter Hop 1 descriptions, like how much use and hop name." ) hop_2 = Property.Number("Hop 2 Addition", default_value="9999", configurable=True, description="Second Hop alert") hop_2_added = Property.Number("", default_value=None) hop_2_desc = Property.Text( label="Hop 2 Description", configurable=True, default_value="", description="Enter Hop 2 descriptions, like how much use and hop name." ) hop_3 = Property.Number("Hop 3 Addition", default_value="9999", configurable=True, description="Third Hop alert") hop_3_added = Property.Number("", default_value=None) hop_3_desc = Property.Text( label="Hop 3 Description", configurable=True, default_value="", description="Enter Hop 3 descriptions, like how much use and hop name." ) hop_4 = Property.Number("Hop 4 Addition", default_value="9999", configurable=True, description="Fourth Hop alert") hop_4_added = Property.Number("", default_value=None) hop_4_desc = Property.Text( label="Hop 4 Description", configurable=True, default_value="", description="Enter Hop 4 descriptions, like how much use and hop name." ) hop_5 = Property.Number("Hop 5 Addition", default_value="9999", configurable=True, description="Fives Hop alert") hop_5_added = Property.Number("", default_value=None) hop_5_desc = Property.Text( label="Hop 5 Description", configurable=True, default_value="", description="Enter Hop 5 descriptions, like how much use and hop name." ) hop_6 = Property.Number("Hop 6 Addition", default_value="9999", configurable=True, description="Sixth Hop alert") hop_6_added = Property.Number("", default_value=None) hop_6_desc = Property.Text( label="Hop 6 Description", configurable=True, default_value="", description="Enter Hop 6 descriptions, like how much use and hop name." ) hop_7 = Property.Number("Hop 7 Addition", default_value="9999", configurable=True, description="Seventh Hop alert") hop_7_added = Property.Number("", default_value=None) hop_7_desc = Property.Text( label="Hop 7 Description", configurable=True, default_value="", description="Enter Hop 7 descriptions, like how much use and hop name." ) def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target tep self.set_target_temp(self.temp, self.kettle) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.temp, self.kettle) def finish(self): self.set_target_temp(0, self.kettle) def check_hop_timer(self, number, value): if self.__getattribute__( "hop_%s_added" % number) is not True and time.time() > ( self.timer_end - (int(self.timer) * 60 - int(value) * 60)): self.__setattr__("hop_%s_added" % number, True) if self.hop_dropper is not None: self.actor_on(int(self.hop_dropper)) self.notify( "Hop Alert", "Adding Hops #%s: %s" % (number, self.__getattribute__("hop_%s_desc" % number)), timeout=10000) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.kettle) >= float(self.temp): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) else: self.check_hop_timer(1, self.hop_1) self.check_hop_timer(2, self.hop_2) self.check_hop_timer(3, self.hop_3) self.check_hop_timer(4, self.hop_4) self.check_hop_timer(5, self.hop_5) self.check_hop_timer(6, self.hop_6) self.check_hop_timer(7, self.hop_7) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Boil Step Completed!", "Starting the next step", timeout=None) self.next()
class SimpleMashOutStep(StepBase): kettle = StepProperty.Kettle( "Kettle", description="Kettle in which the chilling takes place") temp = Property.Number("MashOut Temperature", configurable=True, default_value=76.7, description="Target Temperature of Mashout") timer = Property.Number("MashOut Timer in Minutes", configurable=True, default_value=10, description="Time to be held at Mashout temp") stage = "init" #This process goes through the following stages: init, mashout, sparge, preboil, hotbreak preboiltemp = 90 hotbreaktemp = 99 wait_user = False def init(self): self.stage = "init" self.wait_user = False self.set_target_temp(self.temp, self.kettle) # self.preboiltemp = self.api.cache.get("kettle").get(self.kettle).get_config_parameter("e_max_temp_pid") # self.hotbreaktemp = 99 def start(self): pass def reset(self): pass def finish(self): pass @cbpi.action("Sparge Complete") def sparge_complete(self): if self.stage == "sparge": self.stage = "preboil" self.wait_user = False self.set_target_temp(self.preboiltemp, self.kettle) else: self.notify( "No Action Taken", "Function only works in \"sparge\" sub-stage. Current stage: " + self.stage, type="info", timeout=5000) @cbpi.action("Removed Lid") def lid_removed(self): if self.stage == "preboil": self.stage = "hotbreak" self.wait_user = False self.set_target_temp(self.hotbreaktemp, self.kettle) else: self.notify( "No Action Taken", "Function only works in \"preboil\" sub-stage. Current stage: " + self.stage, type="info", timeout=5000) @cbpi.action("Hotbreak Finished") def hotbreak_finished(self): if self.stage == "hotbreak": self.wait_user = False next(self) else: self.notify( "No Action Taken", "Function only works in \"hotbreak\" sub-stage. Current stage: " + self.stage, type="info", timeout=5000) def execute(self): if self.stage == "init": #let the kettle heat to mash out temp if self.get_kettle_temp(self.kettle) >= self.temp: self.stage = "mashout" elif self.stage == "mashout": #run the mash timer if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: self.stage = "sparge" elif self.stage == "sparge": #wait for user confirmation to continue if self.wait_user == False: self.notify( "MASH OUT COMPLETE", "Sparge and then select \"Sparge Complete\" to continue.", type="warning", timeout=None) self.wait_user = True elif self.stage == "preboil": #let the kettle heat to pre-boil, then wait for user to remove lid if self.get_kettle_temp(self.kettle) >= self.preboiltemp: if self.wait_user == False: self.notify( "REMOVE THE LID", "Heated to Pre-Boil. Remove the lid and click \"Removed Lid\" to continue.", type="warning", timeout=None) self.wait_user = True elif self.stage == "hotbreak": #heat kettle to boil, then wait for user for user to go to boil stage if self.get_kettle_temp(self.kettle) >= self.hotbreaktemp: if self.wait_user == False: self.notify( "WATCH FOR HOTBREAK", "When hotbreak is complete click \"Hotbreak Finished\" to continue.", type="warning", timeout=None) self.wait_user = True else: #An error has occured! Not in a valid status self.notify("INVALID STAGE", "An invalid stage has been returned. Current stage: " + self.stage, type="dangar", timeout=None)
class HERMSStep(StepBase): ''' Just put the decorator @cbpi.step on top of a method ''' # Properties target_temp = Property.Number( "Target Temp", configurable=True, description="Target Temperature of Mash Step") mash_tun = StepProperty.Kettle( "Mash Tun", description="Kettle in which the mashing takes place") hlt = StepProperty.Kettle( "HLT", description="Kettle used for heat transfer, hot liquor tank") timer = Property.Number("Rest (m)", configurable=True, description="Minutes of rest at target temp") hlt_offset = Property.Number( "HLT Offset", configurable=True, default_value=8, description= "Temp relative to mash target to maintain in HLT while rising") def init(self): ''' Initialize Step. This method is called once at the beginning of the step :return: ''' # set target temp self.set_target_temp(self.target_temp, self.mash_tun) self.set_target_temp( int(self.target_temp) + int(self.hlt_offset), self.hlt) @cbpi.action("Start Timer Now") def start(self): ''' Custom Action which can be execute form the brewing dashboard. All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface :return: ''' if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() self.set_target_temp(self.target_temp, self.mash_tun) self.set_target_temp( int(self.target_temp) + int(self.hlt_offset), self.hlt) def finish(self): self.set_target_temp(0, self.mash_tun) self.set_target_temp(0, self.hlt) def execute(self): ''' This method is execute in an interval :return: ''' # Check if Target Temp is reached if self.get_kettle_temp(self.mash_tun) >= int(self.target_temp): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Mash Step Completed!", "Starting the next step", timeout=None) self.next()