mirror of
https://github.com/Manuel83/craftbeerpi3
synced 2026-08-09 18:52:40 +02:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b7908c9c5 | ||
|
|
13836a5680 |
@@ -0,0 +1,4 @@
|
||||
from modules.core.core import CraftBeerPI, Addon
|
||||
|
||||
cbpi = CraftBeerPI()
|
||||
cbpi.addon = Addon(cbpi)
|
||||
@@ -1,6 +1,8 @@
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
from flask_classy import FlaskView, route
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
|
||||
class ActionView(FlaskView):
|
||||
|
||||
@@ -15,9 +17,10 @@ class ActionView(FlaskView):
|
||||
200:
|
||||
description: action invoked
|
||||
"""
|
||||
data = request.json
|
||||
|
||||
self.cbpi.cache["actions"][action]["function"](self.cbpi)
|
||||
|
||||
obj = self.cbpi.cache["actions"][action]["class"](self.cbpi)
|
||||
obj.execute(**data)
|
||||
return ('',204)
|
||||
|
||||
@cbpi.addon.core.initializer()
|
||||
@@ -28,4 +31,4 @@ def init(cbpi):
|
||||
:return: None
|
||||
"""
|
||||
ActionView.cbpi = cbpi
|
||||
ActionView.register(cbpi._app, route_base='/api/action')
|
||||
ActionView.register(cbpi.web, route_base='/api/action')
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import time
|
||||
|
||||
from flask import request
|
||||
from flask_classy import route
|
||||
from flask_login import login_required
|
||||
|
||||
from modules.core.db import DBModel
|
||||
from modules.core.core import cbpi
|
||||
from modules.core.baseview import BaseView
|
||||
from modules import cbpi
|
||||
from modules.core.baseview import RestApi
|
||||
from modules.database.dbmodel import Actor
|
||||
|
||||
|
||||
class ActorView(BaseView):
|
||||
class ActorView(RestApi):
|
||||
model = Actor
|
||||
cache_key = "actors"
|
||||
|
||||
@@ -121,7 +123,7 @@ class ActorView(BaseView):
|
||||
def toggleTimeJob(self, id, t):
|
||||
self.api.cache.get("actors").get(int(id)).timer = int(time.time()) + int(t)
|
||||
self.toggle(int(id))
|
||||
self.api._socketio.sleep(t)
|
||||
self.api.sleep(t)
|
||||
self.api.cache.get("actors").get(int(id)).timer = None
|
||||
self.toggle(int(id))
|
||||
|
||||
@@ -179,13 +181,18 @@ class ActorView(BaseView):
|
||||
200:
|
||||
description: Actor Action called
|
||||
"""
|
||||
|
||||
self.api.actor.action(id, method)
|
||||
data = request.json
|
||||
if data:
|
||||
cbpi.actor.action(id, method, **data)
|
||||
else:
|
||||
cbpi.actor.action(id, method)
|
||||
return ('', 204)
|
||||
|
||||
|
||||
|
||||
|
||||
@cbpi.addon.core.initializer(order=1000)
|
||||
def init(cbpi):
|
||||
ActorView.register(cbpi._app, route_base='/api/actor')
|
||||
ActorView.register(cbpi.web, route_base='/api/actor')
|
||||
ActorView.init_cache()
|
||||
#cbpi.init_actors()
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
from modules.core.baseapi import Buzzer
|
||||
from modules.core.basetypes import Actor, KettleController, FermenterController
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from modules.core.proptypes import Property
|
||||
|
||||
@cbpi.addon.actor.type("Dummy Actor")
|
||||
class Dummy(Actor):
|
||||
|
||||
|
||||
@cbpi.addon.actor.action("WOHOO")
|
||||
def myaction(self):
|
||||
pass
|
||||
# Decorator to create a parameter based action
|
||||
@cbpi.addon.actor.action("Run until Temp reached", parameters={"t": Property.Text(label="Target Temp")})
|
||||
def check_sensor_value(self, t=1):
|
||||
|
||||
def check(api, id, value):
|
||||
'''
|
||||
Background Prozess which checks the sensor value every second
|
||||
:param api:
|
||||
:param id:
|
||||
:param value:
|
||||
:return:
|
||||
'''
|
||||
while api.sensor.get_value(1) < value:
|
||||
api.sleep(1)
|
||||
api.actor.off(id)
|
||||
|
||||
target_value = int(t)
|
||||
|
||||
# Create notificaiton
|
||||
self.api.notify(headline="Waiting", message="Waiting for temp %s" % target_value)
|
||||
# Switch actor on
|
||||
self.api.actor.on(self.id, 100)
|
||||
# Start Background task
|
||||
self.api.start_background_task(check, self.api, id=self.id, value=target_value)
|
||||
|
||||
|
||||
def on(self, power=100):
|
||||
'''
|
||||
@@ -16,11 +39,10 @@ class Dummy(Actor):
|
||||
:param power: int value between 0 - 100
|
||||
:return:
|
||||
'''
|
||||
print "ON"
|
||||
print "ID %s ON" % self.id
|
||||
|
||||
def off(self):
|
||||
print "OFF"
|
||||
|
||||
print "ID %s OFF" % self.id
|
||||
|
||||
|
||||
@cbpi.addon.kettle.controller()
|
||||
@@ -28,6 +50,7 @@ class MyController(KettleController):
|
||||
|
||||
def run(self):
|
||||
while self.is_running():
|
||||
print "HALLO"
|
||||
|
||||
self.sleep(1)
|
||||
|
||||
@@ -38,6 +61,8 @@ class MyController2(FermenterController):
|
||||
def run(self):
|
||||
while self.is_running():
|
||||
print "HALLO"
|
||||
|
||||
self.get_target_temp()
|
||||
self.sleep(1)
|
||||
|
||||
@cbpi.addon.core.initializer(order=200)
|
||||
@@ -45,6 +70,7 @@ def init(cbpi):
|
||||
|
||||
class MyBuzzer(Buzzer):
|
||||
def beep(self):
|
||||
print "BEEEEEP"
|
||||
pass
|
||||
|
||||
cbpi.buzzer = MyBuzzer()
|
||||
|
||||
@@ -3,11 +3,12 @@ import os
|
||||
|
||||
from os.path import join
|
||||
|
||||
from modules.core.basetypes import Actor, Sensor
|
||||
from modules.core.core import cbpi
|
||||
from modules.core.basetypes import Actor, Sensor, Action
|
||||
from modules import cbpi
|
||||
from modules.core.proptypes import Property
|
||||
import random
|
||||
|
||||
|
||||
@cbpi.addon.sensor.type("Dummy Sensor")
|
||||
class Dummy(Sensor):
|
||||
|
||||
@@ -21,10 +22,15 @@ class Dummy(Sensor):
|
||||
else:
|
||||
self.unit = "°F"
|
||||
|
||||
@cbpi.addon.sensor.action("WOHOO")
|
||||
def myaction(self):
|
||||
@cbpi.addon.sensor.action(label="Set Dummy Temp", parameters={
|
||||
"p1":Property.Select(label="Temp",options=[1,2,3]),
|
||||
|
||||
})
|
||||
def myaction(self, p1):
|
||||
self.text = p1
|
||||
self.update_value(int(p1))
|
||||
|
||||
|
||||
print "SENSOR ACTION HALLO!!!"
|
||||
|
||||
def execute(self):
|
||||
while True:
|
||||
@@ -32,16 +38,30 @@ class Dummy(Sensor):
|
||||
self.update_value(int(self.text))
|
||||
except:
|
||||
pass
|
||||
self.api.sleep(1)
|
||||
self.api.sleep(5)
|
||||
|
||||
@cbpi.addon.core.action(key="clear", label="Clear all Logs")
|
||||
def woohoo(cbpi):
|
||||
|
||||
dir = "./logs"
|
||||
test = os.listdir(dir)
|
||||
@cbpi.addon.core.action(name="Delete All Logs")
|
||||
class ParameterAction(Action):
|
||||
|
||||
for item in test:
|
||||
p1 = Property.Number("P1", configurable=True, description="Target Temperature of Mash Step", unit="C")
|
||||
p2 = Property.Number("P2", configurable=True, description="Target Temperature of Mash Step", unit="C")
|
||||
|
||||
if item.endswith(".log"):
|
||||
os.remove(join(dir, item))
|
||||
cbpi.notify(headline="Logs Deleted",message="All Logs Cleared")
|
||||
|
||||
def execute(self, p1, p2, **kwargs):
|
||||
for i in range(5):
|
||||
cbpi.sleep(1)
|
||||
cbpi.notify(headline="Woohoo", message="%s %s" % (p1, p2))
|
||||
|
||||
|
||||
@cbpi.addon.core.action(name="Delete All Logs")
|
||||
class DeleteAllLogs(Action):
|
||||
def execute(self, **kwargs):
|
||||
dir = "./logs"
|
||||
test = os.listdir(dir)
|
||||
|
||||
for item in test:
|
||||
|
||||
if item.endswith(".log"):
|
||||
os.remove(join(dir, item))
|
||||
cbpi.notify(headline="Logs Deleted", message="All Logs Cleared")
|
||||
@@ -1,6 +1,7 @@
|
||||
from modules.core.basetypes import Step
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from modules.core.proptypes import Property
|
||||
import time
|
||||
|
||||
|
||||
@cbpi.addon.step.type("Dummy Step")
|
||||
@@ -17,9 +18,231 @@ class Dummy(Step):
|
||||
time = Property.Text(label="Text", configurable=True, description="WOHOOO")
|
||||
|
||||
def execute(self):
|
||||
#print self.text
|
||||
|
||||
pass
|
||||
|
||||
def reset(self):
|
||||
|
||||
self.stop_timer()
|
||||
self.stop_timer()
|
||||
|
||||
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
@cbpi.addon.step.type("Dummy Step")
|
||||
class MashStep(Step):
|
||||
'''
|
||||
Just put the decorator @cbpi.step on top of a method
|
||||
'''
|
||||
# Properties
|
||||
temp = Property.Number("Temperature", configurable=True, description="Target Temperature of Mash Step", unit="C")
|
||||
kettle = Property.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.addon.step.action("Start Timer")
|
||||
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.api.beep()
|
||||
self.notify("Mash Step Completed!", "Starting the next step", timeout=None)
|
||||
self.next()
|
||||
|
||||
|
||||
@cbpi.addon.step.type("MashInStep")
|
||||
class MashInStep(Step):
|
||||
'''
|
||||
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 = Property.Kettle("Kettle", description="Kettle in which the mashing takes place")
|
||||
s = False
|
||||
|
||||
|
||||
def init(self):
|
||||
'''
|
||||
Initialize Step. This method is called once at the beginning of the step
|
||||
:return:
|
||||
'''
|
||||
# set target tep
|
||||
self.s = False
|
||||
self.set_target_temp(self.temp, 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) and self.s is False:
|
||||
self.s = True
|
||||
self.notify("Step Temp Reached!", "Please press the next button to continue", timeout=None)
|
||||
|
||||
|
||||
|
||||
@cbpi.addon.step.type("MashInStep")
|
||||
class ChilStep(Step):
|
||||
|
||||
timer = Property.Number("Timer in Minutes", configurable=True, default_value=0, description="Timer is started immediately")
|
||||
|
||||
@cbpi.addon.step.action("Start Timer")
|
||||
def start(self):
|
||||
if self.is_timer_finished() is None:
|
||||
self.start_timer(int(self.timer) * 60)
|
||||
|
||||
def reset(self):
|
||||
self.stop_timer()
|
||||
|
||||
|
||||
def finish(self):
|
||||
pass
|
||||
|
||||
def execute(self):
|
||||
if self.is_timer_finished() is None:
|
||||
self.start_timer(int(self.timer) * 60)
|
||||
|
||||
if self.is_timer_finished() == True:
|
||||
self.next()
|
||||
|
||||
@cbpi.addon.step.type("MashInStep")
|
||||
class PumpStep(Step):
|
||||
|
||||
pump = Property.Actor("Pump", description="Pump actor gets toogled")
|
||||
timer = Property.Number("Timer in Minutes", configurable=True, default_value=0, description="Timer is started immediately")
|
||||
|
||||
@cbpi.addon.step.action("Start Timer")
|
||||
def start(self):
|
||||
if self.is_timer_finished() is None:
|
||||
self.start_timer(int(self.timer) * 60)
|
||||
|
||||
def reset(self):
|
||||
self.stop_timer()
|
||||
|
||||
|
||||
def finish(self):
|
||||
self.actor_off(int(self.pump))
|
||||
|
||||
def init(self):
|
||||
self.actor_on(int(self.pump))
|
||||
|
||||
def execute(self):
|
||||
if self.is_timer_finished() is None:
|
||||
self.start_timer(int(self.timer) * 60)
|
||||
|
||||
if self.is_timer_finished() == True:
|
||||
self.next()
|
||||
|
||||
@cbpi.addon.step.type("MashInStep")
|
||||
class BoilStep(Step):
|
||||
'''
|
||||
Just put the decorator @cbpi.step on top of a method
|
||||
'''
|
||||
# Properties
|
||||
temp = Property.Number("Temperature", configurable=True, default_value=100, description="Target temperature for boiling")
|
||||
kettle = Property.Kettle("Kettle", description="Kettle in which the boiling step takes place")
|
||||
timer = Property.Number("Timer in Minutes", configurable=True, default_value=90, description="Timer is started when target temperature is reached")
|
||||
hop_1 = Property.Number("Hop 1 Addition", configurable=True, description="Fist Hop alert")
|
||||
hop_1_added = Property.Number("",default_value=None)
|
||||
hop_2 = Property.Number("Hop 2 Addition", configurable=True, description="Second Hop alert")
|
||||
hop_2_added = Property.Number("", default_value=None)
|
||||
hop_3 = Property.Number("Hop 3 Addition", configurable=True)
|
||||
hop_3_added = Property.Number("", default_value=None, description="Third Hop alert")
|
||||
hop_4 = Property.Number("Hop 4 Addition", configurable=True)
|
||||
hop_4_added = Property.Number("", default_value=None, description="Fourth Hop alert")
|
||||
hop_5 = Property.Number("Hop 5 Addition", configurable=True)
|
||||
hop_5_added = Property.Number("", default_value=None, description="Fives Hop alert")
|
||||
|
||||
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.addon.step.action("Start Timer")
|
||||
def start(self):
|
||||
'''
|
||||
Custom Action which can be execute form the brewing dashboard.
|
||||
All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface
|
||||
:return:
|
||||
'''
|
||||
if self.is_timer_finished() is None:
|
||||
self.start_timer(int(self.timer) * 60)
|
||||
|
||||
def reset(self):
|
||||
self.stop_timer()
|
||||
self.set_target_temp(self.temp, self.kettle)
|
||||
|
||||
def finish(self):
|
||||
self.set_target_temp(0, self.kettle)
|
||||
|
||||
|
||||
def check_hop_timer(self, number, value):
|
||||
|
||||
if self.__getattribute__("hop_%s_added" % number) is not True and time.time() > (
|
||||
self.timer_end - (int(self.timer) * 60 - int(value) * 60)):
|
||||
self.__setattr__("hop_%s_added" % number, True)
|
||||
self.notify("Hop Alert", "Please add Hop %s" % number, timeout=None)
|
||||
|
||||
def execute(self):
|
||||
'''
|
||||
This method is execute in an interval
|
||||
:return:
|
||||
'''
|
||||
# Check if Target Temp is reached
|
||||
if self.get_kettle_temp(self.kettle) >= float(self.temp):
|
||||
# Check if Timer is Running
|
||||
if self.is_timer_finished() is None:
|
||||
self.start_timer(int(self.timer) * 60)
|
||||
else:
|
||||
self.check_hop_timer(1, self.hop_1)
|
||||
self.check_hop_timer(2, self.hop_2)
|
||||
self.check_hop_timer(3, self.hop_3)
|
||||
self.check_hop_timer(4, self.hop_4)
|
||||
self.check_hop_timer(5, self.hop_5)
|
||||
# Check if timer finished and go to next step
|
||||
if self.is_timer_finished() == True:
|
||||
self.notify("Boil Step Completed!", "Starting the next step", timeout=None)
|
||||
self.next()
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import time
|
||||
from thread import start_new_thread
|
||||
|
||||
from modules.core.baseapi import Buzzer
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
|
||||
try:
|
||||
import RPi.GPIO as GPIO
|
||||
@@ -16,19 +16,19 @@ class GPIOBuzzer(Buzzer):
|
||||
|
||||
def __init__(self, gpio):
|
||||
try:
|
||||
cbpi._app.logger.info("INIT BUZZER NOW GPIO%s" % gpio)
|
||||
cbpi.web.logger.info("INIT BUZZER NOW GPIO%s" % gpio)
|
||||
self.gpio = int(gpio)
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setup(self.gpio, GPIO.OUT)
|
||||
self.state = True
|
||||
cbpi._app.logger.info("BUZZER SETUP OK")
|
||||
cbpi.web.logger.info("BUZZER SETUP OK")
|
||||
except Exception as e:
|
||||
cbpi._app.logger.info("BUZZER EXCEPTION %s" % str(e))
|
||||
cbpi.web.logger.info("BUZZER EXCEPTION %s" % str(e))
|
||||
self.state = False
|
||||
|
||||
def beep(self):
|
||||
if self.state is False:
|
||||
cbpi._app.logger.error("BUZZER not working")
|
||||
cbpi.web.logger.error("BUZZER not working")
|
||||
return
|
||||
|
||||
def play(sound):
|
||||
|
||||
@@ -2,13 +2,13 @@ import time
|
||||
|
||||
from flask import json, request
|
||||
from flask_classy import route
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from modules.core.db import DBModel
|
||||
from modules.core.baseview import BaseView
|
||||
from modules.core.baseview import RestApi
|
||||
from modules.database.dbmodel import Config
|
||||
|
||||
|
||||
class ConfigView(BaseView):
|
||||
class ConfigView(RestApi):
|
||||
model = Config
|
||||
cache_key = "config"
|
||||
|
||||
@@ -77,7 +77,7 @@ class ConfigView(BaseView):
|
||||
@classmethod
|
||||
def init_cache(cls):
|
||||
|
||||
with cls.api._app.app_context():
|
||||
with cls.api.web.app_context():
|
||||
cls.api.cache[cls.cache_key] = {}
|
||||
for key, value in cls.model.get_all().iteritems():
|
||||
cls.post_init_callback(value)
|
||||
@@ -85,5 +85,5 @@ class ConfigView(BaseView):
|
||||
|
||||
@cbpi.addon.core.initializer(order=0)
|
||||
def init(cbpi):
|
||||
ConfigView.register(cbpi._app, route_base='/api/config')
|
||||
ConfigView.register(cbpi.web, route_base='/api/config')
|
||||
ConfigView.init_cache()
|
||||
|
||||
+62
-26
@@ -22,6 +22,9 @@ class BaseAPI(object):
|
||||
except:
|
||||
doc = ""
|
||||
|
||||
if self.cbpi.cache.get(key) is None:
|
||||
self.cbpi.cache[key] = {}
|
||||
self.cbpi.logger.debug(name)
|
||||
self.cbpi.cache.get(key)[name] = {"name": name, "class": cls, "description":doc, "properties": [], "actions": []}
|
||||
self.cbpi.cache.get(key)[name].update(options)
|
||||
members = [attr for attr in dir(tmpObj) if not callable(getattr(tmpObj, attr)) and not attr.startswith("__")]
|
||||
@@ -29,23 +32,39 @@ class BaseAPI(object):
|
||||
t = tmpObj.__getattribute__(m)
|
||||
|
||||
if isinstance(t, Property.Number):
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable, "description": t.description, "default_value": t.default_value})
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable, "description": t.description, "default_value": t.default_value, "unit": t.unit})
|
||||
elif isinstance(t, Property.Text):
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "text", "required": t.required, "configurable": t.configurable, "description": t.description, "default_value": t.default_value})
|
||||
elif isinstance(tmpObj.__getattribute__(m), Property.Select):
|
||||
elif isinstance(t, Property.Select):
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options, "description": t.description})
|
||||
elif isinstance(tmpObj.__getattribute__(m), Property.Actor):
|
||||
elif isinstance(t, Property.Actor):
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "actor", "configurable": True, "description": t.description})
|
||||
elif isinstance(tmpObj.__getattribute__(m), Property.Sensor):
|
||||
elif isinstance(t, Property.Sensor):
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "sensor", "configurable": True, "description": t.description})
|
||||
elif isinstance(tmpObj.__getattribute__(m), Property.Kettle):
|
||||
elif isinstance(t, Property.Kettle):
|
||||
self.cbpi.cache.get(key)[name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": True, "description": t.description})
|
||||
|
||||
for method_name, method in cls.__dict__.iteritems():
|
||||
if hasattr(method, "action"):
|
||||
label = method.__getattribute__("label")
|
||||
self.cbpi.cache.get(key)[name]["actions"].append({"method": method_name, "label": label})
|
||||
parameters = method.__getattribute__("parameters")
|
||||
props = []
|
||||
if parameters is not None:
|
||||
for k, t in parameters.iteritems():
|
||||
if isinstance(t, Property.Number):
|
||||
props.append({"name": k, "label": t.label, "type": "number", "configurable": t.configurable, "description": t.description, "default_value": t.default_value, "unit": t.unit})
|
||||
elif isinstance(t, Property.Text):
|
||||
props.append({"name": k, "label": t.label, "type": "text", "required": t.required, "configurable": t.configurable, "description": t.description, "default_value": t.default_value})
|
||||
elif isinstance(t, Property.Select):
|
||||
props.append({"name": k, "label": t.label, "type": "select", "configurable": True, "options": t.options, "description": t.description})
|
||||
elif isinstance(t, Property.Actor):
|
||||
props.append({"name": k, "label": t.label, "type": "actor", "configurable": True, "description": t.description})
|
||||
elif isinstance(t, Property.Sensor):
|
||||
props.append({"name": k, "label": t.label, "type": "sensor", "configurable": True, "description": t.description})
|
||||
elif isinstance(t, Property.Kettle):
|
||||
props.append({"name": k, "label": t.label, "type": "kettle", "configurable": True, "description": t.description})
|
||||
|
||||
self.cbpi.cache.get(key)[name]["actions"].append({"method": method_name, "label": label, "properties":props })
|
||||
return cls
|
||||
|
||||
|
||||
@@ -59,9 +78,11 @@ class SensorAPI(BaseAPI):
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def action(self, label):
|
||||
def action(self, label, parameters=None):
|
||||
|
||||
def real_decorator(func):
|
||||
func.action = True
|
||||
func.parameters = parameters
|
||||
func.label = label
|
||||
return func
|
||||
return real_decorator
|
||||
@@ -80,10 +101,11 @@ class StepAPI(BaseAPI):
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def action(self, label):
|
||||
def action(self, label, parameters=None):
|
||||
def real_decorator(func):
|
||||
func.action = True
|
||||
func.label = label
|
||||
func.parameters = parameters
|
||||
return func
|
||||
return real_decorator
|
||||
|
||||
@@ -100,10 +122,11 @@ class ActorAPI(BaseAPI):
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def action(self, label):
|
||||
def action(self, label, parameters=None):
|
||||
def real_decorator(func):
|
||||
func.action = True
|
||||
func.label = label
|
||||
func.parameters = parameters
|
||||
return func
|
||||
return real_decorator
|
||||
|
||||
@@ -119,10 +142,11 @@ class KettleAPI(BaseAPI):
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def action(self, label):
|
||||
def action(self, label, parameters=None):
|
||||
def real_decorator(func):
|
||||
func.action = True
|
||||
func.label = label
|
||||
func.parameters = parameters
|
||||
return func
|
||||
return real_decorator
|
||||
|
||||
@@ -136,10 +160,11 @@ class FermenterAPI(BaseAPI):
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def action(self, label):
|
||||
def action(self, label, parameters=None):
|
||||
def real_decorator(func):
|
||||
func.action = True
|
||||
func.label = label
|
||||
func.parameters = parameters
|
||||
return func
|
||||
return real_decorator
|
||||
|
||||
@@ -156,7 +181,6 @@ class CoreAPI(BaseAPI):
|
||||
self.cbpi.cache["web_menu"] =[]
|
||||
|
||||
def init(self):
|
||||
|
||||
self.cbpi.cache["init"] = sorted(self.cbpi.cache["init"], key=lambda k: k['order'])
|
||||
for value in self.cbpi.cache.get("init"):
|
||||
|
||||
@@ -168,12 +192,17 @@ class CoreAPI(BaseAPI):
|
||||
try:
|
||||
method(self.cbpi)
|
||||
except Exception as e:
|
||||
print e
|
||||
self.cbpi._socketio.sleep(interval)
|
||||
self.cbpi.logger.error(e)
|
||||
self.cbpi.sleep(interval)
|
||||
|
||||
for value in self.cbpi.cache.get("background"):
|
||||
t = self.cbpi._socketio.start_background_task(target=job, interval=value.get("interval"), method=value.get("function"))
|
||||
t = self.cbpi.start_background_task(target=job, interval=value.get("interval"), method=value.get("function"))
|
||||
|
||||
def action(self, **options):
|
||||
def decorator(f):
|
||||
BaseAPI.parseProps(self, "actions", f)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def add_js(self, name, file):
|
||||
self.cbpi.cache["js"][name] = file
|
||||
@@ -187,15 +216,7 @@ class CoreAPI(BaseAPI):
|
||||
return f
|
||||
return decorator
|
||||
|
||||
|
||||
def action(self, key, label, **options):
|
||||
def decorator(f):
|
||||
self.cbpi.cache.get("actions")[key] = {"label": label, "function": f}
|
||||
return f
|
||||
return decorator
|
||||
|
||||
|
||||
def backgroundjob(self, key, interval, **options):
|
||||
def backgroundtask(self, key, interval, **options):
|
||||
def decorator(f):
|
||||
self.cbpi.cache.get("background").append({"function": f, "key": key, "interval": interval})
|
||||
return f
|
||||
@@ -220,5 +241,20 @@ class CoreAPI(BaseAPI):
|
||||
|
||||
class Buzzer(object):
|
||||
|
||||
def beep():
|
||||
pass
|
||||
def beep(self):
|
||||
pass
|
||||
|
||||
class Addon(object):
|
||||
def __init__(self, cbpi):
|
||||
self.step = StepAPI(cbpi)
|
||||
self.actor = ActorAPI(cbpi)
|
||||
self.sensor = SensorAPI(cbpi)
|
||||
self.kettle = KettleAPI(cbpi)
|
||||
self.fermenter = FermenterAPI(cbpi)
|
||||
self.core = CoreAPI(cbpi)
|
||||
|
||||
def init(self):
|
||||
self.core.init()
|
||||
self.step.init()
|
||||
self.actor.init()
|
||||
self.sensor.init()
|
||||
@@ -12,11 +12,15 @@ class Base(object):
|
||||
self.value = None
|
||||
self.__dirty = False
|
||||
|
||||
class Action(Base):
|
||||
|
||||
def execute(self):
|
||||
pass
|
||||
|
||||
class Actor(Base):
|
||||
|
||||
@classmethod
|
||||
def init_global(cls):
|
||||
|
||||
pass
|
||||
|
||||
def init(self):
|
||||
@@ -68,9 +72,6 @@ class Sensor(Base):
|
||||
print "EXECUTE"
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
class ControllerBase(object):
|
||||
__dirty = False
|
||||
__running = False
|
||||
@@ -227,6 +228,8 @@ class Timer(object):
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
class Step(Base, Timer):
|
||||
|
||||
|
||||
@@ -267,12 +270,31 @@ class Step(Base, Timer):
|
||||
self.value = None
|
||||
self.__dirty = False
|
||||
|
||||
def set_target_temp(self, temp, id=None):
|
||||
temp = float(temp)
|
||||
try:
|
||||
if id is None:
|
||||
self.api.emit("SET_TARGET_TEMP", id=self.kettle_id, temp=temp)
|
||||
else:
|
||||
self.api.emit("SET_TARGET_TEMP", id=id, temp=temp)
|
||||
except Exception as e:
|
||||
|
||||
self.api.notify("Faild to set Target Temp", "", type="warning")
|
||||
|
||||
def get_kettle_temp(self, id=None):
|
||||
id = int(id)
|
||||
if id is None:
|
||||
id = self.kettle_id
|
||||
return self.api.sensor.get_value(int(self.api.cache.get("kettle").get(id).sensor))
|
||||
|
||||
def is_dirty(self):
|
||||
return self.__dirty
|
||||
|
||||
def reset_dirty(self):
|
||||
self.__dirty = False
|
||||
|
||||
def notify(self, headline, messsage, timeout=None):
|
||||
self.api.notify(headline, messsage, timeout)
|
||||
def __setattr__(self, name, value):
|
||||
|
||||
if name != "_StepBase__dirty" and name in self.managed_fields:
|
||||
|
||||
@@ -2,10 +2,10 @@ from flask import request, json
|
||||
from flask_classy import route, FlaskView
|
||||
from flask_login import login_required
|
||||
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
|
||||
|
||||
class BaseView(FlaskView):
|
||||
class RestApi(FlaskView):
|
||||
|
||||
as_array = False
|
||||
cache_key = None
|
||||
@@ -39,7 +39,7 @@ class BaseView(FlaskView):
|
||||
def post(self):
|
||||
|
||||
data = request.json
|
||||
self.api._app.logger.info("INSERT Model %s", self.model.__name__)
|
||||
self.api.web.logger.info("INSERT Model %s", self.model.__name__)
|
||||
self._pre_post_callback(data)
|
||||
m = self.model.insert(**data)
|
||||
if self.api.cache.get(self.cache_key) is not None:
|
||||
@@ -101,7 +101,7 @@ class BaseView(FlaskView):
|
||||
|
||||
@classmethod
|
||||
def init_cache(cls):
|
||||
with cls.api._app.app_context():
|
||||
with cls.api.web.app_context():
|
||||
|
||||
if cls.model.__as_array__ is True:
|
||||
cls.api.cache[cls.cache_key] = []
|
||||
|
||||
+62
-53
@@ -22,7 +22,6 @@ from modules.database.dbmodel import Kettle
|
||||
class ComplexEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
try:
|
||||
|
||||
if isinstance(obj, DBModel):
|
||||
return obj.__dict__
|
||||
elif isinstance(obj, Actor):
|
||||
@@ -33,31 +32,11 @@ class ComplexEncoder(json.JSONEncoder):
|
||||
return obj()
|
||||
else:
|
||||
return None
|
||||
|
||||
return None
|
||||
except TypeError as e:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
class Addon(object):
|
||||
def __init__(self, cbpi):
|
||||
self.step = StepAPI(cbpi)
|
||||
self.actor = ActorAPI(cbpi)
|
||||
self.sensor = SensorAPI(cbpi)
|
||||
self.kettle = KettleAPI(cbpi)
|
||||
self.fermenter = FermenterAPI(cbpi)
|
||||
self.core = CoreAPI(cbpi)
|
||||
|
||||
def init(self):
|
||||
self.core.init()
|
||||
self.step.init()
|
||||
self.actor.init()
|
||||
self.sensor.init()
|
||||
# self.kettle.init()
|
||||
# self.fermenter.init()
|
||||
|
||||
|
||||
class ActorCore(object):
|
||||
key = "actor_types"
|
||||
|
||||
@@ -82,8 +61,7 @@ class ActorCore(object):
|
||||
actor.power = 100
|
||||
self.cbpi.emit("INIT_ACTOR", id=id)
|
||||
except Exception as e:
|
||||
print e
|
||||
self.cbpi._app.logger.error(e)
|
||||
self.cbpi.web.logger.error(e)
|
||||
|
||||
def stop_one(self, id):
|
||||
self.cbpi.cache["actors"][id]["instance"].stop()
|
||||
@@ -99,7 +77,7 @@ class ActorCore(object):
|
||||
self.cbpi.emit("SWITCH_ACTOR_ON", id=id, power=power)
|
||||
return True
|
||||
except Exception as e:
|
||||
print e
|
||||
self.cbpi.logger.error(e)
|
||||
return False
|
||||
|
||||
def off(self, id):
|
||||
@@ -111,7 +89,7 @@ class ActorCore(object):
|
||||
self.cbpi.emit("SWITCH_ACTOR_OFF", id=id)
|
||||
return True
|
||||
except Exception as e:
|
||||
print e
|
||||
self.cbpi.logger.error(e)
|
||||
return False
|
||||
|
||||
def toggle(self, id):
|
||||
@@ -129,11 +107,11 @@ class ActorCore(object):
|
||||
self.cbpi.emit("SWITCH_ACTOR_POWER_CHANGE", id=id, power=power)
|
||||
return True
|
||||
except Exception as e:
|
||||
print e
|
||||
self.cbpi.logger.error(e)
|
||||
return False
|
||||
|
||||
def action(self, id, method):
|
||||
self.cbpi.cache.get("actors").get(id).instance.__getattribute__(method)()
|
||||
def action(self, id, method, **data):
|
||||
self.cbpi.cache.get("actors").get(id).instance.__getattribute__(method)(**data)
|
||||
|
||||
|
||||
def toggle_timeout(self, id, seconds):
|
||||
@@ -182,7 +160,7 @@ class SensorCore(object):
|
||||
|
||||
except Exception as e:
|
||||
print "ERROR"
|
||||
self.cbpi._app.logger.error(e)
|
||||
self.cbpi.web.logger.error(e)
|
||||
|
||||
def stop_one(self, id):
|
||||
|
||||
@@ -206,12 +184,12 @@ class SensorCore(object):
|
||||
with open(filename, "a") as file:
|
||||
file.write(msg)
|
||||
|
||||
def action(self, id, method):
|
||||
self.cbpi.cache.get("sensors").get(id).instance.__getattribute__(method)()
|
||||
|
||||
def action(self, id, method, **data):
|
||||
self.cbpi.cache.get("sensors").get(id).instance.__getattribute__(method)(**data)
|
||||
|
||||
|
||||
class BrewingCore(object):
|
||||
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
self.cbpi.cache["step_types"] = {}
|
||||
@@ -240,7 +218,7 @@ class BrewingCore(object):
|
||||
# Start controller
|
||||
if kettle.logic is not None:
|
||||
cfg = kettle.config.copy()
|
||||
cfg.update(dict(api=cbpi, kettle_id=kettle.id, heater=kettle.heater, sensor=kettle.sensor))
|
||||
cfg.update(dict(api=self.cbpi, kettle_id=kettle.id, heater=kettle.heater, sensor=kettle.sensor))
|
||||
instance = self.get_controller(kettle.logic).get("class")(**cfg)
|
||||
instance.init()
|
||||
kettle.instance = instance
|
||||
@@ -250,13 +228,13 @@ class BrewingCore(object):
|
||||
|
||||
t = self.cbpi._socketio.start_background_task(target=run, instance=instance)
|
||||
kettle.state = not kettle.state
|
||||
self.cbpi.ws_emit("UPDATE_KETTLE", cbpi.cache.get("kettle").get(id))
|
||||
self.cbpi.ws_emit("UPDATE_KETTLE", self.cbpi.cache.get("kettle").get(id))
|
||||
self.cbpi.emit("KETTLE_CONTROLLER_STARTED", id=id)
|
||||
else:
|
||||
# Stop controller
|
||||
kettle.instance.stop()
|
||||
kettle.state = not kettle.state
|
||||
self.cbpi.ws_emit("UPDATE_KETTLE", cbpi.cache.get("kettle").get(id))
|
||||
self.cbpi.ws_emit("UPDATE_KETTLE", self.cbpi.cache.get("kettle").get(id))
|
||||
self.cbpi.emit("KETTLE_CONTROLLER_STOPPED", id=id)
|
||||
|
||||
|
||||
@@ -270,28 +248,50 @@ class FermentationCore(object):
|
||||
return self.cbpi.cache["fermentation_controller_types"].get(name)
|
||||
|
||||
|
||||
class Logger(object):
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
|
||||
def error(self, msg, *args, **kwargs):
|
||||
self.cbpi.web.logger.error(msg, *args, **kwargs)
|
||||
|
||||
def info(self, msg, *args, **kwargs):
|
||||
self.cbpi.web.logger.info(msg, *args, **kwargs)
|
||||
|
||||
def debug(self, msg, *args, **kwargs):
|
||||
self.cbpi.web.logger.debug(msg, *args, **kwargs)
|
||||
|
||||
def warning(self, msg, *args, **kwargs):
|
||||
self.cbpi.web.logger.warning(msg, *args, **kwargs)
|
||||
|
||||
class CraftBeerPI(object):
|
||||
cache = {}
|
||||
eventbus = {}
|
||||
|
||||
def __init__(self):
|
||||
FORMAT = '%(asctime)-15s - %(levelname)s - %(message)s'
|
||||
logging.basicConfig(filename='./logs/app.log', level=logging.INFO, format=FORMAT)
|
||||
self.cache["messages"] = []
|
||||
self.cache["version"] = "3.1"
|
||||
self.modules = {}
|
||||
FORMAT = '%(asctime)-15s - %(levelname)s - %(message)s'
|
||||
logging.basicConfig(filename='./logs/app.log', level=logging.INFO, format=FORMAT)
|
||||
logging.getLogger('socketio').setLevel(logging.ERROR)
|
||||
logging.getLogger('engineio').setLevel(logging.ERROR)
|
||||
self.web = Flask(__name__)
|
||||
self.logger = Logger(self)
|
||||
|
||||
self.logger.info("###Startup CraftBeerPi %s ###" % self.cache.get("version"))
|
||||
self.web.secret_key = 'Cr4ftB33rP1'
|
||||
self.web.json_encoder = ComplexEncoder
|
||||
self._socketio = SocketIO(self.web, json=json, logging=False)
|
||||
|
||||
|
||||
self.modules = {}
|
||||
self.addon = Addon(self)
|
||||
self.actor = ActorCore(self)
|
||||
self.sensor = SensorCore(self)
|
||||
self.brewing = BrewingCore(self)
|
||||
self.fermentation = FermentationCore(self)
|
||||
self._app = Flask(__name__)
|
||||
self._app.secret_key = 'Cr4ftB33rP1'
|
||||
self._app.json_encoder = ComplexEncoder
|
||||
self._socketio = SocketIO(self._app, json=json, logging=False)
|
||||
|
||||
@self._app.route('/')
|
||||
@self.web.route('/')
|
||||
def index():
|
||||
return redirect('ui')
|
||||
|
||||
@@ -303,11 +303,11 @@ class CraftBeerPI(object):
|
||||
self.actor.init()
|
||||
self.beep()
|
||||
try:
|
||||
port = int(cbpi.get_config_parameter('port', '5000'))
|
||||
port = int(self.get_config_parameter('port', '5000'))
|
||||
except ValueError:
|
||||
port = 5000
|
||||
print port
|
||||
self._socketio.run(self._app, host='0.0.0.0', port=port)
|
||||
|
||||
self._socketio.run(self.web, host='0.0.0.0', port=port)
|
||||
|
||||
def beep(self):
|
||||
self.buzzer.beep()
|
||||
@@ -315,6 +315,10 @@ class CraftBeerPI(object):
|
||||
def sleep(self, seconds):
|
||||
self._socketio.sleep(seconds)
|
||||
|
||||
def start_background_task(self, target, *args, **kwargs):
|
||||
|
||||
self._socketio.start_background_task(target, *args, **kwargs)
|
||||
|
||||
def notify(self, headline, message, type="success", timeout=5000):
|
||||
msg = {"id": str(uuid.uuid1()), "type": type, "headline": headline, "message": message, "timeout": timeout}
|
||||
self.ws_emit("NOTIFY", msg)
|
||||
@@ -324,10 +328,10 @@ class CraftBeerPI(object):
|
||||
|
||||
def __init_db(self, ):
|
||||
|
||||
with self._app.app_context():
|
||||
with self.web.app_context():
|
||||
db = self.get_db()
|
||||
try:
|
||||
with self._app.open_resource('../../config/schema.sql', mode='r') as f:
|
||||
with self.web.open_resource('../../config/schema.sql', mode='r') as f:
|
||||
db.cursor().executescript(f.read())
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
@@ -364,15 +368,23 @@ class CraftBeerPI(object):
|
||||
method.callback = True
|
||||
self.cache[key] = method
|
||||
|
||||
def get_config_parameter(self, key, default):
|
||||
def get_config_parameter(self, key, default=None):
|
||||
cfg = self.cache["config"].get(key)
|
||||
if cfg is None:
|
||||
return default
|
||||
else:
|
||||
return cfg.value
|
||||
|
||||
def emit(self, key, **kwargs):
|
||||
def set_config_parameter(self, name, value):
|
||||
from modules.config import Config
|
||||
with self.web.app_context():
|
||||
update_data = {"name": name, "value": value}
|
||||
self.cache.get("config")[name].__dict__.update(**update_data)
|
||||
c = Config.update(**update_data)
|
||||
self.ws_emit("UPDATE_CONFIG", c)
|
||||
|
||||
|
||||
def emit(self, key, **kwargs):
|
||||
if self.eventbus.get(key) is not None:
|
||||
for value in self.eventbus[key]:
|
||||
if value["async"] is False:
|
||||
@@ -391,6 +403,3 @@ class CraftBeerPI(object):
|
||||
|
||||
self.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None)
|
||||
|
||||
|
||||
cbpi = CraftBeerPI()
|
||||
addon = cbpi.addon
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import sqlite3
|
||||
import os
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from modules.core.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"])
|
||||
cbpi.web.logger.info("SKIP DB FILE: %s" % data["file"])
|
||||
return
|
||||
try:
|
||||
with sqlite3.connect("craftbeerpi.db") as conn:
|
||||
@@ -18,14 +18,13 @@ def execute_file(curernt_version, data):
|
||||
cur.execute("INSERT INTO schema_info (version,filename) values (?,?)", (data["version"], data["file"]))
|
||||
conn.commit()
|
||||
|
||||
except sqlite3.OperationalError as err:
|
||||
|
||||
print err
|
||||
except sqlite3.OperationalError as e:
|
||||
cbpi.logger.error(e)
|
||||
|
||||
@cbpi.addon.core.initializer(order=-9999)
|
||||
def init(cbpi):
|
||||
|
||||
with cbpi._app.app_context():
|
||||
with cbpi.web.app_context():
|
||||
conn = get_db()
|
||||
cur = conn.cursor()
|
||||
current_version = None
|
||||
|
||||
@@ -16,6 +16,7 @@ class Property(object):
|
||||
self.configurable = configurable
|
||||
self.default_value = default_value
|
||||
self.description = description
|
||||
self.unit = unit
|
||||
|
||||
class Text(PropertyType):
|
||||
def __init__(self, label, configurable=False, required=False, default_value="", description=""):
|
||||
@@ -44,5 +45,6 @@ class Property(object):
|
||||
def __init__(self, label, description=""):
|
||||
PropertyType.__init__(self)
|
||||
self.label = label
|
||||
self.unit = ""
|
||||
self.configurable = True
|
||||
self.description = description
|
||||
|
||||
@@ -131,4 +131,5 @@ class FermenterStep(DBModel):
|
||||
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()
|
||||
get_db().commit()
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from flask import Blueprint
|
||||
|
||||
from modules.core.core import cbpi, addon
|
||||
from modules import cbpi
|
||||
from flask_swagger import swagger
|
||||
from flask import json
|
||||
from flask import Blueprint
|
||||
|
||||
@addon.core.initializer(order=22)
|
||||
@cbpi.addon.core.initializer(order=22)
|
||||
def web(cbpi):
|
||||
|
||||
s = Blueprint('web_view', __name__, template_folder='templates', static_folder='static')
|
||||
@@ -16,4 +16,4 @@ def web(cbpi):
|
||||
|
||||
|
||||
cbpi.addon.core.add_menu_link("JQuery View", "/web_view")
|
||||
cbpi._app.register_blueprint(s, url_prefix='/web_view')
|
||||
cbpi.web.register_blueprint(s, url_prefix='/web_view')
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from flask import Blueprint
|
||||
|
||||
from modules.core.core import cbpi, addon
|
||||
from modules import cbpi
|
||||
from flask_swagger import swagger
|
||||
from flask import json
|
||||
from flask import Blueprint
|
||||
|
||||
@addon.core.initializer(order=22)
|
||||
@cbpi.addon.core.initializer(order=22)
|
||||
def web(cbpi):
|
||||
|
||||
s = Blueprint('webviewreact', __name__, template_folder='templates', static_folder='static')
|
||||
@@ -16,4 +16,4 @@ def web(cbpi):
|
||||
|
||||
|
||||
cbpi.addon.core.add_menu_link("ReactJS View", "/webviewreact")
|
||||
cbpi._app.register_blueprint(s, url_prefix='/webviewreact')
|
||||
cbpi.web.register_blueprint(s, url_prefix='/webviewreact')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from modules.core.core import cbpi, addon
|
||||
from modules import cbpi
|
||||
from flask_swagger import swagger
|
||||
from flask import json
|
||||
from flask import Blueprint
|
||||
|
||||
@addon.core.initializer(order=22)
|
||||
@cbpi.addon.core.initializer(order=22)
|
||||
def hello(cbpi):
|
||||
|
||||
s = Blueprint('react', __name__, template_folder='templates', static_folder='static')
|
||||
@@ -14,10 +14,10 @@ def hello(cbpi):
|
||||
|
||||
@s.route('/swagger.json', methods=["GET"])
|
||||
def spec():
|
||||
swag = swagger(cbpi._app)
|
||||
swag = swagger(cbpi.web)
|
||||
swag['info']['version'] = "3.0"
|
||||
swag['info']['title'] = "CraftBeerPi"
|
||||
return json.dumps(swag)
|
||||
|
||||
cbpi.addon.core.add_menu_link("Swagger API", "/swagger")
|
||||
cbpi._app.register_blueprint(s, url_prefix='/swagger')
|
||||
cbpi.web.register_blueprint(s, url_prefix='/swagger')
|
||||
|
||||
@@ -2,13 +2,13 @@ import time
|
||||
from flask import request
|
||||
from flask_classy import route
|
||||
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from modules.core.db import get_db, DBModel
|
||||
from modules.core.baseview import BaseView
|
||||
from modules.core.baseview import RestApi
|
||||
from modules.database.dbmodel import Fermenter, FermenterStep
|
||||
|
||||
|
||||
class FermenterView(BaseView):
|
||||
class FermenterView(RestApi):
|
||||
model = Fermenter
|
||||
cache_key = "fermenter"
|
||||
|
||||
@@ -106,6 +106,7 @@ class FermenterView(BaseView):
|
||||
|
||||
@route('/<int:id>/start', methods=['POST'])
|
||||
def start_fermentation(self, id):
|
||||
print "START"
|
||||
active = None
|
||||
for idx, s in enumerate(cbpi.cache.get(self.cache_key)[id].steps):
|
||||
if s.state == 'A':
|
||||
@@ -196,6 +197,7 @@ class FermenterView(BaseView):
|
||||
return cbpi.cache["fermenter"].get(id)
|
||||
|
||||
def target_temp_reached(self,id, step):
|
||||
print "TARGET TEMP REACHED"
|
||||
timestamp = time.time()
|
||||
|
||||
days = step.days * 24 * 60 * 60
|
||||
@@ -210,19 +212,21 @@ class FermenterView(BaseView):
|
||||
cbpi.ws_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))
|
||||
current_temp = current_temp = cbpi.sensor.get_value(int(fermenter.sensor))
|
||||
|
||||
if value.timer_start is None:
|
||||
|
||||
print "TIMER IS NONE"
|
||||
if value.direction == "H" :
|
||||
|
||||
print "TIMER WATING FOR HEATING"
|
||||
if current_temp >= value.temp:
|
||||
self.target_temp_reached(key,value)
|
||||
else:
|
||||
print "TIMER WATING FOR COILING"
|
||||
if current_temp <= value.temp:
|
||||
self.target_temp_reached(key, value)
|
||||
else:
|
||||
@@ -231,10 +235,11 @@ class FermenterView(BaseView):
|
||||
else:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.api.looger.error(e)
|
||||
pass
|
||||
|
||||
|
||||
@cbpi.addon.core.backgroundjob(key="read_target_temps_fermenter", interval=5)
|
||||
@cbpi.addon.core.backgroundtask(key="read_target_temps_fermenter", interval=5)
|
||||
def read_target_temps(cbpi):
|
||||
"""
|
||||
background process that reads all passive sensors in interval of 1 second
|
||||
@@ -247,9 +252,9 @@ def read_target_temps(cbpi):
|
||||
|
||||
instance = FermenterView()
|
||||
|
||||
@cbpi.addon.core.backgroundjob(key="fermentation_task", interval=1)
|
||||
@cbpi.addon.core.backgroundtask(key="fermentation_task", interval=1)
|
||||
def execute_fermentation_step(cbpi):
|
||||
with cbpi._app.app_context():
|
||||
with cbpi.web.app_context():
|
||||
instance.check_step()
|
||||
|
||||
|
||||
@@ -262,5 +267,5 @@ def init_active_steps():
|
||||
def init(cbpi):
|
||||
|
||||
cbpi.cache["fermenter_task"] = {}
|
||||
FermenterView.register(cbpi._app, route_base='/api/fermenter')
|
||||
FermenterView.register(cbpi.web, route_base='/api/fermenter')
|
||||
FermenterView.init_cache()
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from flask import request
|
||||
from flask_classy import FlaskView, route
|
||||
from modules.core.core import cbpi
|
||||
from modules.core.baseview import BaseView
|
||||
from modules import cbpi
|
||||
from modules.core.baseview import RestApi
|
||||
from modules.core.db import DBModel
|
||||
from modules.database.dbmodel import Kettle
|
||||
|
||||
class KettleView(BaseView):
|
||||
class KettleView(RestApi):
|
||||
model = Kettle
|
||||
cache_key = "kettle"
|
||||
|
||||
@@ -217,7 +217,18 @@ class KettleView(BaseView):
|
||||
self.api.brewing.toggle_automatic(id)
|
||||
return ('', 204)
|
||||
|
||||
@cbpi.addon.core.backgroundjob(key="read_target_temps", interval=5)
|
||||
|
||||
@cbpi.addon.core.listen("SET_TARGET_TEMP")
|
||||
def set_target_temp(id, temp):
|
||||
'''
|
||||
Change Taget Temp Event
|
||||
:param id: kettle id
|
||||
:param temp: target temp to set
|
||||
:return: None
|
||||
'''
|
||||
KettleView().postTargetTemp(id,temp)
|
||||
|
||||
@cbpi.addon.core.backgroundtask(key="read_target_temps", interval=5)
|
||||
def read_target_temps(api):
|
||||
"""
|
||||
background process that reads all passive sensors in interval of 1 second
|
||||
@@ -230,5 +241,5 @@ def read_target_temps(api):
|
||||
@cbpi.addon.core.initializer()
|
||||
def init(cbpi):
|
||||
KettleView.api = cbpi
|
||||
KettleView.register(cbpi._app, route_base='/api/kettle')
|
||||
KettleView.register(cbpi.web, route_base='/api/kettle')
|
||||
KettleView.init_cache()
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import flask_login
|
||||
from flask import request
|
||||
|
||||
from modules.core.core import cbpi, addon
|
||||
from modules import cbpi
|
||||
|
||||
class User(flask_login.UserMixin):
|
||||
pass
|
||||
|
||||
@addon.core.initializer(order=0)
|
||||
@cbpi.addon.core.initializer(order=0)
|
||||
def log(cbpi):
|
||||
|
||||
|
||||
cbpi._login_manager = flask_login.LoginManager()
|
||||
cbpi._login_manager.init_app(cbpi._app)
|
||||
@cbpi._app.route('/login', methods=['POST'])
|
||||
cbpi._login_manager.init_app(cbpi.web)
|
||||
|
||||
@cbpi.web.route('/login', methods=['POST'])
|
||||
def login():
|
||||
|
||||
data = request.json
|
||||
@@ -30,16 +31,30 @@ def log(cbpi):
|
||||
return ('',401)
|
||||
|
||||
|
||||
@cbpi._app.route('/logout', methods=['POST'])
|
||||
@cbpi.web.route('/logout', methods=['POST'])
|
||||
def logout():
|
||||
flask_login.logout_user()
|
||||
return 'Logged out'
|
||||
|
||||
@cbpi._login_manager.request_loader
|
||||
def load_user_from_request(request):
|
||||
|
||||
|
||||
api_key = request.args.get('api_key')
|
||||
|
||||
if cbpi.get_config_parameter("password_security", "NO") == "NO":
|
||||
user = User()
|
||||
user.id = "craftbeerpi"
|
||||
return user
|
||||
elif api_key == "123":
|
||||
user = User()
|
||||
user.id = "craftbeerpi"
|
||||
return user
|
||||
return None
|
||||
|
||||
@cbpi._login_manager.user_loader
|
||||
def user_loader(user):
|
||||
|
||||
|
||||
|
||||
if cbpi.get_config_parameter("password_security", "NO") == "YES":
|
||||
if user != "craftbeerpi":
|
||||
return
|
||||
@@ -53,4 +68,6 @@ def log(cbpi):
|
||||
|
||||
@cbpi._login_manager.unauthorized_handler
|
||||
def unauthorized_handler():
|
||||
|
||||
|
||||
return ('Please login',401)
|
||||
|
||||
@@ -2,7 +2,7 @@ import datetime
|
||||
import os
|
||||
from flask import Blueprint, request, send_from_directory, json
|
||||
from flask_classy import FlaskView, route
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
|
||||
|
||||
class LogView(FlaskView):
|
||||
@@ -172,4 +172,4 @@ def init(cbpi):
|
||||
:param app: the flask app
|
||||
:return: None
|
||||
"""
|
||||
LogView.register(cbpi._app, route_base='/api/logs')
|
||||
LogView.register(cbpi.web, route_base='/api/logs')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import json
|
||||
from flask_classy import FlaskView, route
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
|
||||
class NotificationView(FlaskView):
|
||||
|
||||
@@ -63,4 +63,4 @@ def init(cbpi):
|
||||
msg = {"id": len(cbpi.cache["messages"]), "type": "info", "headline": "Support CraftBeerPi with your donation", "message": "You will find the PayPay Donation button in the system menu" , "read": False}
|
||||
cbpi.cache["messages"].append(msg)
|
||||
|
||||
NotificationView.register(cbpi._app, route_base='/api/notification')
|
||||
NotificationView.register(cbpi.web, route_base='/api/notification')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from flask import request, send_from_directory, json
|
||||
from importlib import import_module
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from git import Repo
|
||||
import os
|
||||
import requests
|
||||
@@ -46,6 +46,7 @@ class PluginView(FlaskView):
|
||||
response = requests.get("https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins.yaml")
|
||||
self.api.cache["plugins"] = self.merge(yaml.load(response.text), self.api.cache["plugins"])
|
||||
for key, value in cbpi.cache["plugins"].iteritems():
|
||||
|
||||
value["installed"] = os.path.isdir("./plugins/%s/" % (key))
|
||||
return json.dumps(cbpi.cache["plugins"])
|
||||
|
||||
@@ -135,4 +136,4 @@ class PluginView(FlaskView):
|
||||
def init(cbpi):
|
||||
cbpi.cache["plugins"] = {}
|
||||
PluginView.api = cbpi
|
||||
PluginView.register(cbpi._app, route_base='/api/plugin')
|
||||
PluginView.register(cbpi.web, route_base='/api/plugin')
|
||||
@@ -0,0 +1,146 @@
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
import datetime
|
||||
from flask import json, request, send_from_directory
|
||||
from flask_classy import route, FlaskView
|
||||
|
||||
from modules.core.db import DBModel
|
||||
from modules.core.baseview import RestApi
|
||||
from modules import cbpi
|
||||
from modules.database.dbmodel import Step
|
||||
from yaml import Loader, Dumper
|
||||
from yaml import load, dump
|
||||
|
||||
from modules.step import StepView
|
||||
|
||||
|
||||
class RecipeBook(FlaskView):
|
||||
|
||||
|
||||
@route('/load', methods=["POST"])
|
||||
def load(self):
|
||||
|
||||
data = request.json
|
||||
recipe_name = data.get("name")
|
||||
if re.match("^[A-Za-z0-9_-]*$", recipe_name) is None:
|
||||
return ('Recipie Name contains not allowed characters', 500)
|
||||
|
||||
with open("./recipes/%s.json" % recipe_name) as json_data:
|
||||
d = json.load(json_data)
|
||||
Step.delete_all()
|
||||
StepView().reset()
|
||||
for s in d["steps"]:
|
||||
Step.insert(**{"name": s.get("name"), "type": s.get("type"), "config": s.get("config")})
|
||||
|
||||
self.api.ws_emit("UPDATE_ALL_STEPS", Step.get_all())
|
||||
self.api.notify(headline="Recipe %s loaded successfully" % recipe_name, message="")
|
||||
|
||||
return ('', 204)
|
||||
|
||||
@route('/download/<name>', methods=["GET"])
|
||||
def download(self, name):
|
||||
|
||||
"""
|
||||
Download a log file by name
|
||||
---
|
||||
tags:
|
||||
- logs
|
||||
parameters:
|
||||
- in: path
|
||||
name: file
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
description: filename
|
||||
responses:
|
||||
200:
|
||||
description: Log file downloaded
|
||||
"""
|
||||
file = "%s.json" % name
|
||||
|
||||
if not self.check_filename(file):
|
||||
return ('File Not Found111', 404)
|
||||
return send_from_directory('../../recipes', file, as_attachment=True, attachment_filename=file)
|
||||
|
||||
def check_filename(self, name):
|
||||
import re
|
||||
print "CHECK"
|
||||
pattern = re.compile('^([A-Za-z0-9-_])+.json$')
|
||||
return True if pattern.match(name) else False
|
||||
|
||||
@route('/<name>', methods=["DELETE"])
|
||||
def remove(self, name):
|
||||
|
||||
|
||||
recipe_name = name
|
||||
if re.match("^[A-Za-z0-9_-]*$", recipe_name) is None:
|
||||
return ('Recipie Name contains not allowed characters', 500)
|
||||
|
||||
filename = "./recipes/%s.json" % recipe_name
|
||||
if os.path.isfile(filename) == True:
|
||||
os.remove(filename)
|
||||
self.api.notify(headline="Recipe %s deleted successfully" % recipe_name, message="")
|
||||
return ('', 204)
|
||||
else:
|
||||
self.api.notify(headline="Faild to delete Recipe %s deleted" % recipe_name, message="")
|
||||
return ('', 404)
|
||||
|
||||
return ('', 204)
|
||||
|
||||
|
||||
@route('/', methods=["GET"])
|
||||
def get_all(self):
|
||||
result = []
|
||||
for filename in os.listdir("./recipes"):
|
||||
if filename.endswith(".json"):
|
||||
|
||||
result.append({"id":filename.split(".")[0], "name": filename.split(".")[0], "change_date": self._modification_date('./recipes/%s' % filename)})
|
||||
return json.dumps(result)
|
||||
|
||||
|
||||
def _modification_date(self, filename):
|
||||
t = os.path.getmtime(filename)
|
||||
return datetime.datetime.fromtimestamp(t).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@route('/save', methods=["POST"])
|
||||
def save(self):
|
||||
"""
|
||||
Save Recepie
|
||||
---
|
||||
tags:
|
||||
- steps
|
||||
responses:
|
||||
204:
|
||||
description: Recipe saved
|
||||
"""
|
||||
|
||||
|
||||
|
||||
recipe_name = self.api.get_config_parameter("brew_name")
|
||||
|
||||
if recipe_name is None or len(recipe_name) <= 0:
|
||||
self.api.notify(headline="Please set brew name", message="Recipe not saved!", type="danger")
|
||||
return ('Recipie Name contains not allowed characters', 500)
|
||||
|
||||
if re.match("^[\sA-Za-z0-9_-]*$", recipe_name) is None:
|
||||
self.api.notify(headline="Only alphanummeric charaters are allowd for recipe name", message="", type="danger")
|
||||
return ('Recipie Name contains not allowed characters', 500)
|
||||
|
||||
recipe_data = {"name": recipe_name, "steps": Step.get_all()}
|
||||
|
||||
file_name = recipe_name.replace(" ", "_")
|
||||
with open('./recipes/%s.json' % file_name, 'w') as outfile:
|
||||
json.dump(recipe_data, outfile, indent=4)
|
||||
|
||||
self.api.notify(headline="Recipe %s saved successfully" % recipe_name, message="")
|
||||
|
||||
|
||||
return ('', 204)
|
||||
|
||||
|
||||
@cbpi.addon.core.initializer(order=2000)
|
||||
def init(cbpi):
|
||||
RecipeBook.api = cbpi
|
||||
RecipeBook.register(cbpi.web, route_base='/api/recipebook')
|
||||
@@ -2,7 +2,7 @@ from flask import json, request
|
||||
from flask_classy import FlaskView, route
|
||||
from git import Repo, Git
|
||||
import sqlite3
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from werkzeug.utils import secure_filename
|
||||
import pprint
|
||||
import time
|
||||
@@ -55,11 +55,12 @@ class BeerXMLImport(FlaskView):
|
||||
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"))
|
||||
file.save(os.path.join(self.api.get_config_parameter('UPLOAD_FOLDER', "./upload"), "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.logger.error(e)
|
||||
self.api.notify(headline="Upload Failed", message="Failed to upload Beer xml", type="danger")
|
||||
return ('', 500)
|
||||
|
||||
@@ -88,12 +89,12 @@ class BeerXMLImport(FlaskView):
|
||||
name = self.getRecipeName(id)
|
||||
self.api.set_config_parameter("brew_name", name)
|
||||
boil_time = self.getBoilTime(id)
|
||||
mashstep_type = cbpi.get_config_parameter("step_mash", "MashStep")
|
||||
mash_kettle = cbpi.get_config_parameter("step_mash_kettle", None)
|
||||
mashstep_type = self.api.get_config_parameter("step_mash", "MashStep")
|
||||
mash_kettle = self.api.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
|
||||
boilstep_type = self.api.get_config_parameter("step_boil", "BoilStep")
|
||||
boil_kettle = self.api.get_config_parameter("step_boil_kettle", None)
|
||||
boil_temp = 100 if self.api.get_config_parameter("unit", "C") == "C" else 212
|
||||
|
||||
# READ KBH DATABASE
|
||||
Step.delete_all()
|
||||
@@ -108,7 +109,8 @@ class BeerXMLImport(FlaskView):
|
||||
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}})
|
||||
self.api.emit("UPDATE_ALL_STEPS", Step.get_all())
|
||||
|
||||
self.api.ws_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")
|
||||
@@ -125,9 +127,6 @@ class BeerXMLImport(FlaskView):
|
||||
return float(e.find('./RECIPE[%s]/BOIL_TIME' % (str(id))).text)
|
||||
|
||||
def getSteps(self, id):
|
||||
|
||||
|
||||
|
||||
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
|
||||
steps = []
|
||||
for e in e.findall('./RECIPE[%s]/MASH/MASH_STEPS/MASH_STEP' % (str(id))):
|
||||
@@ -144,4 +143,4 @@ class BeerXMLImport(FlaskView):
|
||||
def init(cbpi):
|
||||
|
||||
BeerXMLImport.api = cbpi
|
||||
BeerXMLImport.register(cbpi._app, route_base='/api/beerxml')
|
||||
BeerXMLImport.register(cbpi.web, route_base='/api/beerxml')
|
||||
|
||||
@@ -2,7 +2,7 @@ from flask import json, request
|
||||
from flask_classy import FlaskView, route
|
||||
from git import Repo, Git
|
||||
import sqlite3
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from werkzeug.utils import secure_filename
|
||||
import pprint
|
||||
import time
|
||||
@@ -39,7 +39,7 @@ class KBH(FlaskView):
|
||||
result.append({"id": row[0], "name": row[1], "brewed": row[2]})
|
||||
return json.dumps(result)
|
||||
except Exception as e:
|
||||
print e
|
||||
self.api.logger.error(e)
|
||||
self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger")
|
||||
return ('', 500)
|
||||
finally:
|
||||
@@ -143,4 +143,4 @@ class KBH(FlaskView):
|
||||
def init(cbpi):
|
||||
|
||||
KBH.api = cbpi
|
||||
KBH.register(cbpi._app, route_base='/api/kbh')
|
||||
KBH.register(cbpi.web, route_base='/api/kbh')
|
||||
|
||||
@@ -2,7 +2,7 @@ from flask import json, request
|
||||
from flask_classy import FlaskView, route
|
||||
from git import Repo, Git
|
||||
import sqlite3
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from werkzeug.utils import secure_filename
|
||||
import pprint
|
||||
import time
|
||||
@@ -59,4 +59,4 @@ class RESTImport(FlaskView):
|
||||
@cbpi.addon.core.initializer()
|
||||
def init(cbpi):
|
||||
RESTImport.api = cbpi
|
||||
RESTImport.register(cbpi._app, route_base='/api/recipe/import/v1')
|
||||
RESTImport.register(cbpi.web, route_base='/api/recipe/import/v1')
|
||||
|
||||
+16
-10
@@ -1,12 +1,12 @@
|
||||
import time
|
||||
from flask_classy import route
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
from modules.core.db import DBModel
|
||||
from modules.core.baseview import BaseView
|
||||
from modules.core.baseview import RestApi
|
||||
from modules.database.dbmodel import Sensor
|
||||
from flask import request
|
||||
|
||||
|
||||
class SensorView(BaseView):
|
||||
class SensorView(RestApi):
|
||||
model = Sensor
|
||||
cache_key = "sensors"
|
||||
|
||||
@@ -34,7 +34,11 @@ class SensorView(BaseView):
|
||||
200:
|
||||
description: Sensor Action called
|
||||
"""
|
||||
cbpi.sensor.action(id, method)
|
||||
data = request.json
|
||||
if data:
|
||||
cbpi.sensor.action(id, method, **data)
|
||||
else:
|
||||
cbpi.sensor.action(id, method)
|
||||
return ('', 204)
|
||||
|
||||
def _post_post_callback(self, m):
|
||||
@@ -51,17 +55,19 @@ class SensorView(BaseView):
|
||||
@cbpi.addon.core.initializer(order=1000)
|
||||
def init(cbpi):
|
||||
|
||||
SensorView.register(cbpi._app, route_base='/api/sensor')
|
||||
SensorView.register(cbpi.web, route_base='/api/sensor')
|
||||
SensorView.init_cache()
|
||||
|
||||
|
||||
#@cbpi.backgroundtask(key="read_passiv_sensor", interval=5)
|
||||
#@cbpi.addon.core.backgroundtask(key="read_passiv_sensor", interval=5)
|
||||
def read_passive_sensor(api):
|
||||
"""
|
||||
background process that reads all passive sensors in interval of 1 second
|
||||
:return: None
|
||||
|
||||
"""
|
||||
for key, value in cbpi.cache.get("sensors").iteritems():
|
||||
if value.mode == "P":
|
||||
value.instance.read()
|
||||
|
||||
|
||||
#for key, value in cbpi.cache.get("sensors").iteritems():
|
||||
# if value.mode == "P":
|
||||
# value.instance.read()
|
||||
|
||||
@@ -3,12 +3,12 @@ from flask import json, request
|
||||
from flask_classy import route
|
||||
|
||||
from modules.core.db import DBModel
|
||||
from modules.core.baseview import BaseView
|
||||
from modules.core.core import cbpi
|
||||
from modules.core.baseview import RestApi
|
||||
from modules import cbpi
|
||||
from modules.database.dbmodel import Step
|
||||
|
||||
|
||||
class StepView(BaseView):
|
||||
class StepView(RestApi):
|
||||
model = Step
|
||||
def _pre_post_callback(self, data):
|
||||
order = self.model.get_max_order()
|
||||
@@ -43,6 +43,7 @@ class StepView(BaseView):
|
||||
"""
|
||||
self.model.delete_all()
|
||||
self.api.emit("ALL_BREWING_STEPS_DELETED")
|
||||
self.api.set_config_parameter("brew_name", "")
|
||||
cbpi.ws_emit("UPDATE_ALL_STEPS", self.model.get_all())
|
||||
return ('', 204)
|
||||
|
||||
@@ -206,24 +207,25 @@ def init_after_startup():
|
||||
@cbpi.addon.core.initializer(order=2000)
|
||||
def init(cbpi):
|
||||
|
||||
StepView.register(cbpi._app, route_base='/api/step')
|
||||
StepView.register(cbpi.web, route_base='/api/step')
|
||||
|
||||
def get_all():
|
||||
with cbpi._app.app_context():
|
||||
with cbpi.web.app_context():
|
||||
return Step.get_all()
|
||||
|
||||
with cbpi._app.app_context():
|
||||
with cbpi.web.app_context():
|
||||
init_after_startup()
|
||||
|
||||
cbpi.add_cache_callback("steps", get_all)
|
||||
|
||||
@cbpi.addon.core.backgroundjob(key="step_task", interval=0.1)
|
||||
@cbpi.addon.core.backgroundtask(key="step_task", interval=0.1)
|
||||
def execute_step(api):
|
||||
'''
|
||||
Background job which executes the step
|
||||
:return:
|
||||
'''
|
||||
with cbpi._app.app_context():
|
||||
with cbpi.web.app_context():
|
||||
|
||||
step = cbpi.cache.get("active_step")
|
||||
if step is not None:
|
||||
step.execute()
|
||||
|
||||
@@ -5,7 +5,7 @@ from flask import json, url_for, Response, request
|
||||
from flask_classy import FlaskView, route
|
||||
from flask_login import login_required, current_user
|
||||
from git import Repo, Git
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
import pprint
|
||||
import time
|
||||
|
||||
@@ -190,4 +190,4 @@ class SystemView(FlaskView):
|
||||
def init(cbpi):
|
||||
|
||||
SystemView.api = cbpi
|
||||
SystemView.register(cbpi._app, route_base='/api/system')
|
||||
SystemView.register(cbpi.web, route_base='/api/system')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from flask import Blueprint,render_template
|
||||
|
||||
from modules.core.core import cbpi
|
||||
from modules import cbpi
|
||||
|
||||
react = Blueprint('ui', __name__, template_folder='templates', static_folder='static')
|
||||
|
||||
@cbpi.addon.core.initializer(order=10)
|
||||
def init(cbpi):
|
||||
cbpi._app.register_blueprint(react, url_prefix='/ui')
|
||||
cbpi.web.register_blueprint(react, url_prefix='/ui')
|
||||
|
||||
|
||||
@react.route('/', methods=["GET"])
|
||||
@@ -22,7 +22,7 @@ def index():
|
||||
|
||||
|
||||
|
||||
@cbpi._app.errorhandler(404)
|
||||
@cbpi.web.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 941 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 313 KiB |
Vendored
+7713
-2
File diff suppressed because one or more lines are too long
+45
-44
File diff suppressed because one or more lines are too long
@@ -10,11 +10,17 @@
|
||||
<link rel="stylesheet" href="static/bootstrap.dark.css">
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
html {
|
||||
background-image: url('static/bg.png');
|
||||
}
|
||||
</style>
|
||||
<title>CraftBeerPi 3.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" ></div>
|
||||
HALLO
|
||||
<script src="static/bundle.js" type="text/javascript"></script>
|
||||
|
||||
|
||||
|
||||
@@ -7,18 +7,13 @@
|
||||
|
||||
<link rel="stylesheet" href="static/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="static/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="static/bootstrap.dark.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Dosis:200,500" rel="stylesheet">
|
||||
<style>
|
||||
|
||||
body, h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Dosis', sans-serif;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link rel="stylesheet" href="static/theme.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Dosis:200,500" rel="stylesheet"/>
|
||||
|
||||
|
||||
<title>CraftBeerPi 3.1</title>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" ></div>
|
||||
|
||||
Executable → Regular
@@ -1,10 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from modules.core.core import *
|
||||
|
||||
cbpi = CraftBeerPI()
|
||||
|
||||
addon = cbpi.addon
|
||||
from modules import cbpi
|
||||
|
||||
|
||||
from modules.core.db_migrate import *
|
||||
@@ -29,5 +27,6 @@ from modules.base_plugins.steps import *
|
||||
from modules.example_plugins.WebViewJquery import *
|
||||
from modules.example_plugins.WebViewReactJs import *
|
||||
from modules.example_plugins.swagger import *
|
||||
from modules.recipe_book import *
|
||||
|
||||
cbpi.run()
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<RECIPES>
|
||||
<RECIPE>
|
||||
<NAME>Pale Ale</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>All Grain</TYPE>
|
||||
<BREWER></BREWER>
|
||||
<DISPLAY_BATCH_SIZE>6.5 gal</DISPLAY_BATCH_SIZE>
|
||||
<DISPLAY_BOIL_SIZE>7.5 gal</DISPLAY_BOIL_SIZE>
|
||||
<BATCH_SIZE>24.60517657</BATCH_SIZE>
|
||||
<BOIL_SIZE>28.39058835</BOIL_SIZE>
|
||||
<BOIL_TIME>60</BOIL_TIME>
|
||||
<EFFICIENCY>63</EFFICIENCY>
|
||||
<NOTES>
|
||||
</NOTES>
|
||||
<PRIMARY_TEMP>20</PRIMARY_TEMP>
|
||||
<EST_COLOR>8.75</EST_COLOR>
|
||||
<IBU>62.22</IBU>
|
||||
<IBU_METHOD>Tinseth</IBU_METHOD>
|
||||
<EST_ABV>4.88</EST_ABV>
|
||||
<EST_OG>1.047 sg</EST_OG>
|
||||
<EST_FG>1.01 sg</EST_FG>
|
||||
<OG>1.047</OG>
|
||||
<FG>1.01</FG>
|
||||
<PRIMING_SUGAR_NAME></PRIMING_SUGAR_NAME>
|
||||
<CARBONATION_USED></CARBONATION_USED>
|
||||
<BF_PRIMING_METHOD></BF_PRIMING_METHOD>
|
||||
<BF_PRIMING_AMOUNT></BF_PRIMING_AMOUNT>
|
||||
<BF_CO2_LEVEL></BF_CO2_LEVEL>
|
||||
<BF_CO2_UNIT>Volumes</BF_CO2_UNIT>
|
||||
<URL></URL>
|
||||
<BATCH_SIZE_MODE>f</BATCH_SIZE_MODE>
|
||||
<YEAST_STARTER>false</YEAST_STARTER>
|
||||
<NO_CHILL_EXTRA_MINUTES></NO_CHILL_EXTRA_MINUTES>
|
||||
<STARTING_MASH_THICKNESS>3.33880656</STARTING_MASH_THICKNESS>
|
||||
<PITCH_RATE>0.35</PITCH_RATE>
|
||||
<FERMENTABLES>
|
||||
<FERMENTABLE>
|
||||
<NAME>Pale 2-Row</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Grain</TYPE>
|
||||
<AMOUNT>4.08233133</AMOUNT>
|
||||
<YIELD>80.43</YIELD>
|
||||
<COLOR>1.8</COLOR>
|
||||
<ADD_AFTER_BOIL>false</ADD_AFTER_BOIL>
|
||||
<ORIGIN>American</ORIGIN>
|
||||
</FERMENTABLE>
|
||||
<FERMENTABLE>
|
||||
<NAME>Caramel / Crystal 60L</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Grain</TYPE>
|
||||
<AMOUNT>0.45359237</AMOUNT>
|
||||
<YIELD>73.91</YIELD>
|
||||
<COLOR>60</COLOR>
|
||||
<ADD_AFTER_BOIL>false</ADD_AFTER_BOIL>
|
||||
<ORIGIN>American</ORIGIN>
|
||||
</FERMENTABLE>
|
||||
<FERMENTABLE>
|
||||
<NAME>Rye</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Grain</TYPE>
|
||||
<AMOUNT>1.0205828325</AMOUNT>
|
||||
<YIELD>82.61</YIELD>
|
||||
<COLOR>3.5</COLOR>
|
||||
<ADD_AFTER_BOIL>false</ADD_AFTER_BOIL>
|
||||
<ORIGIN>American</ORIGIN>
|
||||
</FERMENTABLE>
|
||||
<FERMENTABLE>
|
||||
<NAME>Flaked Wheat</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Adjunct</TYPE>
|
||||
<AMOUNT>0.226796185</AMOUNT>
|
||||
<YIELD>73.91</YIELD>
|
||||
<COLOR>2</COLOR>
|
||||
<ADD_AFTER_BOIL>false</ADD_AFTER_BOIL>
|
||||
<ORIGIN></ORIGIN>
|
||||
</FERMENTABLE>
|
||||
<FERMENTABLE>
|
||||
<NAME>Carapils</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Grain</TYPE>
|
||||
<AMOUNT>0.226796185</AMOUNT>
|
||||
<YIELD>76.09</YIELD>
|
||||
<COLOR>1.3</COLOR>
|
||||
<ADD_AFTER_BOIL>false</ADD_AFTER_BOIL>
|
||||
<ORIGIN>German</ORIGIN>
|
||||
</FERMENTABLE>
|
||||
</FERMENTABLES>
|
||||
<HOPS>
|
||||
<HOP>
|
||||
<NAME>El Dorado</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<ALPHA>15.7</ALPHA>
|
||||
<AMOUNT>0.0283495231</AMOUNT>
|
||||
<USE>First Wort</USE>
|
||||
<USER_HOP_USE>First Wort</USER_HOP_USE>
|
||||
<TIME>0</TIME>
|
||||
<FORM>Leaf</FORM>
|
||||
</HOP>
|
||||
<HOP>
|
||||
<NAME>El Dorado</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<ALPHA>15.7</ALPHA>
|
||||
<AMOUNT>0.0283495231</AMOUNT>
|
||||
<USE>Boil</USE>
|
||||
<USER_HOP_USE>Boil</USER_HOP_USE>
|
||||
<TIME>30</TIME>
|
||||
<FORM>Leaf</FORM>
|
||||
</HOP>
|
||||
<HOP>
|
||||
<NAME>El Dorado</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<ALPHA>15.7</ALPHA>
|
||||
<AMOUNT>0.0283495231</AMOUNT>
|
||||
<USE>Dry Hop</USE>
|
||||
<USER_HOP_USE>Dry Hop</USER_HOP_USE>
|
||||
<TIME>14400</TIME>
|
||||
<FORM>Leaf</FORM>
|
||||
</HOP>
|
||||
</HOPS>
|
||||
<MISCS/>
|
||||
<MASH>
|
||||
<NAME>Mash Steps</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<GRAIN_TEMP>20</GRAIN_TEMP>
|
||||
<MASH_STEPS>
|
||||
<MASH_STEP>
|
||||
<NAME>STEP1</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Infusion</TYPE>
|
||||
<STEP_TIME>60</STEP_TIME>
|
||||
<INFUSE_AMOUNT>1.5141647136</INFUSE_AMOUNT>
|
||||
<STEP_TEMP>65.555555555556</STEP_TEMP>
|
||||
</MASH_STEP>
|
||||
</MASH_STEPS>
|
||||
</MASH>
|
||||
<YEASTS>
|
||||
<YEAST>
|
||||
<NAME>California Ale Yeast WLP001</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<TYPE>Ale</TYPE>
|
||||
<FORM>Liquid</FORM>
|
||||
<AMOUNT>0.1</AMOUNT>
|
||||
<PRODUCT_ID>WLP001</PRODUCT_ID>
|
||||
<LABORATORY>White Labs</LABORATORY>
|
||||
<ATTENUATION>76.5</ATTENUATION>
|
||||
<FLOCCULATION>Medium</FLOCCULATION>
|
||||
<MIN_TEMPERATURE>20</MIN_TEMPERATURE>
|
||||
<MAX_TEMPERATURE>22.777777777778</MAX_TEMPERATURE>
|
||||
</YEAST>
|
||||
</YEASTS>
|
||||
<WATERS/>
|
||||
<STYLE>
|
||||
<NAME>English IPA</NAME>
|
||||
<VERSION>1</VERSION>
|
||||
<CATEGORY>India Pale Ale (IPA)</CATEGORY>
|
||||
<CATEGORY_NUMBER>14</CATEGORY_NUMBER>
|
||||
<STYLE_LETTER>A</STYLE_LETTER>
|
||||
<STYLE_GUIDE>BJCP</STYLE_GUIDE>
|
||||
<TYPE>Ale</TYPE>
|
||||
<OG_MIN>1.05</OG_MIN>
|
||||
<OG_MAX>1.075</OG_MAX>
|
||||
<FG_MIN>1.01</FG_MIN>
|
||||
<FG_MAX>1.018</FG_MAX>
|
||||
<ABV_MIN>5</ABV_MIN>
|
||||
<ABV_MAX>7.5</ABV_MAX>
|
||||
<IBU_MIN>40</IBU_MIN>
|
||||
<IBU_MAX>60</IBU_MAX>
|
||||
<COLOR_MIN>8</COLOR_MIN>
|
||||
<COLOR_MAX>14</COLOR_MAX>
|
||||
</STYLE>
|
||||
</RECIPE>
|
||||
</RECIPES>
|
||||
Reference in New Issue
Block a user