Преглед на файлове

- Refactoring

- Action Button with Parameter
- Actor Action with Parameter
- Sensor Action with Parameter
core_refactoring
Manuel83 преди 8 години
родител
ревизия
9b7908c9c5
променени са 41 файла, в които са добавени 8098 реда и са изтрити 271 реда
  1. +4
    -0
      modules/__init__.py
  2. +7
    -4
      modules/action/__init__.py
  3. +14
    -7
      modules/actor/__init__.py
  4. +31
    -9
      modules/base_plugins/actor.py
  5. +33
    -13
      modules/base_plugins/sensor.py
  6. +7
    -4
      modules/base_plugins/steps.py
  7. +5
    -5
      modules/buzzer/__init__.py
  8. +5
    -5
      modules/config/__init__.py
  9. +63
    -27
      modules/core/baseapi.py
  10. +7
    -6
      modules/core/basetypes.py
  11. +4
    -4
      modules/core/baseview.py
  12. +53
    -53
      modules/core/core.py
  13. +5
    -6
      modules/core/db_migrate.py
  14. +2
    -0
      modules/core/proptypes.py
  15. +0
    -0
      modules/core/step.py
  16. +3
    -3
      modules/example_plugins/WebViewJquery/__init__.py
  17. +3
    -3
      modules/example_plugins/WebViewReactJs/__init__.py
  18. +4
    -4
      modules/example_plugins/swagger/__init__.py
  19. +10
    -11
      modules/fermenter/__init__.py
  20. +5
    -5
      modules/kettle/__init__.py
  21. +24
    -7
      modules/login/__init__.py
  22. +2
    -2
      modules/logs/__init__.py
  23. +2
    -2
      modules/notification/__init__.py
  24. +3
    -3
      modules/plugin/__init__.py
  25. +4
    -4
      modules/recipe_book/__init__.py
  26. +3
    -3
      modules/recipe_import/beerxml.py
  27. +3
    -3
      modules/recipe_import/kbh.py
  28. +2
    -2
      modules/recipe_import/restapi.py
  29. +16
    -10
      modules/sensor/__init__.py
  30. +9
    -8
      modules/step/__init__.py
  31. +2
    -2
      modules/system/__init__.py
  32. +3
    -3
      modules/ui/__init__.py
  33. Двоични данни
      modules/ui/static/bg.jpg
  34. Двоични данни
      modules/ui/static/bg1.jpg
  35. Двоични данни
      modules/ui/static/bg2.jpg
  36. +7713
    -2
      modules/ui/static/bootstrap.dark.css
  37. +37
    -36
      modules/ui/static/bundle.js
  38. +6
    -0
      modules/ui/static/index.html
  39. +3
    -8
      modules/ui/templates/index.html
  40. +0
    -4
      name.py
  41. +1
    -3
      run.py

+ 4
- 0
modules/__init__.py Целия файл

@@ -0,0 +1,4 @@
from modules.core.core import CraftBeerPI, Addon

cbpi = CraftBeerPI()
cbpi.addon = Addon(cbpi)

+ 7
- 4
modules/action/__init__.py Целия файл

@@ -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')

+ 14
- 7
modules/actor/__init__.py Целия файл

@@ -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()

+ 31
- 9
modules/base_plugins/actor.py Целия файл

