- 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.buzzer | ||||
| import modules.stats | import modules.stats | ||||
| import modules.kettle | import modules.kettle | ||||
| import modules.recipe_import | |||||
| import modules.core.db_mirgrate | |||||
| from app_config import cbpi | from app_config import cbpi | ||||
| # Build the database: | # Build the database: | ||||
| # This will create the database file using SQLAlchemy | # This will create the database file using SQLAlchemy | ||||
| @@ -17,10 +17,10 @@ class ActorView(BaseView): | |||||
| obj.state = 0 | obj.state = 0 | ||||
| obj.power = 100 | obj.power = 100 | ||||
| def post_post_callback(self, m): | |||||
| def _post_post_callback(self, m): | |||||
| self.api.init_actor(m.id) | self.api.init_actor(m.id) | ||||
| def post_put_callback(self, m): | |||||
| def _post_put_callback(self, m): | |||||
| self.api.init_actor(m.id) | self.api.init_actor(m.id) | ||||
| @@ -62,8 +62,6 @@ class ActorView(BaseView): | |||||
| @cbpi.initalizer(order=1000) | @cbpi.initalizer(order=1000) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE ACTOR MODULE" | |||||
| cbpi.app.logger.info("INITIALIZE ACTOR MODULE") | |||||
| ActorView.register(cbpi.app, route_base='/api/actor') | ActorView.register(cbpi.app, route_base='/api/actor') | ||||
| ActorView.init_cache() | ActorView.init_cache() | ||||
| cbpi.init_actors() | cbpi.init_actors() | ||||
| @@ -188,11 +188,11 @@ def loadPlugins(): | |||||
| @cbpi.initalizer(order=1) | @cbpi.initalizer(order=1) | ||||
| def initPlugins(app): | def initPlugins(app): | ||||
| print "INITIALIZE CUSTOM PLUGINS" | |||||
| loadCorePlugins() | loadCorePlugins() | ||||
| loadPlugins() | loadPlugins() | ||||
| @cbpi.initalizer(order=2) | @cbpi.initalizer(order=2) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE ADDON MODULE" | |||||
| cbpi.app.register_blueprint(blueprint, url_prefix='/api/editor') | cbpi.app.register_blueprint(blueprint, url_prefix='/api/editor') | ||||
| @@ -50,7 +50,7 @@ class MashStep(StepBase): | |||||
| ''' | ''' | ||||
| # Check if Target Temp is reached | # 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 | # Check if Timer is Running | ||||
| if self.is_timer_finished() is None: | if self.is_timer_finished() is None: | ||||
| self.start_timer(int(self.timer) * 60) | self.start_timer(int(self.timer) * 60) | ||||
| @@ -102,3 +102,5 @@ class Dummy(ActorBase): | |||||
| def off(self): | def off(self): | ||||
| print "OFF" | print "OFF" | ||||
| @@ -113,5 +113,5 @@ def set_temp(t): | |||||
| @cbpi.initalizer() | @cbpi.initalizer() | ||||
| def init(cbpi): | def init(cbpi): | ||||
| cbpi.app.logger.info("INITIALIZE ONE WIRE MODULE") | |||||
| cbpi.app.register_blueprint(blueprint, url_prefix='/api/one_wire') | 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"]} | update_data = {"name": data["name"], "value": data["value"]} | ||||
| if self.api.cache.get(self.cache_key) is not None: | 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) | 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__) | 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__) | return json.dumps(self.api.cache.get(self.cache_key)[name].__dict__) | ||||
| @route('/<id>', methods=["GET"]) | @route('/<id>', methods=["GET"]) | ||||
| @@ -54,6 +52,6 @@ class ConfigView(BaseView): | |||||
| @cbpi.initalizer(order=1) | @cbpi.initalizer(order=1) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE CONFIG MODULE" | |||||
| ConfigView.register(cbpi.app, route_base='/api/config') | ConfigView.register(cbpi.app, route_base='/api/config') | ||||
| ConfigView.init_cache() | ConfigView.init_cache() | ||||
| @@ -9,7 +9,7 @@ class BaseView(FlaskView): | |||||
| cache_key = None | cache_key = None | ||||
| api = cbpi | api = cbpi | ||||
| @route('/<int:id>') | |||||
| @route('/<int:id>', methods=["GET"]) | |||||
| def getOne(self, id): | def getOne(self, id): | ||||
| if self.api.cache.get(self.cache_key) is not None: | if self.api.cache.get(self.cache_key) is not None: | ||||
| @@ -17,36 +17,36 @@ class BaseView(FlaskView): | |||||
| else: | else: | ||||
| return json.dumps(self.model.get_one(id)) | return json.dumps(self.model.get_one(id)) | ||||
| @route('/') | |||||
| @route('/', methods=["GET"]) | |||||
| def getAll(self): | def getAll(self): | ||||
| if self.api.cache.get(self.cache_key) is not None: | if self.api.cache.get(self.cache_key) is not None: | ||||
| return json.dumps(self.api.cache.get(self.cache_key)) | return json.dumps(self.api.cache.get(self.cache_key)) | ||||
| else: | else: | ||||
| return json.dumps(self.model.get_all()) | return json.dumps(self.model.get_all()) | ||||
| def pre_post_callback(self, data): | |||||
| def _pre_post_callback(self, data): | |||||
| pass | pass | ||||
| def post_post_callback(self, m): | |||||
| def _post_post_callback(self, m): | |||||
| pass | pass | ||||
| @route('/', methods=["POST"]) | @route('/', methods=["POST"]) | ||||
| def post(self): | def post(self): | ||||
| data = request.json | data = request.json | ||||
| self.pre_post_callback(data) | |||||
| self._pre_post_callback(data) | |||||
| m = self.model.insert(**data) | m = self.model.insert(**data) | ||||
| if self.api.cache.get(self.cache_key) is not None: | if self.api.cache.get(self.cache_key) is not None: | ||||
| self.api.cache.get(self.cache_key)[m.id] = m | self.api.cache.get(self.cache_key)[m.id] = m | ||||
| self.post_post_callback(m) | |||||
| self._post_post_callback(m) | |||||
| return json.dumps(m) | return json.dumps(m) | ||||
| def pre_put_callback(self, m): | |||||
| def _pre_put_callback(self, m): | |||||
| pass | pass | ||||
| def post_put_callback(self, m): | |||||
| def _post_put_callback(self, m): | |||||
| pass | pass | ||||
| @@ -59,32 +59,32 @@ class BaseView(FlaskView): | |||||
| except: | except: | ||||
| pass | pass | ||||
| if self.api.cache.get(self.cache_key) is not None: | 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) | self.api.cache.get(self.cache_key)[id].__dict__.update(**data) | ||||
| m = self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__) | 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]) | return json.dumps(self.api.cache.get(self.cache_key)[id]) | ||||
| else: | else: | ||||
| m = self.model.update(**data) | m = self.model.update(**data) | ||||
| self.post_put_callback(m) | |||||
| self._post_put_callback(m) | |||||
| return json.dumps(m) | return json.dumps(m) | ||||
| def pre_delete_callback(self, m): | |||||
| def _pre_delete_callback(self, m): | |||||
| pass | pass | ||||
| def post_delete_callback(self, id): | |||||
| def _post_delete_callback(self, id): | |||||
| pass | pass | ||||
| @route('/<int:id>', methods=["DELETE"]) | @route('/<int:id>', methods=["DELETE"]) | ||||
| def delete(self, id): | def delete(self, id): | ||||
| if self.api.cache.get(self.cache_key) is not None: | 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] | del self.api.cache.get(self.cache_key)[id] | ||||
| m = self.model.delete(id) | m = self.model.delete(id) | ||||
| def post_delete_callback(self, id): | |||||
| def _post_delete_callback(self, id): | |||||
| pass | pass | ||||
| return ('',204) | return ('',204) | ||||
| @@ -159,9 +159,8 @@ class SensorAPI(object): | |||||
| def log_action(self, text): | def log_action(self, text): | ||||
| filename = "./logs/action.log" | filename = "./logs/action.log" | ||||
| formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime()) | formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime()) | ||||
| with open(filename, "a") as file: | with open(filename, "a") as file: | ||||
| text = text.encode("utf-8") | |||||
| file.write("%s,%s\n" % (formatted_time, text)) | file.write("%s,%s\n" % (formatted_time, text)) | ||||
| def shutdown_sensor(self, id): | def shutdown_sensor(self, id): | ||||
| @@ -243,6 +242,15 @@ class CraftBeerPi(ActorAPI, SensorAPI): | |||||
| else: | else: | ||||
| return cfg.value | 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): | def add_config_parameter(self, name, value, type, description, options=None): | ||||
| from modules.config import Config | from modules.config import Config | ||||
| with self.app.app_context(): | with self.app.app_context(): | ||||
| @@ -281,10 +289,13 @@ class CraftBeerPi(ActorAPI, SensorAPI): | |||||
| def actor(self, cls): | def actor(self, cls): | ||||
| return self.__parseProps("actor_types", cls) | return self.__parseProps("actor_types", cls) | ||||
| def actor2(self, description="", power=True, **options): | def actor2(self, description="", power=True, **options): | ||||
| def decorator(f): | def decorator(f): | ||||
| print f() | print f() | ||||
| print f | |||||
| print options | print options | ||||
| print description | print description | ||||
| return f | return f | ||||
| @@ -458,7 +469,7 @@ class CraftBeerPi(ActorAPI, SensorAPI): | |||||
| def job(interval, method): | def job(interval, method): | ||||
| while True: | while True: | ||||
| try: | try: | ||||
| method() | |||||
| method(self) | |||||
| except Exception as e: | except Exception as e: | ||||
| self.app.logger.error("Exception" + method.__name__ + ": " + str(e)) | self.app.logger.error("Exception" + method.__name__ + ": " + str(e)) | ||||
| self.socketio.sleep(interval) | 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 | @classmethod | ||||
| def init_global(cls): | def init_global(cls): | ||||
| print "GLOBAL ACTOR INIT" | |||||
| pass | |||||
| def get_config_parameter(self, key, default_value): | def get_config_parameter(self, key, default_value): | ||||
| return self.api.get_config_parameter(key, default_value) | return self.api.get_config_parameter(key, default_value) | ||||
| @@ -14,10 +14,10 @@ class Base(object): | |||||
| self.api.socketio.sleep(seconds) | self.api.socketio.sleep(seconds) | ||||
| def init(self): | def init(self): | ||||
| print "INIT BASE" | |||||
| pass | |||||
| def stop(self): | def stop(self): | ||||
| print "STOP HARDWARE" | |||||
| pass | |||||
| def update(self, **kwds): | def update(self, **kwds): | ||||
| pass | pass | ||||
| @@ -99,10 +99,10 @@ class ActorBase(Base): | |||||
| return 1 | return 1 | ||||
| def set_power(self, power): | def set_power(self, power): | ||||
| print "SET POWER TO %s" % power | |||||
| pass | |||||
| def on(self, power=0): | def on(self, power=0): | ||||
| print "ON" | |||||
| pass | |||||
| def off(self): | def off(self): | ||||
| print "OFF" | |||||
| pass | |||||
| @@ -44,7 +44,7 @@ class KettleAPI(NotificationAPI): | |||||
| return self.api.cache.get("kettle").get(id).target_temp | return self.api.cache.get("kettle").get(id).target_temp | ||||
| def set_target_temp(self, temp, id=None): | def set_target_temp(self, temp, id=None): | ||||
| temp = int(temp) | |||||
| temp = float(temp) | |||||
| try: | try: | ||||
| if id is None: | if id is None: | ||||
| @@ -59,7 +59,7 @@ class Timer(object): | |||||
| timer_end = Property.Number("TIMER_END", configurable=False) | timer_end = Property.Number("TIMER_END", configurable=False) | ||||
| def start_timer(self, timer): | def start_timer(self, timer): | ||||
| print "START TIMER NEW" | |||||
| if self.timer_end is not None: | if self.timer_end is not None: | ||||
| return | return | ||||
| self.timer_end = int(time.time()) + timer | self.timer_end = int(time.time()) + timer | ||||
| @@ -100,13 +100,13 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI): | |||||
| self.n = True | self.n = True | ||||
| def init(self): | def init(self): | ||||
| print "INIT STEP" | |||||
| pass | |||||
| def finish(self): | def finish(self): | ||||
| print "FINSIH STEP" | |||||
| pass | |||||
| def reset(self): | def reset(self): | ||||
| print "REST STEP" | |||||
| pass | |||||
| def execute(self): | def execute(self): | ||||
| print "-------------" | print "-------------" | ||||
| @@ -63,18 +63,19 @@ class FermenterView(BaseView): | |||||
| model = Fermenter | model = Fermenter | ||||
| cache_key = "fermenter" | cache_key = "fermenter" | ||||
| def post_post_callback(self, m): | |||||
| def _post_post_callback(self, m): | |||||
| m.state = False | m.state = False | ||||
| m.steps = [] | m.steps = [] | ||||
| def pre_put_callback(self, m): | |||||
| def _pre_put_callback(self, m): | |||||
| m.state = False | m.state = False | ||||
| try: | try: | ||||
| m.instance.stop() | m.instance.stop() | ||||
| except: | except: | ||||
| pass | pass | ||||
| def post_put_callback(self, m): | |||||
| def _post_put_callback(self, m): | |||||
| m.state = False | m.state = False | ||||
| @route('/<int:id>/targettemp/<temp>', methods=['POST']) | @route('/<int:id>/targettemp/<temp>', methods=['POST']) | ||||
| @@ -282,7 +283,7 @@ class FermenterView(BaseView): | |||||
| @cbpi.backgroundtask(key="read_target_temps_fermenter", interval=5) | @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 | background process that reads all passive sensors in interval of 1 second | ||||
| :return: None | :return: None | ||||
| @@ -295,7 +296,7 @@ def read_target_temps(): | |||||
| instance = FermenterView() | instance = FermenterView() | ||||
| @cbpi.backgroundtask(key="fermentation_task", interval=1) | @cbpi.backgroundtask(key="fermentation_task", interval=1) | ||||
| def execute_fermentation_step(): | |||||
| def execute_fermentation_step(api): | |||||
| with cbpi.app.app_context(): | with cbpi.app.app_context(): | ||||
| instance.check_step() | instance.check_step() | ||||
| @@ -310,6 +311,6 @@ def init_active_steps(): | |||||
| @cbpi.initalizer(order=1) | @cbpi.initalizer(order=1) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE CONFIG MODULE" | |||||
| FermenterView.register(cbpi.app, route_base='/api/fermenter') | FermenterView.register(cbpi.app, route_base='/api/fermenter') | ||||
| FermenterView.init_cache() | FermenterView.init_cache() | ||||
| @@ -15,7 +15,7 @@ class Kettle2View(BaseView): | |||||
| cache_key = "kettle" | cache_key = "kettle" | ||||
| @classmethod | @classmethod | ||||
| def pre_post_callback(self, data): | |||||
| def _pre_post_callback(self, data): | |||||
| data["target_temp"] = 0 | data["target_temp"] = 0 | ||||
| @classmethod | @classmethod | ||||
| @@ -23,16 +23,16 @@ class Kettle2View(BaseView): | |||||
| obj.state = False | obj.state = False | ||||
| def post_post_callback(self, m): | |||||
| def _post_post_callback(self, m): | |||||
| m.state = False | m.state = False | ||||
| def pre_put_callback(self, m): | |||||
| def _pre_put_callback(self, m): | |||||
| try: | try: | ||||
| m.instance.stop() | m.instance.stop() | ||||
| except: | except: | ||||
| pass | pass | ||||
| def post_put_callback(self, m): | |||||
| def _post_put_callback(self, m): | |||||
| m.state = False | m.state = False | ||||
| @route('/<int:id>/targettemp/<temp>', methods=['POST']) | @route('/<int:id>/targettemp/<temp>', methods=['POST']) | ||||
| @@ -76,11 +76,11 @@ def set_target_temp(id, temp): | |||||
| :param temp: target temp to set | :param temp: target temp to set | ||||
| :return: None | :return: None | ||||
| ''' | ''' | ||||
| print "GOT EVENT %s %s" % (id, temp) | |||||
| Kettle2View().postTargetTemp(id,temp) | Kettle2View().postTargetTemp(id,temp) | ||||
| @cbpi.backgroundtask(key="read_target_temps", interval=5) | @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 | background process that reads all passive sensors in interval of 1 second | ||||
| :return: None | :return: None | ||||
| @@ -105,5 +105,4 @@ def init(app): | |||||
| :param app: the flask app | :param app: the flask app | ||||
| :return: None | :return: None | ||||
| """ | """ | ||||
| print "INITIALIZE LOG MODULE" | |||||
| LogView.register(cbpi.app, route_base='/api/logs') | 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" | cache_key = "sensors" | ||||
| def post_post_callback(self, m): | |||||
| def _post_post_callback(self, m): | |||||
| cbpi.init_sensor(m.id) | cbpi.init_sensor(m.id) | ||||
| def post_put_callback(self, m): | |||||
| def _post_put_callback(self, m): | |||||
| cbpi.stop_sensor(m.id) | cbpi.stop_sensor(m.id) | ||||
| cbpi.init_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.stop_sensor(m.id) | ||||
| @cbpi.initalizer(order=1000) | @cbpi.initalizer(order=1000) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE SENSOR MODULE" | |||||
| SensorView.register(cbpi.app, route_base='/api/sensor') | SensorView.register(cbpi.app, route_base='/api/sensor') | ||||
| SensorView.init_cache() | SensorView.init_cache() | ||||
| cbpi.init_sensors() | cbpi.init_sensors() | ||||
| @cbpi.backgroundtask(key="read_passiv_sensor", interval=5) | @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 | background process that reads all passive sensors in interval of 1 second | ||||
| :return: None | :return: None | ||||
| @@ -16,7 +16,7 @@ def getserial(): | |||||
| @cbpi.initalizer(order=9999) | @cbpi.initalizer(order=9999) | ||||
| def sendStats(cbpi): | def sendStats(cbpi): | ||||
| print "INITIALIZE STATS" | |||||
| try: | try: | ||||
| serial = getserial() | serial = getserial() | ||||
| @@ -66,16 +66,14 @@ class Step(DBModel): | |||||
| class StepView(BaseView): | class StepView(BaseView): | ||||
| model = Step | model = Step | ||||
| def pre_post_callback(self, data): | |||||
| def _pre_post_callback(self, data): | |||||
| order = self.model.get_max_order() | order = self.model.get_max_order() | ||||
| data["order"] = 1 if order is None else order + 1 | data["order"] = 1 if order is None else order + 1 | ||||
| data["state"] = "I" | data["state"] = "I" | ||||
| @route('/sort', methods=["POST"]) | @route('/sort', methods=["POST"]) | ||||
| def sort_steps(self): | def sort_steps(self): | ||||
| Step.sort(request.json) | Step.sort(request.json) | ||||
| cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all()) | cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all()) | ||||
| return ('', 204) | return ('', 204) | ||||
| @@ -87,16 +85,12 @@ class StepView(BaseView): | |||||
| @route('/action/<method>', methods=["POST"]) | @route('/action/<method>', methods=["POST"]) | ||||
| def action(self, method): | def action(self, method): | ||||
| cbpi.cache["active_step"].__getattribute__(method)() | cbpi.cache["active_step"].__getattribute__(method)() | ||||
| return ('', 204) | return ('', 204) | ||||
| @route('/reset', methods=["POST"]) | @route('/reset', methods=["POST"]) | ||||
| def reset(self): | def reset(self): | ||||
| self.model.reset_all_steps() | self.model.reset_all_steps() | ||||
| #db.session.commit() | |||||
| self.stop_step() | self.stop_step() | ||||
| cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all()) | cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all()) | ||||
| return ('', 204) | return ('', 204) | ||||
| @@ -133,7 +127,6 @@ class StepView(BaseView): | |||||
| return ('', 204) | return ('', 204) | ||||
| def init_step(self, step): | def init_step(self, step): | ||||
| cbpi.log_action("Start Step %s" % step.name) | cbpi.log_action("Start Step %s" % step.name) | ||||
| type_cfg = cbpi.cache.get("step_types").get(step.type) | type_cfg = cbpi.cache.get("step_types").get(step.type) | ||||
| if type_cfg is None: | 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))) | 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 = type_cfg.get("class")(**cfg) | ||||
| instance.init() | instance.init() | ||||
| # set step instance to ache | # set step instance to ache | ||||
| cbpi.cache["active_step"] = instance | cbpi.cache["active_step"] = instance | ||||
| @@ -204,7 +196,6 @@ def init_after_startup(): | |||||
| # step type not found. cant restart step | # step type not found. cant restart step | ||||
| return | return | ||||
| print "STEP SATE......", step.stepstate | |||||
| cfg = step.stepstate.copy() | cfg = step.stepstate.copy() | ||||
| cfg.update(dict(api=cbpi, id=step.id, managed_fields=get_manged_fields_as_array(type_cfg))) | cfg.update(dict(api=cbpi, id=step.id, managed_fields=get_manged_fields_as_array(type_cfg))) | ||||
| instance = type_cfg.get("class")(**cfg) | instance = type_cfg.get("class")(**cfg) | ||||
| @@ -213,7 +204,7 @@ def init_after_startup(): | |||||
| @cbpi.initalizer(order=2000) | @cbpi.initalizer(order=2000) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE STEPS MODULE" | |||||
| StepView.register(cbpi.app, route_base='/api/step') | StepView.register(cbpi.app, route_base='/api/step') | ||||
| def get_all(): | def get_all(): | ||||
| @@ -225,7 +216,7 @@ def init(cbpi): | |||||
| cbpi.add_cache_callback("steps", get_all) | cbpi.add_cache_callback("steps", get_all) | ||||
| @cbpi.backgroundtask(key="step_task", interval=0.1) | @cbpi.backgroundtask(key="step_task", interval=0.1) | ||||
| def execute_step(): | |||||
| def execute_step(api): | |||||
| ''' | ''' | ||||
| Background job which executes the step | Background job which executes the step | ||||
| :return: | :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 flask_classy import FlaskView, route | ||||
| from git import Repo, Git | from git import Repo, Git | ||||
| @@ -101,7 +102,44 @@ class SystemView(FlaskView): | |||||
| def dump(self): | def dump(self): | ||||
| return json.dumps(cbpi.cache) | 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() | @cbpi.initalizer() | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE SYSTEM MODULE" | |||||
| SystemView.api = cbpi | |||||
| SystemView.register(cbpi.app, route_base='/api/system') | 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) | @cbpi.initalizer(order=10) | ||||
| def init(cbpi): | def init(cbpi): | ||||
| print "INITIALIZE UI" | |||||
| cbpi.app.register_blueprint(react, url_prefix='/ui') | cbpi.app.register_blueprint(react, url_prefix='/ui') | ||||
| @react.route('/') | |||||
| @react.route('/', methods=["GET"]) | |||||
| def index(): | def index(): | ||||
| return react.send_static_file("index.html") | return react.send_static_file("index.html") | ||||
| @@ -52,11 +52,15 @@ | |||||
| /* Einschränken des sichtbaren Ausschnitts */ | |||||
| </style> | </style> | ||||
| <title>CraftBeerPi 3.0</title> | <title>CraftBeerPi 3.0</title> | ||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| <div id="root"></div> | |||||
| <div id="root" ></div> | |||||
| <script src="static/bundle.js" type="text/javascript"></script> | <script src="static/bundle.js" type="text/javascript"></script> | ||||
| <!-- | <!-- | ||||
| This HTML file is a template. | 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); | |||||