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 PumpStep(StepBase): pump = StepProperty.Actor("Pump") timer = Property.Number("Timer in Minutes", configurable=True, default_value=0) @cbpi.action("Stat Timer") def start(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() def finish(self): self.actor_off(int(self.pump)) def init(self): self.actor_on(int(self.pump)) def execute(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: self.next()
class PumpStep(StepBase): pump = StepProperty.Actor("Pump", description="Pump actor gets toogled") timer = Property.Number("Timer in Minutes", configurable=True, default_value=0, description="Timer is started immediately") @cbpi.action("Stat Timer") def start(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) def reset(self): self.stop_timer() def finish(self): self.actor_off(int(self.pump)) def init(self): self.actor_on(int(self.pump)) def execute(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: # if you dont want a beep sound comment out like : # cbpi.PumpStepEndBeep() cbpi.PumpStepEndBeep() self.next()
class PumpStep(StepBase): pump = StepProperty.Actor("Pump", description="Pump actor gets toogled") timer = Property.Number("Timer in Minutes", configurable=True, default_value=0, description="Timer is started immediately") @cbpi.action("Start Timer") def start(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) #self.start_stopwatch() def reset(self): self.stop_timer() #self.stop_stopwatch() def finish(self): self.actor_off(int(self.pump)) #self.stop_stopwatch() def init(self): self.actor_on(int(self.pump)) def execute(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: self.next()
class PressureSensor(StepBase): volume = Property.Number("volume", configurable=True) sensor = StepProperty.Sensor("Sensor") actor = StepProperty.Actor("Actor") def init(self): if self.actor is not None: self.actor_on(int(self.actor)) @cbpi.action("Turn Actor OFF") def start(self): if self.actor is not None: self.actor_off(int(self.actor)) def reset(self): if self.actor is not None: self.actor_off(int(self.actor)) def finish(self): if self.actor is not None: self.actor_off(int(self.actor)) def execute(self): for key, value in cbpi.cache.get("sensors").iteritems(): if key == int(self.sensor): sensorValue = value.instance.last_value if float(sensorValue) >= float(self.volume): self.next()
class SimpleActorTimer(StepBase): # Properties actor1 = StepProperty.Actor("Actor 1") actor2 = StepProperty.Actor("Actor 2") timer = Property.Number("Timer in Minutes", configurable=True, description="Timer is started immediately.") #------------------------------------------------------------------------------- def init(self): self.actors = [self.actor1, self.actor2] self.actors_on() #------------------------------------------------------------------------------- def finish(self): self.actors_off() #------------------------------------------------------------------------------- def execute(self): # Check if Timer is Running if self.is_timer_finished() is None: self.start_timer(float(self.timer) * 60) # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("{} complete".format(self.name), "Starting the next step", 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 BaseColletingStep(object): collectingActor = StepProperty.Actor( "Устройство отбора", description="Исполнительное устройство отбора") collectingSensor = StepProperty.Sensor( "Индикатор отбора", description="Индикатор скорости отбора") isPaused = False power = 0 maxSpeed = 0 @cbpi.action("Начать отбор") def start(self): if self.isPaused: self.time = datetime.utcnow() self.notify("", "Отбор продолжен", type="success", timeout=2000) self.isPaused = False @cbpi.action("Остановить отбор") def stop(self): if not self.isPaused: self.notify("", "Отбор приостановлен", type="success", timeout=2000) self.isPaused = True def notifySensor(self): try: sensor = self.api.cache.get("sensors").get( int(self.collectingSensor)).instance sensor.collecting = int(self.collectingSpeed) except: pass def updateMaxCollectingSpeed(self): try: actor = self.api.cache.get("actors").get(int( self.collectingActor)).instance self.maxSpeed = actor.get_max_speed() except: self.maxSpeed = 0 def manageActor(self): actorId = int(self.collectingActor) self.actor_power(power=self.power, id=actorId) if self.isPaused: self.actor_off(actorId) else: self.actor_on(power=self.power, id=actorId) def calculateActorPower(self): if self.maxSpeed > 0: self.power = min( round(float(self.collectingSpeed) / self.maxSpeed * 100), 100)
class Flowmeter(StepBase): sensor = StepProperty.Sensor("Sensor") actorA = StepProperty.Actor("Actor") volume = Property.Number("Volume", configurable=True) resetFlowmeter = Property.Number( "Reset flowmeter when done. 1 = Yes 0 = No", configurable=True, default_value="1") def init(self): if int(self.actorA) is not None: self.actor_on(int(self.actorA)) @cbpi.action("Turn Actor OFF") def start(self): if self.actorA is not None: self.actor_off(int(self.actorA)) def reset(self): if self.actorA is not None: self.actor_off(int(self.actorA)) def finish(self): if self.actorA is not None: self.actor_off(int(self.actorA)) if self.resetFlowmeter == "1": for key, value in list(cbpi.cache.get("sensors").items()): if key == int(self.sensor): value.instance.reset() def execute(self): for key, value in list(cbpi.cache.get("sensors").items()): if key == int(self.sensor): sensorValue = value.instance.getValue() if float(sensorValue) >= float(self.volume): next(self)
class SimpleToggleStep(StepBase): # Properties actor = StepProperty.Actor("Actor") power = Property.Number("Power", configurable=True) toggle_type = Property.Select("Toggle Type", options=["On", "Off", "Power Only"]) def init(self): if self.toggle_type == "On": if self.power is not None and self.power: self.actor_on(int(self.actor), int(self.power)) else: self.actor_on(int(self.actor)) if self.toggle_type == "Off": self.actor_off(int(self.actor)) if self.toggle_type == "Power Only" and self.power is not None and self.power: self.actor_power(int(self.actor), int(self.power)) def finish(self): pass def execute(self): next(self)
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 FlowSensorCalibrate(StepBase): # properties actor_prop = StepProperty.Actor("Actor") sensor_prop = StepProperty.Sensor("Sensor") timer_prop = Property.Number("Timer", configurable=True, default_value=10) threshold_prop = Property.Number( "Flow threshold", configurable=True, default_value=0.1, description="Value at which flow is considered stopped") #------------------------------------------------------------------------------- def init(self): # convert properties to usable attributes self.actor = int(self.actor_prop) self.sensor = cbpi.cache.get("sensors")[int(self.sensor_prop)].instance self.threshold = float(self.threshold_prop) self.flowing = False # reset sensor volume to start calibration self.sensor.reset_volume() # turn on actor self.actor_on(self.actor) # start timer if self.is_timer_finished() is None: self.start_timer(float(self.timer_prop) * 60) #------------------------------------------------------------------------------- def execute(self): sensor_data = self.sensor.read_sensor_data() if (not self.flowing) and (sensor_data['flow'] >= self.threshold): # flow has started cbpi.app.logger.info( "FlowSensor '{}' calibrate flow started".format( self.sensor.name)) self.flowing = True elif (self.flowing) and (sensor_data['flow'] <= self.threshold): # flow has stopped cbpi.app.logger.info( "FlowSensor '{}' calibrate flow stopped".format( self.sensor.name)) self.next() elif self.is_timer_finished() == True: # timer has expired cbpi.app.logger.info( "FlowSensor '{}' calibrate timer expired".format( self.sensor.name)) self.next() #------------------------------------------------------------------------------- def finish(self): # turn off actor self.actor_off(self.actor) # notify complete and total pulses sensor_data = self.sensor.read_sensor_data() self.notify("Flow Sensor Calibration Complete", self.sensor.name, type="success", timeout=None) self.notify("Pulse Count: {}".format(sensor_data['count']), self.sensor.name, type="info", timeout=None) #------------------------------------------------------------------------------- def reset(self): self.actor_off(self.actor)
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 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 SpargeStep(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("Boil Kettle") kettle2 = StepProperty.Kettle("HLT Kettle") actor1 = StepProperty.Actor("Wort Pump") actor2 = StepProperty.Actor("Water Pump") sensor1 = StepProperty.Sensor("BK Volume Sensor") sensor2 = StepProperty.Sensor("HLT Volume Sensor") volume1 = Property.Number("BK Volume Target", configurable=True) volume2 = Property.Number("HLT Volume Min", configurable=True) volumeStart = Property.Number("HLT Volume Start", configurable=True) volumeDiff = Property.Number("Volume Difference", configurable=True) timer = Property.Number("Timer in Minutes", configurable=True) temp = Property.Number("BK Temp", configurable=True) volumeBoil = Property.Number("BK Boil Volume", configurable=True) volumeState1 = 0 volumeState2 = 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) def finish(self): self.set_target_temp(0, self.kettle2) def execute(self): ''' This method is execute in an interval :return: ''' sensorValue1 = cbpi.get_sensor_value(int(self.sensor1)) sensorValue2 = cbpi.get_sensor_value(int(self.sensor2)) volumeChange1 = float(sensorValue1) - float(self.volumeState1) volumeChange2 = float(self.volumeState2) - float(sensorValue2) kettle1_state = cbpi.cache.get("kettle")[int(self.kettle1)].state kettle2_state = cbpi.cache.get("kettle")[int(self.kettle2)].state for key, value in cbpi.cache["actors"].iteritems(): if key == int(self.actor1): actorState1 = value.state if key == int(self.actor2): actorState2 = value.state if float(sensorValue1) >= float( self.volumeBoil) and kettle1_state is False: Kettle2View().toggle(int(self.kettle1)) self.notify("Kettle Update", "Kettle 1 Auto is on.", timeout=None) self.set_target_temp(self.temp, self.kettle1) # Check if kettle2 volume limit reached if float(sensorValue2) <= float(self.volume2): if kettle2_state is True: self.set_target_temp(0, self.kettle2) Kettle2View().toggle(int(self.kettle2)) self.notify("Kettle Update", "Kettle 2 Auto is off.", timeout=None) if self.is_timer_finished() is None: self.start_timer(int(self.timer) * 60) # Make sure kettle1 hasn't reached target if float(sensorValue1) < float(self.volume1): self.actor_on(int(self.actor1)) if self.is_timer_finished() == True: self.actor_off(int(self.actor2)) else: self.actor_on(int(self.actor2)) else: if self.volumeState1 > 0: totalDiff = float(volumeChange2) - float(volumeChange1) if totalDiff > 0: if abs(totalDiff) > float(self.volumeDiff): self.actor_off(int(self.actor2)) self.actor_on(int(self.actor1)) else: self.actor_on(int(self.actor2)) else: if abs(totalDiff) > float(self.volumeDiff): self.actor_off(int(self.actor1)) self.actor_on(int(self.actor2)) else: self.actor_on(int(self.actor1)) else: if volumeChange >= 10: self.volumeState2 = sensorValue2 self.volumeState1 = sensorValue1 # Check if kettle1 target volume has been reached if float(sensorValue1) >= float(self.volume1): self.set_target_temp(0, self.kettle2) self.stop_timer() self.start_timer(0) self.notify("Sparge Complete!", "Starting the next step.", timeout=None) self.next()
class FlowSensorTransfer(StepBase): a_sensor_prop = StepProperty.Sensor( "Flow Sensor", description="Sensor that contols this step") b_actor1_prop = StepProperty.Actor( "Actor 1", description="Actor to turn on for the duration of this step") c_actor2_prop = StepProperty.Actor( "Actor 2", description="Actor to turn on for the duration of this step") d_volume_prop = Property.Number( "Target Volume", configurable=True, description="Leave blank to continue until flow stops") e_reset_start_prop = Property.Select("Reset sensor at start?", options=["Yes", "No"]) f_reset_finish_prop = Property.Select("Reset sensor at finish?", options=["Yes", "No"]) g_threshold_prop = Property.Number( "Flow threshold", configurable=True, default_value=0.1, description="Value at which flow is considered stopped") #------------------------------------------------------------------------------- def init(self): # convert properties to usable attributes self.sensor = cbpi.cache.get("sensors")[int( self.a_sensor_prop)].instance self.actors = [self.b_actor1_prop, self.c_actor2_prop] try: self.target_volume = float(self.d_volume_prop) except: self.target_volume = 0.0 self.reset_start = self.e_reset_start_prop == "Yes" self.reset_finish = self.f_reset_finish_prop == "Yes" try: self.threshold = float(self.g_threshold_prop) except: self.threshold = 0.1 self.flowing = False # reset sensor volume if indicated if self.reset_start: self.sensor.reset_volume() # turn on actors self.actors_on() #------------------------------------------------------------------------------- def execute(self): sensor_data = self.sensor.read_sensor_data() if (not self.flowing) and (sensor_data['flow'] >= self.threshold): # flow has started cbpi.app.logger.info( "FlowSensor '{}' transfer flow started".format( self.sensor.name)) self.flowing = True elif (self.flowing) and (sensor_data['flow'] <= self.threshold): # flow has stopped cbpi.app.logger.info( "FlowSensor '{}' transfer flow stopped".format( self.sensor.name)) self.next() elif self.target_volume and (sensor_data['volume'] >= self.target_volume): # target volume reached cbpi.app.logger.info( "FlowSensor '{}' transfer target volume reached".format( self.sensor.name)) self.next() #------------------------------------------------------------------------------- def finish(self): # turn actors off self.actors_off() # notify complete and total volume sensor_data = self.sensor.read_sensor_data() self.notify("{} complete".format(self.name), "Total Volume: {:.2f}{}".format(sensor_data['volume'], self.sensor.volume_units), timeout=None) # reset sensor volume if indicated if self.reset_finish: self.sensor.reset_volume() #------------------------------------------------------------------------------- def reset(self): self.actors_off() #------------------------------------------------------------------------------- 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 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 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 Mod_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") # Temp Protection modification start protection_temp_diff = Property.Number( "Temperature Difference", configurable=True, description= "Treshold Temperature = Temperature + Temperature Difference. Timer will stop if this temperature si reached and brewing procces will be interrupted", default_value=8) protection_actor = StepProperty.Actor( "Portective Circuit Actor", description= "Please select an Actor which will be activated if treshold temperature is reached" ) s = False # Temp Protection modification end 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.s = False #de-activate temperature protection self.actor_off(self.protection_actor) @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) # Temp Protection modification start #de-activate temperature protection self.actor_off(self.protection_actor) s = False # Temp Protection modification end def finish(self): self.set_target_temp(0, self.kettle) # Temp Protection modification start #de-activate temperature protection self.actor_off(self.protection_actor) s = False # Temp Protection modification end 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) #Temp protection start modification and overshoot deteection. If temperature is over N Celsius, protection will stop the process if (int(self.get_kettle_temp(self.kettle)) >= (int(self.temp) + int(self.protection_temp_diff))) and self.s is False: self.s = True self.notify( "ALARM", "Exiting brewing process - critical temperature is reached", timeout=0, type="danger") #Activate temperature protection self.actor_on(self.protection_actor) #Dont go to the next step, show "Alarm" and remaining time self.notify(str( time.strftime('%H:%M:%S', time.gmtime(int((self.timer_remaining()))))), "time until the end of this step", timeout=0) #Reset and stop timer self.timer_end = True #Temp protection end modification # Check if timer finished and go to next step if self.is_timer_finished() == True and self.s == False: self.s = True self.notify("Mash Step Completed!", "Starting the next step", timeout=None) next(self)
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 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()