@@ -1,15 +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):
print "WOOHOO"
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):
'''
@@ -17,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()
@@ -29,7 +50,7 @@ class MyController(KettleController):

def run(self):
while self.is_running():
print "HALLO"

self.sleep(1)

@@ -49,6 +70,7 @@ def init(cbpi):

class MyBuzzer(Buzzer):
def beep(self):
print "BEEEEEP"
pass

cbpi.buzzer = MyBuzzer()

+ 33
- 13
modules/base_plugins/sensor.py Целия файл

@@ -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))


self.api.notify(headline="WOHOO", message="HALLO")

def execute(self):
while True:
@@ -34,14 +40,28 @@ class Dummy(Sensor):
pass
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):

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")


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:
for item in test:

if item.endswith(".log"):
os.remove(join(dir, item))
cbpi.notify(headline="Logs Deleted",message="All Logs Cleared")
if item.endswith(".log"):
os.remove(join(dir, item))
cbpi.notify(headline="Logs Deleted", message="All Logs Cleared")

+ 7
- 4
modules/base_plugins/steps.py Целия файл

@@ -1,5 +1,5 @@
from modules.core.basetypes import Step
from modules.core.core import cbpi
from modules import cbpi
from modules.core.proptypes import Property
import time

@@ -18,7 +18,7 @@ class Dummy(Step):
time = Property.Text(label="Text", configurable=True, description="WOHOOO")

def execute(self):
#print self.text
pass

def reset(self):
@@ -36,8 +36,8 @@ 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")
kettle = Property.Kettle("Kettle", description="Kettle in which the mashing takes place")
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):
@@ -79,6 +79,7 @@ class MashStep(Step):

# 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()

@@ -243,3 +244,5 @@ class BoilStep(Step):
if self.is_timer_finished() == True:
self.notify("Boil Step Completed!", "Starting the next step", timeout=None)
self.next()



+ 5
- 5
modules/buzzer/__init__.py Целия файл

@@ -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):


+ 5
- 5
modules/config/__init__.py Целия файл

@@ -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()

+ 63
- 27
modules/core/baseapi.py Целия файл

@@ -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()

+ 7
- 6
modules/core/basetypes.py Целия файл

@@ -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
@@ -271,7 +272,6 @@ class Step(Base, Timer):
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)
@@ -281,7 +281,6 @@ class Step(Base, Timer):
self.api.notify("Faild to set Target Temp", "", type="warning")
def get_kettle_temp(self, id=None):
id = int(id)
if id is None:
@@ -294,6 +293,8 @@ class Step(Base, Timer):
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:


+ 4
- 4
modules/core/baseview.py Целия файл

@@ -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] = []


+ 53
- 53
modules/core/core.py Целия файл

@@ -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:
@@ -373,7 +377,7 @@ class CraftBeerPI(object):
def set_config_parameter(self, name, value):
from modules.config import Config
with self._app.app_context():
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)
@@ -381,7 +385,6 @@ class CraftBeerPI(object):
def emit(self, key, **kwargs):
if self.eventbus.get(key) is not None:
for value in self.eventbus[key]:
if value["async"] is False:
@@ -400,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

+ 5
- 6
modules/core/db_migrate.py Целия файл

@@ -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


+ 2
- 0
modules/core/proptypes.py Целия файл

@@ -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

+ 0
- 0
modules/core/step.py Целия файл


+ 3
- 3
modules/example_plugins/WebViewJquery/__init__.py Целия файл

@@ -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')

+ 3
- 3
modules/example_plugins/WebViewReactJs/__init__.py Целия файл

@@ -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')

+ 4
- 4
modules/example_plugins/swagger/__init__.py Целия файл

@@ -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')

+ 10
- 11
modules/fermenter/__init__.py Целия файл

@@ -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"

@@ -212,10 +212,9 @@ class FermenterView(BaseView):
cbpi.ws_emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])

def check_step(self):
print "CHECK STEP"
print cbpi.cache["fermenter_task"]

for key, value in cbpi.cache["fermenter_task"].iteritems():
print value
try:
fermenter = self.get_fermenter(key)
current_temp = current_temp = cbpi.sensor.get_value(int(fermenter.sensor))
@@ -236,11 +235,11 @@ class FermenterView(BaseView):
else:
pass
except Exception as e:
print 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
@@ -253,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()


@@ -268,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()

+ 5
- 5
modules/kettle/__init__.py Целия файл

@@ -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"
@@ -228,7 +228,7 @@ def set_target_temp(id, temp):
'''
KettleView().postTargetTemp(id,temp)
@cbpi.addon.core.backgroundjob(key="read_target_temps", interval=5)
@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
@@ -241,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()

