class SelfLearning433Mhz(ActorBase): oncode = Property.Text("Switch ON code", configurable=True, default_value="") offcode = Property.Text("Switch OFF code", configurable=True, default_value="") def send(self, code): try: call([ "sudo /home/pi/433Utils/RPi_utils/./codesend %s 4 355" % (code) ], shell=True) except Exception as e: self.api.app.logger.error("FAILED to switch 433Mhz Code: %s" % (code)) self.api.app.logger.info("SWITCHED Selflearning 433Mhz Code: %s" % (code)) def on(self, power=100): self.send(self.oncode) self.api.app.logger.info("SWITCHED Selflearning 433Mhz Socket: %s" % (self.oncode)) def off(self): self.send(self.offcode) self.api.app.logger.info("SWITCHED Selflearning 433Mhz Socket: %s" % (self.offcode))
class MQTT_SENSOR(SensorActive): a_topic = Property.Text("Topic", configurable=True, default_value="", description="MQTT Topic") b_payload = Property.Text("Payload Directory", configurable=True, default_value="", description="Where to find the message in the payload, leave blank for raw payload") c_unit = Property.Text("Unit", configurable=True, default_value="", description="Displayed Unit") d_offset = Property.Number("Offset", configurable=True, default_value="0", description="Offset relative to sensor data") last_value = None send_value = 0 def init(self): self.topic = self.a_topic if self.b_payload == "": self.payload_text = None else: self.payload_text = self.b_payload.split('.') self.unit = self.c_unit[0:3] SensorActive.init(self) def on_message(client, userdata, msg): try: json_data = json.loads(msg.payload) val = json_data if self.payload_text is not None: for key in self.payload_text: val = val.get(key, None) if isinstance(val, (int, float, basestring)): q.put({"id": on_message.sensorid, "value": val}) except Exception as e: print e on_message.sensorid = self.id self.api.cache["mqtt"].client.subscribe(self.topic) self.api.cache["mqtt"].client.message_callback_add( self.topic, on_message) def get_value(self): try: self.send_value = round( float(self.last_value) + float(self.d_offset), 2) except Exception as e: pass return {"value": self.send_value, "unit": self.unit} def get_unit(self): return self.unit def stop(self): self.api.cache["mqtt"].client.unsubscribe(self.topic) SensorActive.stop(self) def execute(self): ''' Active sensor has to handle his own loop :return: ''' self.sleep(5)
class GPIOPWM(ActorBase): gpio = Property.Select("GPIO", options=[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 ]) duty_cylce = Property.Number("Duty Cycle", configurable=True) p = None power = 100 def init(self): GPIO.setup(int(self.gpio), GPIO.OUT) GPIO.output(int(self.gpio), 0) def on(self, power=None): if power is not None: self.power = int(power) if self.duty_cylce is None: duty_cylce = 50 self.p = GPIO.PWM(int(self.gpio), int(self.duty_cylce)) self.p.start(int(self.power)) def set_power(self, power): if power is not None: self.power = int(power) self.p.ChangeDutyCycle(self.power) def off(self): print "GPIO OFF" self.p.stop()
class iSpindel(SensorActive): key = Property.Text(label="iSpindel Name", configurable=True, description="Enter the name of your iSpindel") sensorType = Property.Select("Data Type", options=["Temperature", "Gravity", "Battery"], description="Select which type of data to register for this sensor") tuningPolynom = Property.Text(label="Tuning Polynomial", configurable=True, default_value="tilt", description="Enter your iSpindel polynomial. Use the variable tilt for the angle reading from iSpindel. Does not support ^ character.") unitsGravity = Property.Select("Gravity Units", options=["SG", "Brix", "°P"], description="Displays gravity reading with this unit if the Data Type is set to Gravity. Does not convert between units, to do that modify your polynomial.") def get_unit(self): if self.sensorType == "Temperature": return "°C" if self.get_config_parameter("unit", "C") == "C" else "°F" elif self.sensorType == "Gravity": return self.unitsGravity elif self.sensorType == "Battery": return "V" else: return " " def stop(self): pass def execute(self): global cache while self.is_running(): try: if cache[self.key] is not None: if self.sensorType == "Gravity": reading = calcGravity(self.tuningPolynom, cache[self.key]['Angle'], self.unitsGravity) else: reading = cache[self.key][self.sensorType] self.data_received(reading) except: pass self.api.socketio.sleep(1)
class WiiSensor(SensorPassive): offset = Property.Number('Offset', True, 0) unit_of_measure = Property.Select('Unit of measure', ['KG', 'LBS']) def init(self): self.t = WiiThread() def shudown(): shudown.cb.shutdown() shudown.cb = self.t self.t.start() def stop(self): try: self.t.stop() except: pass def get_unit(self): if self.unit_of_measure == 'KG': return 'KG' else: return 'LBS' def read(self): if self.unit_of_measure == 'KG': self.data_received(round(self.t.value + float(self.offset), 2)) else: self.data_received( round(self.t.value * 2.20462262185 + float(self.offset), 2))
class PIDArduino(KettleController): a_p = Property.Number("P", True, 0) b_i = Property.Number("I", True, 0) c_d = Property.Number("D", True, 0) d_max_out = Property.Number("max. output %", True, 100) def run(self): sampleTime = 5 wait_time = 5 p = float(self.a_p) i = float(self.b_i) d = float(self.c_d) maxout = float(self.d_max_out) pid = PIDArduino(sampleTime, p, i, d, 0, maxout) while self.is_running(): heat_percent = pid.calc(self.get_temp(), self.get_target_temp()) heating_time = sampleTime * heat_percent / 100 wait_time = sampleTime - heating_time if heating_time == sampleTime: self.heater_on(100) self.sleep(heating_time) elif wait_time == sampleTime: self.heater_off() self.sleep(wait_time) else: self.heater_on(100) self.sleep(heating_time) self.heater_off() self.sleep(wait_time)
class eManometer(SensorActive): key = Property.Text(label="eManometer Name", configurable=True, description="Enter the name of your eManometer") sensorType = Property.Select( "Data Type", options=["Temperature", "Pressure", "CO2"], description="Select which type of data to register for this sensor") def get_unit(self): if self.sensorType == "Temperature": return "°C" if self.get_config_parameter("unit", "C") == "C" else "°F" elif self.sensorType == "Pressure": return "bar" elif self.sensorType == "CO2": return "g/l" else: return " " def stop(self): pass def execute(self): global cache while self.is_running(): try: if cache[self.key] is not None: reading = cache[self.key][self.sensorType] self.data_received(reading) except: pass self.api.socketio.sleep(1)
class Hysteresis(FermenterController): heater_offset_min = Property.Number("Heater Offset min", True, 0) heater_offset_max = Property.Number("Heater Offset max", True, 0) cooler_offset_min = Property.Number("Cooler Offset min", True, 0) cooler_offset_max = Property.Number("Cooler Offset max", True, 0) def stop(self): super(FermenterController, self).stop() self.heater_off() self.cooler_off() def run(self): while self.is_running(): target_temp = self.get_target_temp() temp = self.get_temp() if temp + int(self.heater_offset_min) < target_temp: self.heater_on(100) if temp + int(self.heater_offset_max) > target_temp: self.heater_off() if temp > target_temp + int(self.cooler_offset_min): self.cooler_on(100) if temp < target_temp + int(self.cooler_offset_max): self.cooler_off() self.sleep(1)
class PIDPWM(KettleController): a_p = Property.Number("P", True, 0) b_i = Property.Number("I", True, 0) c_d = Property.Number("D", True, 0) d_max_out = Property.Number("max. output %", True, 100) def run(self): sampleTime = 5 p = float(self.a_p) i = float(self.b_i) d = float(self.c_d) maxout = float(self.d_max_out) pid = PIDArduino(sampleTime, p, i, d, 0, maxout) self.heater_on(0) while self.is_running(): heat_percent = pid.calc(self.get_temp(), self.get_target_temp()) print(heat_percent) self.actor_power(round(heat_percent)) self.sleep(sampleTime) def stop(self): super(KettleController, self).stop() self.heater_off()
class brewBubbles(SensorActive): log("cbpi_brewbubbles Start Instancing") key = Property.Text(label="BrewBubbles Name", configurable=True, description="Enter the name of your BrewBubbles") sensorType = Property.Select( "Data Type", options=["BPM", "Room Temp.", "Vessel Temp."], description="Select which type of data to register for this sensor") log("cbpi_brewbubbles continue Instancing") def get_unit(self): if self.sensorType == "Temperature": if self.get_config_parameter("unit", "C") == "C": return "°C" else: return "°F" else: return " " def stop(self): pass def execute(self): global cache while self.is_running(): try: if cache[self.key] is not None: reading = cache[self.key][self.sensorType] self.data_received(reading) except: pass self.api.socketio.sleep(1)
class LauteringAutomation(KettleController): minSensorDistance = Property.Number( "Minimum Distance to Sensor (High Fill Level)", True, 0) maxSensorDistance = Property.Number( "Maximum Distance to Sensor (Low Fill Level)", True, 0) pumping = False currentFillLevel = 0 def stop(self): super(KettleController, self).stop() self.heater_off() def run(self): self.sleep(1) while self.is_running(): self.currentFillLevel = self.get_temp() if bool(self.pumping): if self.currentFillLevel >= float(self.maxSensorDistance): self.heater_off() self.pumping = False else: self.heater_on(100) else: if self.currentFillLevel <= float(self.minSensorDistance): self.heater_on(100) self.pumping = True else: self.heater_off() self.sleep(1)
class Message_CG(StepBase): messagetodisplay = Property.Text("Message To Display", configurable=True, default_value="Message you want to display", description="Message to be displayed") timer = Property.Number("Seconds to wait for next step (use 0 to wait for user)?", configurable=True, default_value=1, description="How long should the brew session wait before continuing? If you select 0 then it will wait for user to click Start Next Step.") s = False @cbpi.action("Start Timer") def start(self): self.s = False if self.is_timer_finished() is None: self.start_timer(int(self.timer)+1) def reset(self): self.stop_timer() def execute(self): if self.is_timer_finished() is None: self.start_timer(int(self.timer)+1) if self.s == False: self.s = True if int(self.timer) == 0: self.notify(self.messagetodisplay, "Please select \"Next Step\" to continue", type="warning", timeout=None) else: self.notify(self.messagetodisplay, "Brewing will continue automatically when the timer completes.", type="info", timeout=((int(self.timer)-1)*1000)) if self.is_timer_finished() == True: if int(self.timer) == 0: pass else: self.next()
class MQTTActor_Compressor(ActorBase): topic = Property.Text("Topic", configurable=True, default_value="", description="MQTT TOPIC") delay = Property.Number("Compressor Delay", configurable=True, default_value=10, description="minutes") on_payload = Property.Text("On Payload", configurable=True, default_value='{"state": "on"}', description="Payload to send to turn the actor on") off_payload = Property.Text("Off Payload", configurable=True, default_value='{"state": "off"}', description="Payload to send to turn the actor off") compressor_on = False compressor_wait = datetime.utcnow() delayed = False def init(self): super(MQTTActor_Compressor, self).init() cbpi.MQTTActor_Compressors += [self] def on(self, power=100): if datetime.utcnow() >= self.compressor_wait: self.compressor_on = True self.api.cache["mqtt"].client.publish(self.topic, payload=self.on_payload, qos=2, retain=True) self.delayed = False else: print "Delaying Turing on Compressor" cbpi.app.logger.info("Delaying Turing on Compressor") self.delayed = True def off(self): if self.compressor_on: self.compressor_on = False self.compressor_wait = datetime.utcnow() + timedelta(minutes=int(self.delay)) self.delayed = False self.api.cache["mqtt"].client.publish(self.topic, payload=self.off_payload, qos=2, retain=True)
class OnAtStartup(ActorBase): actor_setting = Property.Actor( "Actor", description="Select the actor you would like to have ON at startup") power_setting = Property.Number( "Power", True, 100, description="Select the power of the actor at startup") def init(self): if not 0 <= int(self.power_setting) <= 100: self.api.notify(headline="OnAtStartup Error", message="Power must be between 0 and 100", timeout=None, type="danger") raise ValueError("Power must be between 0 and 100") else: self.api.switch_actor_on(int(self.actor_setting), power=int(self.power_setting)) def set_power(self, power): pass def off(self): pass def on(self, power=None): pass
class MQTTActor_Compressor(ActorBase): topic = Property.Text("Topic", configurable=True, default_value="", description="MQTT TOPIC") delay = Property.Number( "Compressor Delay", configurable=True, default_value=10, description="minutes") compressor_on = False compressor_wait = datetime.utcnow() delayed = False def init(self): super(MQTTActor_Compressor, self).init() cbpi.MQTTActor_Compressors += [self] print "Initially setting MQTT Actor Compressor to off.." self.off() def on(self, power=100): if datetime.utcnow() >= self.compressor_wait: self.compressor_on = True self.api.cache["mqtt"].client.publish(self.topic, payload=json.dumps( {"on": True, "power": 100}), qos=2, retain=True) self.delayed = False else: cbpi.app.logger.info("Delaying Turing on Compressor") self.delayed = True def off(self): if self.compressor_on: self.compressor_on = False self.compressor_wait = datetime.utcnow() + timedelta(minutes=int(self.delay)) self.delayed = False self.api.cache["mqtt"].client.publish( self.topic, payload=json.dumps({"on": False}), qos=2, retain=True)
class Hysteresis(KettleController): # Custom Properties on = Property.Number("Offset On", True, 0, description="Offset below target temp when heater should switched on. Should be bigger then Offset Off") off = Property.Number("Offset Off", True, 0, description="Offset below target temp when heater should switched off. Should be smaller then Offset Off") def stop(self): ''' Invoked when the automatic is stopped. Normally you switch off the actors and clean up everything :return: None ''' super(KettleController, self).stop() self.heater_off() def run(self): ''' Each controller is exectuted in its own thread. The run method is the entry point :return: ''' while self.is_running(): if self.get_temp() < self.get_target_temp() - float(self.on): self.heater_on(100) elif self.get_temp() >= self.get_target_temp() - float(self.off): self.heater_off() else: self.heater_off() self.sleep(1)
class HeartsStep(StepBase, BaseColletingStep): temperatureSensor = StepProperty.Sensor( "Датчик температуры", description="Датчик температуры в кубе") initialCollecting = Property.Number("Стартовая скорость отбора, мл/ч", configurable=True, default_value=1000) endTemp = Property.Number("Температура завершения отбора", configurable=True, default_value=93) collectingSpeed = 0.0 temperature = 0 def finish(self): self.actor_off(int(self.collectingActor)) self.notify("", "Отбор тела завершен", type="success", timeout=2000) def execute(self): self.updateAndCheckTemperature() self.recountCollecting() self.notifySensor() self.updateMaxCollectingSpeed() self.calculateActorPower() self.manageActor() def recountCollecting(self): self.collectingSpeed = int( self.initialCollecting) * (6.04 - 0.06 * float(self.temperature)) def updateAndCheckTemperature(self): self.temperature = float( self.get_sensor_value(int(self.temperatureSensor))) if self.temperature >= int(self.endTemp): next(self)
class Socket433MHz(ActorBase): code_on = Property.Text( "ON Code", configurable=True, description="Enter the code for turning the 433Hz socket on") code_off = Property.Text( "OFF Code", configurable=True, description="Enter the code for turning the 433Hz socket off") pin = Property.Text( "GPIO PIN", configurable=True, description="Enter the code GPIO PIN for the rf transmitter") def on(self, power=100): logging.basicConfig( level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S', format='%(asctime)-15s - [%(levelname)s] %(module)s: %(message)s', ) rfdevice = RFDevice(int(self.pin)) rfdevice.enable_tx() rfdevice.tx_code(int(self.code_on), None, None) rfdevice.cleanup() def off(self): logging.basicConfig( level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S', format='%(asctime)-15s - [%(levelname)s] %(module)s: %(message)s', ) rfdevice = RFDevice(int(self.pin)) rfdevice.enable_tx() rfdevice.tx_code(int(self.code_off), None, None) rfdevice.cleanup()
class SimpleManualStep(StepBase): # Properties heading = Property.Text("Heading", configurable=True, default_value="Step Alert", description="First line of notification.") message = Property.Text("Message", configurable=True, default_value="Press next button to continue", description="Second line of notification.") notifyType = Property.Select( "Type", options=["success", "info", "warning", "danger"]) proceed = Property.Select( "Next Step", options=["Pause", "Continue"], description= "Whether or not to automatically continue to the next brew step.") #------------------------------------------------------------------------------- def init(self): if self.notifyType not in ["success", "info", "warning", "danger"]: self.notifyType = "info" self.notify(self.heading, self.message, type=self.notifyType, timeout=None) if self.proceed == "Continue": next(self)
class HeadsStep(StepBase, BaseColletingStep): collectingSpeed = Property.Number("Скорость отбора, мл/ч", configurable=True, default_value=100) headsTotal = Property.Number("Объем голов для отбора, мл", configurable=True, default_value=100) total = 0 time = datetime.utcnow() def finish(self): self.actor_off(int(self.collectingActor)) self.notify("", "Отбор голов завершен", type="success", timeout=2000) def execute(self): self.updateMaxCollectingSpeed() self.calculateActorPower() self.manageActor() self.checkTotalCollecting() self.notifySensor() def checkTotalCollecting(self): time = datetime.utcnow() if (time - self.time).total_seconds() >= 10: self.time = time self.total += float(self.collectingSpeed) / 360.0 if self.total >= int(self.headsTotal): next(self)
class Hysteresis(KettleController): # Custom Properties on = Property.Number("Offset On", True, 0) off = Property.Number("Offset Off", True, 0) def stop(self): ''' Invoked when the automatic is stopped. Normally you switch off the actors and clean up everything :return: None ''' super(KettleController, self).stop() self.heater_off() def run(self): ''' Each controller is exectuted in its own thread. The run method is the entry point :return: ''' while self.is_running(): self.actor_power(50) if self.get_temp() < self.get_target_temp() - int(self.on): self.heater_on(100) elif self.get_temp() >= self.get_target_temp() - int(self.off): self.heater_off() else: self.heater_off() self.sleep(1)
class HopDropperActor(ActorBase): gpio = Property.Select("GPIO", options=[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 ], description="GPIO to which the actor is connected") timeout = Property.Number( "Timeout", configurable=True, default_value=2, description="After how many seconds the actor should switch off again") def init(self): GPIO.setup(int(self.gpio), GPIO.OUT) GPIO.output(int(self.gpio), 0) def on(self, power=0): def toggleTimeJob(id, t): self.api.socketio.sleep(t) self.api.switch_actor_off(int(id)) if self.timeout: t = self.api.socketio.start_background_task(target=toggleTimeJob, id=self.id, t=int(self.timeout)) GPIO.output(int(self.gpio), 1) def off(self): GPIO.output(int(self.gpio), 0)
class Mod_PWM(ActorBase): gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]) frequency = Property.Number("Cycles Per Second", configurable=True) power = 100 p = None stopped = True def init(self): GPIO.setup(int(self.gpio), GPIO.OUT) GPIO.output(int(self.gpio), 0) def on(self, power): self.stopped = False if self.p is None: if self.frequency is None: self.frequency = 50 self.p = GPIO.PWM(int(self.gpio), int(self.frequency)) self.p.start(int(Mod_PWM.power)) if power is not None: self.p.ChangeDutyCycle(int(power)) else: self.p.ChangeDutyCycle(int(Mod_PWM.power)) def set_power(self, power): if power is not None: Mod_PWM.power = power if self.stopped is False: self.p.ChangeDutyCycle(int(Mod_PWM.power)) def off(self): self.stopped = True self.p.ChangeDutyCycle(0)
class HTTPActor(ActorBase): a_url = Property.Text("Controller Address", configurable=True, default_value="http://<ipaddress>:<port>", description="Address of the controller. Do not add a trailing slash (ex: http://192.168.0.10)") b_on = Property.Text("On Command", configurable=True, default_value="control?cmd=GPIO,<pin>,1", description="Command to turn actor on") c_off = Property.Text("Off Command", configurable=True, default_value="control?cmd=GPIO,<pin>,0", description="Command to turn actor off") d_pow = Property.Text("Power Command", configurable=True, default_value="control?cmd=PWM,<pin>,", description="Command to set actor power level. Power level will be added to the end of the command. If device does not support this, make this field blank.") power = 100 def send(self, command): try: h = httplib2.Http(".cache") ## Sending http command "" (resp, content) = h.request("%s/%s" % (self.a_url, command), "GET", headers={'cache-control':'no-cache'}) except Exception as e: self.api.app.logger.error("FAILED to switch HTTP actor: %s/%s" % (self.a_url, command)) def on(self, power=None): self.send(self.b_on) if power is not None: self.set_power(power) def off(self): self.send(self.c_off) def set_power(self, power): if power is not None and self.d_pow is not None and self.d_pow: if power != self.power: self.power = int(power) self.send("%s%s" % (self.d_pow, power))
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 PIDHendi(KettleController): P = Property.Number("P", configurable=True, default_value=40, unit="") I = Property.Number("I", configurable=True, default_value=140, unit="") D = Property.Number("D", configurable=True, default_value=0, unit="") Pmax = Property.Number("Max Power", configurable=True, default_value=100, unit="%") def run(self): p = float(self.P) i = float(self.I) d = float(self.D) pmax = int(self.Pmax) ts = 5 print((p, i, d, pmax)) pid = PID(ts, p, i, d, pmax) while self.is_running(): heat_percent = pid.calc(self.get_sensor_value(), self.get_target_temp()) if heat_percent == 0: self.actor_power(heat_percent) self.heater_off() cbpi.log_action("PIDHendi OFF {}") else: self.actor_power(heat_percent) self.heater_on(power=heat_percent) cbpi.log_action("PIDHendi calling heater_on(power={})".format(heat_percent)) self.sleep(ts) self.heater_off()
class SimpleBoilLogic(KettleController): ramp_power = Property.Number("Ramp Up Power %", True, 100) boil_power = Property.Number("Boil Power %", True, 70) def stop(self): super(KettleController, self).stop() self.heater_off() def run(self): r_power = int(self.ramp_power) b_power = int(self.boil_power) while self.is_running(): temp = self.get_temp() r_limit = self.get_target_temp() if temp < r_limit and r_limit > 0: self.heater_on(r_power) self.actor_power(r_power) if temp >= r_limit and r_limit > 0: self.heater_on(b_power) self.actor_power(b_power) self.sleep(1)
class TrailingAverageSensor(SensorPassive): sensor_prop = Property.Sensor("Sensor", description="Select a sensor to average readings of.") count_prop = Property.Number("Count", configurable=True, default_value=12, description="Number of readings to average.") decimals_prop = Property.Number("Decimals", configurable=True, default_value=1, description="How many decimals to round the average to.") #------------------------------------------------------------------------------- def init(self): self.values = list() self.sensor_id = int(self.sensor_prop) self.count = int(self.count_prop) self.weight = 1.0/self.count self.decimals = int(self.decimals_prop) #------------------------------------------------------------------------------- def read(self): self.values.append(float(cbpi.cache.get("sensors")[int(self.sensor_id)].instance.last_value)) while len(self.values) > self.count: self.values.pop(0) numerator = 0.0 denominator = 0.0 weight = 1.0 for value in reversed(self.values): numerator += value * weight denominator += weight weight = weight - self.weight self.data_received(round(numerator/denominator, self.decimals)) #------------------------------------------------------------------------------- def get_unit(self): return cbpi.cache.get("sensors")[int(self.sensor_id)].instance.get_unit() #------------------------------------------------------------------------------- def stop(self): pass
class Hysteresis(FermenterController): heater_offset_min = Property.Number("Heater Offset ON", True, 0, description="Offset as decimal number when the heater is switched on. Should be greater then 'Heater Offset OFF'. For example a value of 2 switches on the heater if the current temperature is 2 degrees below the target temperature") heater_offset_max = Property.Number("Heater Offset OFF", True, 0, description="Offset as decimal number when the heater is switched off. Should be smaller then 'Heater Offset ON'. For example a value of 1 switches off the heater if the current temperature is 1 degree below the target temperature") cooler_offset_min = Property.Number("Cooler Offset ON", True, 0, description="Offset as decimal number when the cooler is switched on. Should be greater then 'Cooler Offset OFF'. For example a value of 2 switches on the cooler if the current temperature is 2 degrees above the target temperature") cooler_offset_max = Property.Number("Cooler Offset OFF", True, 0, description="Offset as decimal number when the cooler is switched off. Should be less then 'Cooler Offset ON'. For example a value of 1 switches off the cooler if the current temperature is 1 degree above the target temperature") def stop(self): super(FermenterController, self).stop() self.heater_off() self.cooler_off() def run(self): while self.is_running(): target_temp = self.get_target_temp() temp = self.get_temp() if temp + float(self.heater_offset_min) <= target_temp: self.heater_on(100) if temp + float(self.heater_offset_max) >= target_temp: self.heater_off() if temp >= target_temp + float(self.cooler_offset_min): self.cooler_on(100) if temp <= target_temp + float(self.cooler_offset_max): self.cooler_off() self.sleep(1)
class GPIOinput(SensorActive): gpio = Property.Select("GPIO", options=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]) input_type = Property.Select("Input Type", options=["Momentary","Latch Rise", "Latch Fall"], description="Momentary - input high = high val. Latched - pulse on, pulse off") pud_type = Property.Select("Pull Up/Down", options=["Pull Up","Pull Down","Off"], description="Pull Up or down ressitor on input") on_val = Property.Number("High Value", configurable=True, default_value="0", description="Read value when input is high or Latch True(3.3V)") off_val = Property.Number("Low Value", configurable=True, default_value="100", description="Read value when sensor is low or Latch False (0V)") out_val = [0,1] input_on = False latch_val = False def init(self): self.input_on = False self.trigger_val = None try: GPIO.setup(int(self.gpio), GPIO.IN , pull_up_down = PUD_map[self.pud_type]) GPIO.remove_event_detect(int(self.gpio)) GPIO.add_event_detect(int(self.gpio), GPIO.BOTH, callback=self.IO_trigger, bouncetime=20) self.out_val = [self.off_val,self.on_val] self.latch_val = trig_level[self.input_type] super(GPIOinput,self).init() print "Init Complete" self.data_received(self.out_val[self.input_on]) except Exception as e: print e def get_unit(self): unit = "NA" return unit def IO_trigger(self, channel): self.sleep(0.0005) self.trigger_val = GPIO.input(int(self.gpio)) if self.input_type[0] == "L": if self.trigger_val != self.latch_val: self.trigger_val = None def execute(self): while self.is_running(): self.api.socketio.sleep(.1) #if GPIO.event_detected(int(self.gpio)): if self.trigger_val is not None: # if we're here, an edge was recognized #self.sleep(0.01) # debounce if self.input_type[0] == "M": self.input_on = GPIO.input(int(self.gpio)) else: #Latch if self.trigger_val == self.latch_val: self.input_on = not self.input_on self.data_received(self.out_val[self.input_on]) self.trigger_val = None def stop(self): self.__running = False GPIO.cleanup([int(self.gpio)]) GPIO.remove_event_detect(int(self.gpio))