- Import BeerXML - Import REST API - Migration DB - Set Default Screen after Start - Cleanuptags/3.1_alpha
| @@ -1 +1 @@ | |||
| 3.0.1 | |||
| 3.0.2 | |||
| @@ -35,6 +35,8 @@ import modules.system | |||
| import modules.buzzer | |||
| import modules.stats | |||
| import modules.kettle | |||
| import modules.recipe_import | |||
| import modules.core.db_mirgrate | |||
| from app_config import cbpi | |||
| # Build the database: | |||
| # This will create the database file using SQLAlchemy | |||
| @@ -17,10 +17,10 @@ class ActorView(BaseView): | |||
| obj.state = 0 | |||
| obj.power = 100 | |||
| def post_post_callback(self, m): | |||
| def _post_post_callback(self, m): | |||
| self.api.init_actor(m.id) | |||
| def post_put_callback(self, m): | |||
| def _post_put_callback(self, m): | |||
| self.api.init_actor(m.id) | |||
| @@ -62,8 +62,6 @@ class ActorView(BaseView): | |||
| @cbpi.initalizer(order=1000) | |||
| def init(cbpi): | |||
| print "INITIALIZE ACTOR MODULE" | |||
| cbpi.app.logger.info("INITIALIZE ACTOR MODULE") | |||
| ActorView.register(cbpi.app, route_base='/api/actor') | |||
| ActorView.init_cache() | |||
| cbpi.init_actors() | |||
| @@ -188,11 +188,11 @@ def loadPlugins(): | |||
| @cbpi.initalizer(order=1) | |||
| def initPlugins(app): | |||
| print "INITIALIZE CUSTOM PLUGINS" | |||
| loadCorePlugins() | |||
| loadPlugins() | |||
| @cbpi.initalizer(order=2) | |||
| def init(cbpi): | |||
| print "INITIALIZE ADDON MODULE" | |||
| cbpi.app.register_blueprint(blueprint, url_prefix='/api/editor') | |||
| @@ -50,7 +50,7 @@ class MashStep(StepBase): | |||
| ''' | |||
| # Check if Target Temp is reached | |||
| if self.get_kettle_temp(self.kettle) >= int(self.temp): | |||
| 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) | |||
| @@ -102,3 +102,5 @@ class Dummy(ActorBase): | |||
| def off(self): | |||
| print "OFF" | |||
| @@ -113,5 +113,5 @@ def set_temp(t): | |||
| @cbpi.initalizer() | |||
| def init(cbpi): | |||
| cbpi.app.logger.info("INITIALIZE ONE WIRE MODULE") | |||
| cbpi.app.register_blueprint(blueprint, url_prefix='/api/one_wire') | |||
| @@ -24,11 +24,9 @@ class ConfigView(BaseView): | |||
| update_data = {"name": data["name"], "value": data["value"]} | |||
| if self.api.cache.get(self.cache_key) is not None: | |||
| #self.pre_post_callback(self.api.cache.get(self.cache_key)[name]) | |||
| print self.api.cache.get(self.cache_key)[name] | |||
| self.api.cache.get(self.cache_key)[name].__dict__.update(**update_data) | |||
| m = self.model.update(**self.api.cache.get(self.cache_key)[name].__dict__) | |||
| self.post_put_callback(self.api.cache.get(self.cache_key)[name]) | |||
| self._post_put_callback(self.api.cache.get(self.cache_key)[name]) | |||
| return json.dumps(self.api.cache.get(self.cache_key)[name].__dict__) | |||
| @route('/<id>', methods=["GET"]) | |||
| @@ -54,6 +52,6 @@ class ConfigView(BaseView): | |||
| @cbpi.initalizer(order=1) | |||
| def init(cbpi): | |||
| print "INITIALIZE CONFIG MODULE" | |||
| ConfigView.register(cbpi.app, route_base='/api/config') | |||
| ConfigView.init_cache() | |||
| @@ -9,7 +9,7 @@ class BaseView(FlaskView): | |||
| cache_key = None | |||
| api = cbpi | |||
| @route('/<int:id>') | |||
| @route('/<int:id>', methods=["GET"]) | |||
| def getOne(self, id): | |||
| if self.api.cache.get(self.cache_key) is not None: | |||
| @@ -17,36 +17,36 @@ class BaseView(FlaskView): | |||
| else: | |||
| return json.dumps(self.model.get_one(id)) | |||
| @route('/') | |||
| @route('/', methods=["GET"]) | |||
| def getAll(self): | |||
| if self.api.cache.get(self.cache_key) is not None: | |||
| return json.dumps(self.api.cache.get(self.cache_key)) | |||
| else: | |||
| return json.dumps(self.model.get_all()) | |||
| def pre_post_callback(self, data): | |||
| def _pre_post_callback(self, data): | |||
| pass | |||
| def post_post_callback(self, m): | |||
| def _post_post_callback(self, m): | |||
| pass | |||
| @route('/', methods=["POST"]) | |||
| def post(self): | |||
| data = request.json | |||
| self.pre_post_callback(data) | |||
| self._pre_post_callback(data) | |||
| m = self.model.insert(**data) | |||
| if self.api.cache.get(self.cache_key) is not None: | |||
| self.api.cache.get(self.cache_key)[m.id] = m | |||
| self.post_post_callback(m) | |||
| self._post_post_callback(m) | |||
| return json.dumps(m) | |||
| def pre_put_callback(self, m): | |||
| def _pre_put_callback(self, m): | |||
| pass | |||
| def post_put_callback(self, m): | |||
| def _post_put_callback(self, m): | |||
| pass | |||
| @@ -59,32 +59,32 @@ class BaseView(FlaskView): | |||
| except: | |||
| pass | |||
| if self.api.cache.get(self.cache_key) is not None: | |||
| self.pre_put_callback(self.api.cache.get(self.cache_key)[id]) | |||
| self._pre_put_callback(self.api.cache.get(self.cache_key)[id]) | |||
| self.api.cache.get(self.cache_key)[id].__dict__.update(**data) | |||
| m = self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__) | |||
| self.post_put_callback(self.api.cache.get(self.cache_key)[id]) | |||
| self._post_put_callback(self.api.cache.get(self.cache_key)[id]) | |||
| return json.dumps(self.api.cache.get(self.cache_key)[id]) | |||
| else: | |||
| m = self.model.update(**data) | |||
| self.post_put_callback(m) | |||
| self._post_put_callback(m) | |||
| return json.dumps(m) | |||
| def pre_delete_callback(self, m): | |||
| def _pre_delete_callback(self, m): | |||
| pass | |||
| def post_delete_callback(self, id): | |||
| def _post_delete_callback(self, id): | |||
| pass | |||
| @route('/<int:id>', methods=["DELETE"]) | |||
| def delete(self, id): | |||
| if self.api.cache.get(self.cache_key) is not None: | |||
| self.pre_delete_callback(self.api.cache.get(self.cache_key)[id]) | |||
| self._pre_delete_callback(self.api.cache.get(self.cache_key)[id]) | |||
| del self.api.cache.get(self.cache_key)[id] | |||
| m = self.model.delete(id) | |||
| def post_delete_callback(self, id): | |||
| def _post_delete_callback(self, id): | |||
| pass | |||
| return ('',204) | |||
| @@ -159,9 +159,8 @@ class SensorAPI(object): | |||
| def log_action(self, text): | |||
| filename = "./logs/action.log" | |||
| formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime()) | |||
| with open(filename, "a") as file: | |||
| text = text.encode("utf-8") | |||
| file.write("%s,%s\n" % (formatted_time, text)) | |||
| def shutdown_sensor(self, id): | |||
| @@ -243,6 +242,15 @@ class CraftBeerPi(ActorAPI, SensorAPI): | |||
| else: | |||
| return cfg.value | |||
| def set_config_parameter(self, name, value): | |||
| from modules.config import Config | |||
| with self.app.app_context(): | |||
| update_data = {"name": name, "value": value} | |||
| self.cache.get("config")[name].__dict__.update(**update_data) | |||
| c = Config.update(**update_data) | |||
| self.emit("UPDATE_CONFIG", c) | |||
| def add_config_parameter(self, name, value, type, description, options=None): | |||
| from modules.config import Config | |||
| with self.app.app_context(): | |||
| @@ -281,10 +289,13 @@ class CraftBeerPi(ActorAPI, SensorAPI): | |||
| def actor(self, cls): | |||
| return self.__parseProps("actor_types", cls) | |||
| def actor2(self, description="", power=True, **options): | |||
| def decorator(f): | |||
| print f() | |||
| print f | |||
| print options | |||
| print description | |||
| return f | |||
| @@ -458,7 +469,7 @@ class CraftBeerPi(ActorAPI, SensorAPI): | |||
| def job(interval, method): | |||
| while True: | |||
| try: | |||
| method() | |||
| method(self) | |||
| except Exception as e: | |||
| self.app.logger.error("Exception" + method.__name__ + ": " + str(e)) | |||
| self.socketio.sleep(interval) | |||
| @@ -0,0 +1,47 @@ | |||
| import sqlite3 | |||
| import os | |||
| from modules import cbpi | |||
| from db import get_db | |||
| def execute_file(curernt_version, data): | |||
| if curernt_version >= data["version"]: | |||
| cbpi.app.logger.info("SKIP DB FILE: %s" % data["file"]) | |||
| return | |||
| try: | |||
| with sqlite3.connect("craftbeerpi.db") as conn: | |||
| with open('./update/%s' % data["file"], 'r') as f: | |||
| d = f.read() | |||
| sqlCommands = d.split(";") | |||
| cur = conn.cursor() | |||
| for s in sqlCommands: | |||
| cur.execute(s) | |||
| cur.execute("INSERT INTO schema_info (version,filename) values (?,?)", (data["version"], data["file"])) | |||
| conn.commit() | |||
| except sqlite3.OperationalError as err: | |||
| print "EXCEPT" | |||
| print err | |||
| @cbpi.initalizer(order=-9999) | |||
| def init(app=None): | |||
| with cbpi.app.app_context(): | |||
| conn = get_db() | |||
| cur = conn.cursor() | |||
| current_version = None | |||
| try: | |||
| cur.execute("SELECT max(version) as m FROM schema_info") | |||
| m = cur.fetchone() | |||
| current_version = m["m"] | |||
| except: | |||
| pass | |||
| result = [] | |||
| for filename in os.listdir("./update"): | |||
| if filename.endswith(".sql"): | |||
| d = {"version": int(filename[:filename.index('_')]), "file": filename} | |||
| result.append(d) | |||
| execute_file(current_version, d) | |||
| @@ -5,7 +5,7 @@ class Base(object): | |||
| @classmethod | |||
| def init_global(cls): | |||
| print "GLOBAL ACTOR INIT" | |||
| pass | |||
| def get_config_parameter(self, key, default_value): | |||
| return self.api.get_config_parameter(key, default_value) | |||
| @@ -14,10 +14,10 @@ class Base(object): | |||
| self.api.socketio.sleep(seconds) | |||
| def init(self): | |||
| print "INIT BASE" | |||
| pass | |||
| def stop(self): | |||
| print "STOP HARDWARE" | |||
| pass | |||
| def update(self, **kwds): | |||
| pass | |||
| @@ -99,10 +99,10 @@ class ActorBase(Base): | |||
| return 1 | |||
| def set_power(self, power): | |||
| print "SET POWER TO %s" % power | |||
| pass | |||
| def on(self, power=0): | |||
| print "ON" | |||
| pass | |||
| def off(self): | |||
| print "OFF" | |||
| pass | |||
| @@ -44,7 +44,7 @@ class KettleAPI(NotificationAPI): | |||
| return self.api.cache.get("kettle").get(id).target_temp | |||
| def set_target_temp(self, temp, id=None): | |||
| temp = int(temp) | |||
| temp = float(temp) | |||
| try: | |||
| if id is None: | |||
| @@ -59,7 +59,7 @@ class Timer(object): | |||
| timer_end = Property.Number("TIMER_END", configurable=False) | |||
| def start_timer(self, timer): | |||
| print "START TIMER NEW" | |||
| if self.timer_end is not None: | |||
| return | |||
| self.timer_end = int(time.time()) + timer | |||
| @@ -100,13 +100,13 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI): | |||
| self.n = True | |||
| def init(self): | |||
| print "INIT STEP" | |||
| pass | |||
| def finish(self): | |||
| print "FINSIH STEP" | |||
| pass | |||
| def reset(self): | |||
| print "REST STEP" | |||
| pass | |||
| def execute(self): | |||
| print "-------------" | |||
| @@ -63,18 +63,19 @@ class FermenterView(BaseView): | |||
| model = Fermenter | |||
| cache_key = "fermenter" | |||
| def post_post_callback(self, m): | |||
| def _post_post_callback(self, m): | |||
| m.state = False | |||
| m.steps = [] | |||
| def pre_put_callback(self, m): | |||
| def _pre_put_callback(self, m): | |||
| m.state = False | |||
| try: | |||
| m.instance.stop() | |||
| except: | |||
| pass | |||
| def post_put_callback(self, m): | |||
| def _post_put_callback(self, m): | |||
| m.state = False | |||
| @route('/<int:id>/targettemp/<temp>', methods=['POST']) | |||
| @@ -282,7 +283,7 @@ class FermenterView(BaseView): | |||
| @cbpi.backgroundtask(key="read_target_temps_fermenter", interval=5) | |||
| def read_target_temps(): | |||
| def read_target_temps(api): | |||
| """ | |||
| background process that reads all passive sensors in interval of 1 second | |||
| :return: None | |||
| @@ -295,7 +296,7 @@ def read_target_temps(): | |||
| instance = FermenterView() | |||
| @cbpi.backgroundtask(key="fermentation_task", interval=1) | |||
| def execute_fermentation_step(): | |||
| def execute_fermentation_step(api): | |||
| with cbpi.app.app_context(): | |||
| instance.check_step() | |||
| @@ -310,6 +311,6 @@ def init_active_steps(): | |||
| @cbpi.initalizer(order=1) | |||
| def init(cbpi): | |||
| print "INITIALIZE CONFIG MODULE" | |||
| FermenterView.register(cbpi.app, route_base='/api/fermenter') | |||
| FermenterView.init_cache() | |||
| @@ -15,7 +15,7 @@ class Kettle2View(BaseView): | |||
| cache_key = "kettle" | |||
| @classmethod | |||
| def pre_post_callback(self, data): | |||
| def _pre_post_callback(self, data): | |||
| data["target_temp"] = 0 | |||
| @classmethod | |||
| @@ -23,16 +23,16 @@ class Kettle2View(BaseView): | |||
| obj.state = False | |||
| def post_post_callback(self, m): | |||
| def _post_post_callback(self, m): | |||
| m.state = False | |||
| def pre_put_callback(self, m): | |||
| def _pre_put_callback(self, m): | |||
| try: | |||
| m.instance.stop() | |||
| except: | |||
| pass | |||
| def post_put_callback(self, m): | |||
| def _post_put_callback(self, m): | |||
| m.state = False | |||
| @route('/<int:id>/targettemp/<temp>', methods=['POST']) | |||
| @@ -76,11 +76,11 @@ def set_target_temp(id, temp): | |||
| :param temp: target temp to set | |||
| :return: None | |||
| ''' | |||
| print "GOT EVENT %s %s" % (id, temp) | |||
| Kettle2View().postTargetTemp(id,temp) | |||
| @cbpi.backgroundtask(key="read_target_temps", interval=5) | |||
| def read_target_temps(): | |||
| def read_target_temps(api): | |||
| """ | |||
| background process that reads all passive sensors in interval of 1 second | |||
| :return: None | |||
| @@ -105,5 +105,4 @@ def init(app): | |||
| :param app: the flask app | |||
| :return: None | |||
| """ | |||
| print "INITIALIZE LOG MODULE" | |||
| LogView.register(cbpi.app, route_base='/api/logs') | |||
| @@ -0,0 +1,3 @@ | |||
| import beerxml | |||
| import kbh | |||
| import restapi | |||
| @@ -0,0 +1,110 @@ | |||
| from flask import json, request | |||
| from flask_classy import FlaskView, route | |||
| from git import Repo, Git | |||
| import sqlite3 | |||
| from modules.app_config import cbpi | |||
| from werkzeug.utils import secure_filename | |||
| import pprint | |||
| import time | |||
| import os | |||
| from modules.steps import Step,StepView | |||
| import xml.etree.ElementTree | |||
| class BeerXMLImport(FlaskView): | |||
| BEER_XML_FILE = "./upload/beer.xml" | |||
| @route('/', methods=['GET']) | |||
| def get(self): | |||
| if not os.path.exists(self.BEER_XML_FILE): | |||
| self.api.notify(headline="File Not Found", message="Please upload a Beer.xml File", | |||
| type="danger") | |||
| return ('', 404) | |||
| result = [] | |||
| for idx, r in enumerate(self.getDict().get("RECIPES").get("RECIPE")): | |||
| result.append({"id": idx, "name": r.get("NAME")}) | |||
| return json.dumps(result) | |||
| def allowed_file(self, filename): | |||
| return '.' in filename and filename.rsplit('.', 1)[1] in set(['xml']) | |||
| @route('/upload', methods=['POST']) | |||
| def upload_file(self): | |||
| try: | |||
| if request.method == 'POST': | |||
| file = request.files['file'] | |||
| if file and self.allowed_file(file.filename): | |||
| file.save(os.path.join(self.api.app.config['UPLOAD_FOLDER'], "beer.xml")) | |||
| self.api.notify(headline="Upload Successful", message="The Beer XML file was uploaded succesfully") | |||
| return ('', 204) | |||
| return ('', 404) | |||
| except Exception as e: | |||
| self.api.notify(headline="Upload Failed", message="Failed to upload Beer xml", type="danger") | |||
| return ('', 500) | |||
| @route('/<int:id>', methods=['POST']) | |||
| def load(self, id): | |||
| recipe = self.getDict().get("RECIPES").get("RECIPE")[id] | |||
| steps = recipe.get("MASH",{}).get("MASH_STEPS",{}).get("MASH_STEP",[]) | |||
| name = recipe.get("NAME") | |||
| self.api.set_config_parameter("brew_name", name) | |||
| boil_time = recipe.get("BOIL_TIME", 90) | |||
| mashstep_type = cbpi.get_config_parameter("step_mash", "MashStep") | |||
| mash_kettle = cbpi.get_config_parameter("step_mash_kettle", None) | |||
| boilstep_type = cbpi.get_config_parameter("step_boil", "BoilStep") | |||
| boil_kettle = cbpi.get_config_parameter("step_boil_kettle", None) | |||
| boil_temp = 100 if cbpi.get_config_parameter("unit", "C") == "C" else 212 | |||
| # READ KBH DATABASE | |||
| Step.delete_all() | |||
| StepView().reset() | |||
| conn = None | |||
| try: | |||
| conn = sqlite3.connect(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db') | |||
| c = conn.cursor() | |||
| for row in steps: | |||
| Step.insert(**{"name": row.get("NAME"), "type": mashstep_type, "config": {"kettle": mash_kettle, "temp": float(row.get("STEP_TEMP")), "timer": row.get("STEP_TIME")}}) | |||
| Step.insert(**{"name": "ChilStep", "type": "ChilStep", "config": {"timer": 15}}) | |||
| ## Add cooking step | |||
| Step.insert(**{"name": "Boil", "type": boilstep_type, "config": {"kettle": boil_kettle, "temp": boil_temp, "timer": boil_time}}) | |||
| ## Add Whirlpool step | |||
| Step.insert(**{"name": "Whirlpool", "type": "ChilStep", "config": {"timer": 15}}) | |||
| # setBrewName(name) | |||
| self.api.emit("UPDATE_ALL_STEPS", Step.get_all()) | |||
| self.api.notify(headline="Recipe %s loaded successfully" % name, message="") | |||
| except Exception as e: | |||
| self.api.notify(headline="Failed to load Recipe", message=e.message, type="danger") | |||
| return ('', 500) | |||
| finally: | |||
| if conn: | |||
| conn.close() | |||
| return ('', 204) | |||
| def getDict(self): | |||
| ''' | |||
| Beer XML file to dict | |||
| :return: beer.xml file as dict | |||
| ''' | |||
| try: | |||
| import xmltodict | |||
| with open(self.BEER_XML_FILE) as fd: | |||
| doc = xmltodict.parse(fd.read()) | |||
| return doc | |||
| except: | |||
| self.api.notify(headline="Failed to load Beer.xml", message="Please check if you uploaded an beer.xml", type="danger") | |||
| @cbpi.initalizer() | |||
| def init(cbpi): | |||
| BeerXMLImport.api = cbpi | |||
| BeerXMLImport.register(cbpi.app, route_base='/api/beerxml') | |||
| @@ -0,0 +1,109 @@ | |||
| from flask import json, request | |||
| from flask_classy import FlaskView, route | |||
| from git import Repo, Git | |||
| import sqlite3 | |||
| from modules.app_config import cbpi | |||
| from werkzeug.utils import secure_filename | |||
| import pprint | |||
| import time | |||
| import os | |||
| from modules.steps import Step, StepView | |||
| class KBH(FlaskView): | |||
| @route('/', methods=['GET']) | |||
| def get(self): | |||
| conn = None | |||
| try: | |||
| if not os.path.exists(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db'): | |||
| self.api.notify(headline="File Not Found", message="Please upload a Kleiner Brauhelfer Database", type="danger") | |||
| return ('', 404) | |||
| conn = sqlite3.connect(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db') | |||
| c = conn.cursor() | |||
| c.execute('SELECT ID, Sudname, BierWurdeGebraut FROM Sud') | |||
| data = c.fetchall() | |||
| result = [] | |||
| for row in data: | |||
| result.append({"id": row[0], "name": row[1], "brewed": row[2]}) | |||
| return json.dumps(result) | |||
| except Exception as e: | |||
| print e | |||
| self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger") | |||
| return ('', 500) | |||
| finally: | |||
| if conn: | |||
| conn.close() | |||
| def allowed_file(self, filename): | |||
| return '.' in filename and filename.rsplit('.', 1)[1] in set(['sqlite']) | |||
| @route('/upload', methods=['POST']) | |||
| def upload_file(self): | |||
| try: | |||
| if request.method == 'POST': | |||
| file = request.files['file'] | |||
| if file and self.allowed_file(file.filename): | |||
| filename = secure_filename(file.filename) | |||
| file.save(os.path.join(self.api.app.config['UPLOAD_FOLDER'], "kbh.db")) | |||
| self.api.notify(headline="Upload Successful", message="The Kleiner Brauhelfer Database was uploaded succesfully") | |||
| return ('', 204) | |||
| return ('', 404) | |||
| except Exception as e: | |||
| self.api.notify(headline="Upload Failed", message="Failed to upload Kleiner Brauhelfer", type="danger") | |||
| return ('', 500) | |||
| @route('/<int:id>', methods=['POST']) | |||
| def load(self, id): | |||
| mashstep_type = cbpi.get_config_parameter("step_mash", "MashStep") | |||
| mashinstep_type = cbpi.get_config_parameter("step_mashin", "MashInStep") | |||
| chilstep_type = cbpi.get_config_parameter("step_chil", "ChilStep") | |||
| boilstep_type = cbpi.get_config_parameter("step_boil", "BoilStep") | |||
| mash_kettle = cbpi.get_config_parameter("step_mash_kettle", None) | |||
| boil_kettle = cbpi.get_config_parameter("step_boil_kettle", None) | |||
| boil_temp = 100 if cbpi.get_config_parameter("unit", "C") == "C" else 212 | |||
| # READ KBH DATABASE | |||
| Step.delete_all() | |||
| StepView().reset() | |||
| conn = None | |||
| try: | |||
| conn = sqlite3.connect(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db') | |||
| c = conn.cursor() | |||
| c.execute('SELECT EinmaischenTemp, Sudname FROM Sud WHERE ID = ?', (id,)) | |||
| row = c.fetchone() | |||
| name = row[1] | |||
| self.api.set_config_parameter("brew_name", name) | |||
| Step.insert(**{"name": "MashIn", "type": mashinstep_type, "config": {"kettle": mash_kettle, "temp": row[0]}}) | |||
| ### add rest step | |||
| for row in c.execute('SELECT * FROM Rasten WHERE SudID = ?', (id,)): | |||
| Step.insert(**{"name": row[5], "type": mashstep_type, "config": {"kettle": mash_kettle, "temp": row[3], "timer": row[4]}}) | |||
| Step.insert(**{"name": "Chil", "type": chilstep_type, "config": {"timer": 15}}) | |||
| ## Add cooking step | |||
| c.execute('SELECT max(Zeit) FROM Hopfengaben WHERE SudID = ?', (id,)) | |||
| row = c.fetchone() | |||
| Step.insert(**{"name": "Boil", "type": boilstep_type, "config": {"kettle": boil_kettle, "temp": boil_temp, "timer": row[0]}}) | |||
| ## Add Whirlpool step | |||
| Step.insert(**{"name": "Whirlpool", "type": chilstep_type, "config": {"timer": 15}}) | |||
| #setBrewName(name) | |||
| self.api.emit("UPDATE_ALL_STEPS", Step.get_all()) | |||
| self.api.notify(headline="Recipe %s loaded successfully" % name, message="") | |||
| except Exception as e: | |||
| self.api.notify(headline="Failed to load Recipe", message=e.message, type="danger") | |||
| return ('', 500) | |||
| finally: | |||
| if conn: | |||
| conn.close() | |||
| return ('', 204) | |||
| @cbpi.initalizer() | |||
| def init(cbpi): | |||
| KBH.api = cbpi | |||
| KBH.register(cbpi.app, route_base='/api/kbh') | |||
| @@ -0,0 +1,62 @@ | |||
| from flask import json, request | |||
| from flask_classy import FlaskView, route | |||
| from git import Repo, Git | |||
| import sqlite3 | |||
| from modules.app_config import cbpi | |||
| from werkzeug.utils import secure_filename | |||
| import pprint | |||
| import time | |||
| import os | |||
| from modules.steps import Step,StepView | |||
| import xml.etree.ElementTree | |||
| class RESTImport(FlaskView): | |||
| @route('/', methods=['POST']) | |||
| def load(self): | |||
| try: | |||
| data = request.json | |||
| name = data.get("name", "No Name") | |||
| self.api.set_config_parameter("brew_name", name) | |||
| chilstep_type = cbpi.get_config_parameter("step_chil", "ChilStep") | |||
| mashstep_type = cbpi.get_config_parameter("step_mash", "MashStep") | |||
| mash_kettle = cbpi.get_config_parameter("step_mash_kettle", None) | |||
| boilstep_type = cbpi.get_config_parameter("step_boil", "BoilStep") | |||
| boil_kettle = cbpi.get_config_parameter("step_boil_kettle", None) | |||
| boil_temp = 100 if cbpi.get_config_parameter("unit", "C") == "C" else 212 | |||
| # READ KBH DATABASE | |||
| Step.delete_all() | |||
| StepView().reset() | |||
| for step in data.get("steps"): | |||
| if step.get("type", None) == "MASH": | |||
| Step.insert(**{"name": step.get("name","Mash Step"), "type": mashstep_type, "config": {"kettle": mash_kettle, "temp": step.get("temp",0), "timer": step.get("timer",0)}}) | |||
| elif step.get("type", None) == "CHIL": | |||
| Step.insert(**{"name": step.get("name","Chil"), "type": chilstep_type, "config": {"timer": step.get("timer")}}) | |||
| elif step.get("type", None) == "BOIL": | |||
| Step.insert(**{"name": step.get("name", "Boil"), "type": boilstep_type, "config": {"kettle": boil_kettle, "timer": step.get("timer"), "temp": boil_temp}}) | |||
| else: | |||
| pass | |||
| self.api.emit("UPDATE_ALL_STEPS", Step.get_all()) | |||
| self.api.notify(headline="Recipe %s loaded successfully" % name, message="") | |||
| except Exception as e: | |||
| self.api.notify(headline="Failed to load Recipe", type="danger", message=str(e)) | |||
| m = str(e.message) | |||
| return (str(e), 500) | |||
| return ('', 204) | |||
| @cbpi.initalizer() | |||
| def init(cbpi): | |||
| RESTImport.api = cbpi | |||
| RESTImport.register(cbpi.app, route_base='/api/recipe/import/v1') | |||
| @@ -13,26 +13,26 @@ class SensorView(BaseView): | |||
| cache_key = "sensors" | |||
| def post_post_callback(self, m): | |||
| def _post_post_callback(self, m): | |||
| cbpi.init_sensor(m.id) | |||
| def post_put_callback(self, m): | |||
| def _post_put_callback(self, m): | |||
| cbpi.stop_sensor(m.id) | |||
| cbpi.init_sensor(m.id) | |||
| def pre_delete_callback(self, m): | |||
| def _pre_delete_callback(self, m): | |||
| cbpi.stop_sensor(m.id) | |||
| @cbpi.initalizer(order=1000) | |||
| def init(cbpi): | |||
| print "INITIALIZE SENSOR MODULE" | |||
| SensorView.register(cbpi.app, route_base='/api/sensor') | |||
| SensorView.init_cache() | |||
| cbpi.init_sensors() | |||
| @cbpi.backgroundtask(key="read_passiv_sensor", interval=5) | |||
| def read_passive_sensor(): | |||
| def read_passive_sensor(api): | |||
| """ | |||
| background process that reads all passive sensors in interval of 1 second | |||
| :return: None | |||
| @@ -16,7 +16,7 @@ def getserial(): | |||
| @cbpi.initalizer(order=9999) | |||
| def sendStats(cbpi): | |||
| print "INITIALIZE STATS" | |||
| try: | |||
| serial = getserial() | |||
| @@ -66,16 +66,14 @@ class Step(DBModel): | |||
| class StepView(BaseView): | |||
| model = Step | |||
| def pre_post_callback(self, data): | |||
| def _pre_post_callback(self, data): | |||
| order = self.model.get_max_order() | |||
| data["order"] = 1 if order is None else order + 1 | |||
| data["state"] = "I" | |||
| @route('/sort', methods=["POST"]) | |||
| def sort_steps(self): | |||
| Step.sort(request.json) | |||
| cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all()) | |||
| return ('', 204) | |||
| @@ -87,16 +85,12 @@ class StepView(BaseView): | |||
| @route('/action/<method>', methods=["POST"]) | |||
| def action(self, method): | |||
| cbpi.cache["active_step"].__getattribute__(method)() | |||
| return ('', 204) | |||
| @route('/reset', methods=["POST"]) | |||
| def reset(self): | |||
| self.model.reset_all_steps() | |||
| #db.session.commit() | |||
| self.stop_step() | |||
| cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all()) | |||
| return ('', 204) | |||
| @@ -133,7 +127,6 @@ class StepView(BaseView): | |||
| return ('', 204) | |||
| def init_step(self, step): | |||
| cbpi.log_action("Start Step %s" % step.name) | |||
| type_cfg = cbpi.cache.get("step_types").get(step.type) | |||
| if type_cfg is None: | |||
| @@ -146,7 +139,6 @@ class StepView(BaseView): | |||
| cfg.update(dict(name=step.name, api=cbpi, id=step.id, timer_end=None, managed_fields=get_manged_fields_as_array(type_cfg))) | |||
| instance = type_cfg.get("class")(**cfg) | |||
| instance.init() | |||
| # set step instance to ache | |||
| cbpi.cache["active_step"] = instance | |||
| @@ -204,7 +196,6 @@ def init_after_startup(): | |||
| # step type not found. cant restart step | |||
| return | |||
| print "STEP SATE......", step.stepstate | |||
| cfg = step.stepstate.copy() | |||
| cfg.update(dict(api=cbpi, id=step.id, managed_fields=get_manged_fields_as_array(type_cfg))) | |||
| instance = type_cfg.get("class")(**cfg) | |||
| @@ -213,7 +204,7 @@ def init_after_startup(): | |||
| @cbpi.initalizer(order=2000) | |||
| def init(cbpi): | |||
| print "INITIALIZE STEPS MODULE" | |||
| StepView.register(cbpi.app, route_base='/api/step') | |||
| def get_all(): | |||
| @@ -225,7 +216,7 @@ def init(cbpi): | |||
| cbpi.add_cache_callback("steps", get_all) | |||
| @cbpi.backgroundtask(key="step_task", interval=0.1) | |||
| def execute_step(): | |||
| def execute_step(api): | |||
| ''' | |||
| Background job which executes the step | |||
| :return: | |||
| @@ -1,4 +1,5 @@ | |||
| from flask import json | |||
| import yaml | |||
| from flask import json, url_for, Response | |||
| from flask_classy import FlaskView, route | |||
| from git import Repo, Git | |||
| @@ -101,7 +102,44 @@ class SystemView(FlaskView): | |||
| def dump(self): | |||
| return json.dumps(cbpi.cache) | |||
| @route('/endpoints', methods=['GET']) | |||
| def endpoints(self): | |||
| import urllib | |||
| output = [] | |||
| vf = self.api.app.view_functions | |||
| for f in self.api.app.view_functions: | |||
| print f | |||
| endpoints = {} | |||
| re = { | |||
| "swagger": "2.0", | |||
| "host": "", | |||
| "info": { | |||
| "description":"", | |||
| "version": "", | |||
| "title": "CraftBeerPi" | |||
| }, | |||
| "schemes": ["http"], | |||
| "paths": endpoints} | |||
| for rule in self.api.app.url_map.iter_rules(): | |||
| r = rule | |||
| endpoints[rule.rule] = {} | |||
| if "HEAD" in r.methods: r.methods.remove("HEAD") | |||
| if "OPTIONS" in r.methods: r.methods.remove("OPTIONS") | |||
| for m in rule.methods: | |||
| endpoints[rule.rule][m] = dict(summary="", description="", consumes=["application/json"],produces=["application/json"]) | |||
| with open("config/version.yaml", 'r') as stream: | |||
| y = yaml.load(stream) | |||
| pprint.pprint(y) | |||
| pprint.pprint(re) | |||
| return Response(yaml.dump(re), mimetype='text/yaml') | |||
| @cbpi.initalizer() | |||
| def init(cbpi): | |||
| print "INITIALIZE SYSTEM MODULE" | |||
| SystemView.api = cbpi | |||
| SystemView.register(cbpi.app, route_base='/api/system') | |||
| @@ -5,13 +5,12 @@ react = Blueprint('react', __name__, template_folder='templates', static_folder= | |||
| @cbpi.initalizer(order=10) | |||
| def init(cbpi): | |||
| print "INITIALIZE UI" | |||
| cbpi.app.register_blueprint(react, url_prefix='/ui') | |||
| @react.route('/') | |||
| @react.route('/', methods=["GET"]) | |||
| def index(): | |||
| return react.send_static_file("index.html") | |||
| @@ -52,11 +52,15 @@ | |||
| /* Einschränken des sichtbaren Ausschnitts */ | |||
| </style> | |||
| <title>CraftBeerPi 3.0</title> | |||
| </head> | |||
| <body> | |||
| <div id="root"></div> | |||
| <div id="root" ></div> | |||
| <script src="static/bundle.js" type="text/javascript"></script> | |||
| <!-- | |||
| This HTML file is a template. | |||
| @@ -0,0 +1,18 @@ | |||
| CREATE TABLE IF NOT EXISTS schema_info | |||
| ( | |||
| id INTEGER PRIMARY KEY NOT NULL, | |||
| version INTEGER, | |||
| filename VARCHAR(80), | |||
| creation_data DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| INSERT OR IGNORE INTO config VALUES ('step_mashin', 'MashStep', 'step', 'Default MashIn Step type for import', NULL ); | |||
| INSERT OR IGNORE INTO config VALUES ('step_mash', 'MashStep', 'step', 'Default Mash Step type for import', NULL); | |||
| INSERT OR IGNORE INTO config VALUES ('step_boil', 'BoilStep', 'step', 'Default Boil Step type for import', NULL); | |||
| INSERT OR IGNORE INTO config VALUES ('step_chil', 'ChilStep', 'step', 'Default Chil Step type for import', NULL); | |||
| INSERT OR IGNORE INTO config VALUES ('step_mash_kettle', 1, 'kettle', 'Default Mash Tun', NULL); | |||
| INSERT OR IGNORE INTO config VALUES ('step_boil_kettle', 1, 'kettle', 'Default Boil Tun ', NULL); | |||