+ 24
- 7
modules/login/__init__.py Целия файл

@@ -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,15 +31,29 @@ 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.user_loader
def user_loader(user):
@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":
@@ -53,4 +68,6 @@ def log(cbpi):

@cbpi._login_manager.unauthorized_handler
def unauthorized_handler():


return ('Please login',401)

+ 2
- 2
modules/logs/__init__.py Целия файл

@@ -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')

+ 2
- 2
modules/notification/__init__.py Целия файл

@@ -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')

+ 3
- 3
modules/plugin/__init__.py Целия файл

@@ -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,7 +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():
print key
value["installed"] = os.path.isdir("./plugins/%s/" % (key))
return json.dumps(cbpi.cache["plugins"])
@@ -136,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')

+ 4
- 4
modules/recipe_book/__init__.py Целия файл

@@ -7,8 +7,8 @@ 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 BaseView
from modules.core.core import cbpi
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
@@ -59,7 +59,7 @@ class RecipeBook(FlaskView):
description: Log file downloaded
"""
file = "%s.json" % name
print file
if not self.check_filename(file):
return ('File Not Found111', 404)
return send_from_directory('../../recipes', file, as_attachment=True, attachment_filename=file)
@@ -143,4 +143,4 @@ class RecipeBook(FlaskView):
@cbpi.addon.core.initializer(order=2000)
def init(cbpi):
RecipeBook.api = cbpi
RecipeBook.register(cbpi._app, route_base='/api/recipebook')
RecipeBook.register(cbpi.web, route_base='/api/recipebook')

+ 3
- 3
modules/recipe_import/beerxml.py Целия файл

@@ -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
@@ -60,7 +60,7 @@ class BeerXMLImport(FlaskView):
return ('', 204)
return ('', 404)
except Exception as e:
print e
self.api.logger.error(e)
self.api.notify(headline="Upload Failed", message="Failed to upload Beer xml", type="danger")
return ('', 500)

@@ -143,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')

+ 3
- 3
modules/recipe_import/kbh.py Целия файл

@@ -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
- 2
modules/recipe_import/restapi.py Целия файл

@@ -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
modules/sensor/__init__.py Целия файл

@@ -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()

+ 9
- 8
modules/step/__init__.py Целия файл

@@ -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()
@@ -207,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()


+ 2
- 2
modules/system/__init__.py Целия файл

@@ -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')

+ 3
- 3
modules/ui/__init__.py Целия файл

@@ -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



Двоични данни
modules/ui/static/bg.jpg Целия файл

Before After
Width: 1023  |  Height: 682  |  Size: 149KB

Двоични данни
modules/ui/static/bg1.jpg Целия файл

Before After
Width: 2506  |  Height: 2000  |  Size: 941KB

Двоични данни
modules/ui/static/bg2.jpg Целия файл

Before After
Width: 1300  |  Height: 866  |  Size: 313KB

+ 7713
- 2
modules/ui/static/bootstrap.dark.css
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 37
- 36
modules/ui/static/bundle.js
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 6
- 0
modules/ui/static/index.html Целия файл

@@ -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>




+ 3
- 8
modules/ui/templates/index.html Целия файл

@@ -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>
<link rel="stylesheet" href="static/theme.css">
<link href="https://fonts.googleapis.com/css?family=Dosis:200,500" rel="stylesheet"/>

body, h1, h2, h3, h4, h5, h6 {
font-family: 'Dosis', sans-serif;
}

</style>
<title>CraftBeerPi 3.1</title>


<title>CraftBeerPi 3.1</title>
</head>
<body>
<div id="root" ></div>


+ 0
- 4
name.py Целия файл

@@ -1,4 +0,0 @@
import re

recipe_name = "Manuel 8881881 18181 "
print re.match("^[\sA-Za-z0-9_-]*$", recipe_name)

+ 1
- 3
run.py Целия файл

@@ -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 *


Loading…
Отказ
Запис