import time from flask import request from flask_classy import route from modules import DBModel, cbpi, get_db from modules.core.baseview import BaseView class Fermenter(DBModel): __fields__ = ["name", "brewname", "sensor", "sensor2", "sensor3", "heater", "cooler", "logic", "config", "target_temp"] __table_name__ = "fermenter" __json_fields__ = ["config"] class FermenterStep(DBModel): __fields__ = ["name", "days", "hours", "minutes", "temp", "direction", "order", "state", "start", "end", "timer_start", "fermenter_id"] __table_name__ = "fermenter_step" @classmethod def get_by_fermenter_id(cls, id): cur = get_db().cursor() cur.execute("SELECT * FROM %s WHERE fermenter_id = ?" % cls.__table_name__,(id,)) result = [] for r in cur.fetchall(): result.append(cls(r)) return result @classmethod def get_max_order(cls,id): cur = get_db().cursor() cur.execute("SELECT max(fermenter_step.'order') as 'order' FROM %s WHERE fermenter_id = ?" % cls.__table_name__, (id,)) r = cur.fetchone() return r.get("order") @classmethod def update_state(cls, id, state): cur = get_db().cursor() cur.execute("UPDATE %s SET state = ? WHERE id =?" % cls.__table_name__, (state, id)) get_db().commit() @classmethod def update_timer(cls, id, timer): cur = get_db().cursor() cur.execute("UPDATE %s SET timer_start = ? WHERE id =?" % cls.__table_name__, (timer, id)) get_db().commit() @classmethod def get_by_state(cls, state): cur = get_db().cursor() cur.execute("SELECT * FROM %s WHERE state = ?" % cls.__table_name__, state) r = cur.fetchone() if r is not None: return cls(r) else: return None @classmethod def reset_all_steps(cls,id): cur = get_db().cursor() cur.execute("UPDATE %s SET state = 'I', start = NULL, end = NULL, timer_start = NULL WHERE fermenter_id = ?" % cls.__table_name__, (id,)) get_db().commit() class FermenterView(BaseView): model = Fermenter cache_key = "fermenter" def _post_post_callback(self, m): m.state = False m.steps = [] def _pre_put_callback(self, m): m.state = False try: m.instance.stop() except: pass def _post_put_callback(self, m): m.state = False self.reset(int(m.id)) @route('//targettemp/', methods=['POST']) def postTargetTemp(self, id, temp): if temp is None or not temp: return ('', 500) id = int(id) temp = float(temp) cbpi.cache.get(self.cache_key)[id].target_temp = float(temp) self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__) cbpi.emit("UPDATE_FERMENTER_TARGET_TEMP", {"id": id, "target_temp": temp}) return ('', 204) @route('//brewname', methods=['POST']) def postBrewName(self, id): data = request.json brewname = data.get("brewname") cbpi.cache.get(self.cache_key)[id].brewname = brewname self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__) cbpi.emit("UPDATE_FERMENTER_BREWNAME", {"id": id, "brewname": brewname}) return ('', 204) @classmethod def post_init_callback(cls, obj): obj.steps = FermenterStep.get_by_fermenter_id(obj.id) obj.state = False @route('//step', methods=['POST']) def postStep(self, id): data = request.json order_max = FermenterStep.get_max_order(id) order = order_max + 1 if order_max is not None else 1 data["order"] = order data["days"] = 0 if data["days"] == "" else data["days"] data["hours"] = 0 if data["hours"] == "" else data["hours"] data["minutes"] = 0 if data["minutes"] == "" else data["minutes"] data["temp"] = 0 if data["temp"] == "" else data["temp"] data["state"] = "I" data["name"] = "NO NAME" if data["name"] == "" else data["name"] f = FermenterStep.insert(**data) cbpi.cache.get(self.cache_key)[id].steps.append(f) cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) return ('', 204) @route('//step/', methods=["PUT"]) def putStep(self, id, stepid): data = request.json # Select modal data["id"] = stepid data["fermenter_id"] = id data["days"] = 0 if data["days"] == "" else data["days"] data["hours"] = 0 if data["hours"] == "" else data["hours"] data["minutes"] = 0 if data["minutes"] == "" else data["minutes"] for s in cbpi.cache.get(self.cache_key)[id].steps: if s.id == stepid: s.__dict__.update(**data) FermenterStep.update(**s.__dict__) break cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) return ('', 204) @route('//step/', methods=["DELETE"]) def deleteStep(self, id, stepid): for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps): if s.id == stepid: del cbpi.cache.get(self.cache_key)[id].steps[idx] FermenterStep.delete(s.id) break cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) return ('', 204) @route('//start', methods=['POST']) def start_fermentation(self, id): active = None for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps): if s.state == 'A': active = s break inactive = None for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps): if s.state == 'I': inactive = s break if active is not None: active.state = 'D' active.end = time.time() FermenterStep.update(**active.__dict__) del cbpi.cache["fermenter_task"][id] if inactive is not None: fermenter = self.get_fermenter(inactive.fermenter_id) current_temp = cbpi.get_sensor_value(int(fermenter.sensor)) inactive.state = 'A' inactive.start = time.time() inactive.direction = "C" if current_temp >= inactive.temp else "H" FermenterStep.update(**inactive.__dict__) self.postTargetTemp(id, inactive.temp) cbpi.cache["fermenter_task"][id] = inactive cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) return ('', 204) @route('//reset', methods=["POST"]) def reset(self, id): FermenterStep.reset_all_steps(id) cbpi.cache[self.cache_key][id].steps = FermenterStep.get_by_fermenter_id(id) if id in cbpi.cache["fermenter_task"]: del cbpi.cache["fermenter_task"][id] cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) return ('', 204) @route('//automatic', methods=['POST']) def toggle(self, id): fermenter = cbpi.cache.get(self.cache_key)[id] try: print fermenter.state if fermenter.state is False: # Start controller if fermenter.logic is not None: cfg = fermenter.config.copy() cfg.update( dict(api=cbpi, fermenter_id=fermenter.id, heater=fermenter.heater, sensor=fermenter.sensor)) instance = cbpi.get_fermentation_controller(fermenter.logic).get("class")(**cfg) instance.init() fermenter.instance = instance def run(instance): instance.run() fermenter.state = not fermenter.state cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id)) t = cbpi.socketio.start_background_task(target=run, instance=instance) fermenter.state = not fermenter.state cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id)) else: # Stop controller fermenter.instance.stop() fermenter.state = not fermenter.state cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id)) except Exception as e: print e cbpi.notify("Toogle Fementer Controller failed", "Pleae check the %s configuration" % fermenter.name, type="danger", timeout=None) return ('', 500) return ('', 204) def get_fermenter(self, id): return cbpi.cache["fermenter"].get(id) def target_temp_reached(self,id, step): timestamp = time.time() days = step.days * 24 * 60 * 60 hours = step.hours * 60 * 60 minutes = step.minutes * 60 target_time = days + hours + minutes + timestamp FermenterStep.update_timer(step.id, target_time) step.timer_start = target_time cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) def check_step(self): for key, value in cbpi.cache["fermenter_task"].iteritems(): try: fermenter = self.get_fermenter(key) current_temp = current_temp = cbpi.get_sensor_value(int(fermenter.sensor)) if value.timer_start is None: if value.direction == "H" : if current_temp >= value.temp: self.target_temp_reached(key,value) else: if current_temp <= value.temp: self.target_temp_reached(key, value) else: if time.time() >= value.timer_start: self.start_fermentation(key) else: pass except Exception as e: pass @cbpi.backgroundtask(key="read_target_temps_fermenter", interval=5) def read_target_temps(api): """ background process that reads all passive sensors in interval of 1 second :return: None """ result = {} for key, value in cbpi.cache.get("fermenter").iteritems(): cbpi.save_to_file(key, value.target_temp, prefix="fermenter") instance = FermenterView() @cbpi.backgroundtask(key="fermentation_task", interval=1) def execute_fermentation_step(api): with cbpi.app.app_context(): instance.check_step() def init_active_steps(): ''' active_steps = FermenterStep.query.filter_by(state='A') for a in active_steps: db.session.expunge(a) cbpi.cache["fermenter_task"][a.fermenter_id] = a ''' @cbpi.initalizer(order=1) def init(cbpi): FermenterView.register(cbpi.app, route_base='/api/fermenter') FermenterView.init_cache()