Переглянути джерело

-Ui Changes

-Brew Steps added
-Clean AddOn Dialog
tags/3.1_alpha
Manuel83 8 роки тому
джерело
коміт
00739ea38f
21 змінених файлів з 297 додано та 75 видалено
  1. +4
    -0
      .gitignore
  2. +3
    -0
      config/schema.sql
  3. +1
    -0
      config/version.yaml
  4. +3
    -0
      modules/__init__.py
  5. +4
    -2
      modules/app_config.py
  6. +127
    -0
      modules/base_plugins/brew_steps/__init__.py
  7. +18
    -2
      modules/base_plugins/dummy_temp/__init__.py
  8. +11
    -0
      modules/base_plugins/gpio_actor/__init__.py
  9. +0
    -2
      modules/buzzer/__init__.py
  10. +1
    -1
      modules/config/__init__.py
  11. +17
    -7
      modules/core/core.py
  12. +4
    -2
      modules/core/db.py
  13. +3
    -0
      modules/core/hardware.py
  14. +2
    -1
      modules/core/props.py
  15. +3
    -4
      modules/notification/__init__.py
  16. +24
    -6
      modules/steps/__init__.py
  17. +2
    -0
      modules/ui/endpoints.py
  18. +16
    -0
      modules/ui/src/App.css
  19. +5
    -0
      modules/ui/src/index.css
  20. +47
    -48
      modules/ui/static/bundle.js
  21. +2
    -0
      modules/ui/static/index.html

+ 4
- 0
.gitignore Переглянути файл

@@ -8,3 +8,7 @@ craftbeerpi.db
modules/plugins/*
!modules/plugins/__init__.py
*.pyc
*.js
modules/ui/package.json

modules/ui/.babelrc

+ 3
- 0
config/schema.sql Переглянути файл

@@ -86,6 +86,9 @@ INSERT INTO config VALUES ('unit', 'C', 'select', 'Temperature Unit', '["C","F"]
INSERT INTO config VALUES ('brewery_name', 'My Home Brewery', 'text', 'Your brewery name', NULL );
INSERT INTO config VALUES ('buzzer', 16, 'select', 'Buzzer GPIO', '[16,17,18,19,20]');
INSERT INTO config VALUES ('setup', 'YES', 'select', 'Show the Setup dialog', '["YES","NO"]');
INSERT INTO config VALUES ('brew_name', '', 'text', 'Brew Name', NULL);
INSERT INTO config VALUES ('donation_notification', 'YES', 'select', 'Disable Donation Notification', '["YES","NO"]');


CREATE TABLE actor
(


+ 1
- 0
config/version.yaml Переглянути файл

@@ -0,0 +1 @@
3.0.1

+ 3
- 0
modules/__init__.py Переглянути файл

@@ -63,3 +63,6 @@ cbpi.run_background_processes()
app.logger.info("##########################################")
app.logger.info("### STARTUP COMPLETE")
app.logger.info("##########################################")

+ 4
- 2
modules/app_config.py Переглянути файл

@@ -15,7 +15,9 @@ from modules.core.db import DBModel

app = Flask(__name__)

logging.basicConfig(filename='./logs/app.log',level=logging.INFO)
FORMAT = '%(asctime)-15s - %(levelname)s - %(message)s'

logging.basicConfig(filename='./logs/app.log',level=logging.INFO, format=FORMAT)
app.config['SECRET_KEY'] = 'craftbeerpi'
app.config['UPLOAD_FOLDER'] = './upload'

@@ -45,7 +47,7 @@ class ComplexEncoder(json.JSONEncoder):
return None

app.json_encoder = ComplexEncoder
socketio = SocketIO(app, json=json)
socketio = SocketIO(app, json=json, logging=False)
cbpi = CraftBeerPi(app, socketio)

app.logger.info("##########################################")


+ 127
- 0
modules/base_plugins/brew_steps/__init__.py Переглянути файл

@@ -7,6 +7,7 @@ from modules.core.step import StepBase
from modules import cbpi



@cbpi.step
class MashStep(StepBase):
'''
@@ -69,6 +70,10 @@ class MashInStep(StepBase):
kettle = StepProperty.Kettle("Kettle")
s = False

@cbpi.action("Change Power")
def change_power(self):
self.actor_power(1, 50)

def init(self):
'''
Initialize Step. This method is called once at the beginning of the step
@@ -93,3 +98,125 @@ class MashInStep(StepBase):



@cbpi.step
class ChilStep(StepBase):

timer = Property.Number("Timer in Minutes", configurable=True, default_value=0)

@cbpi.action("Stat 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.step
class PumpStep(StepBase):

pump = StepProperty.Actor("Pump")
timer = Property.Number("Timer in Minutes", configurable=True, default_value=0)

@cbpi.action("Stat 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.step
class BoilStep(StepBase):
'''
Just put the decorator @cbpi.step on top of a method
'''
# Properties
temp = Property.Number("Temperature", configurable=True, default_value=100)
kettle = StepProperty.Kettle("Kettle")
timer = Property.Number("Timer in Minutes", configurable=True, default_value=90)
hop_1 = Property.Number("Hop 1 Addition", configurable=True)
hop_1_added = Property.Number("",default_value=None)

hop_2 = Property.Number("Hop 2 Addition", configurable=True)
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)

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.action("Start Timer Now")
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) >= int(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)
# Check if timer finished and go to next step
if self.is_timer_finished() == True:
self.next()

+ 18
- 2
modules/base_plugins/dummy_temp/__init__.py Переглянути файл

@@ -10,21 +10,37 @@ from modules.core.props import Property

@cbpi.sensor
class DummyTempSensor(SensorActive):

temp = Property.Number("Temperature", configurable=True, default_value=5)

def get_unit(self):
'''
:return: Unit of the sensor as string. Should not be longer than 3 characters
'''
return "°C" if self.get_config_parameter("unit", "C") == "C" else "°F"

def stop(self):
'''
Stop the sensor. Is called when the sensor config is updated or the sensor is deleted
:return:
'''
pass

def execute(self):
'''
Active sensor has to handle his own loop
:return:
'''
while self.is_running():

self.data_received(self.temp)
socketio.sleep(5)


@classmethod
def init_global(cls):
'''
Called one at the startup for all sensors
:return:
'''





+ 11
- 0
modules/base_plugins/gpio_actor/__init__.py Переглянути файл

@@ -57,6 +57,11 @@ class GPIOPWM(ActorBase):
self.p.start(int(self.power))

def set_power(self, power):
'''
Optional: Set the power of your actor
:param power: int value between 0 - 100
:return:
'''
if power is not None:
self.power = int(power)
self.p.ChangeDutyCycle(self.power)
@@ -87,7 +92,13 @@ class RelayBoard(ActorBase):
class Dummy(ActorBase):

def on(self, power=100):
'''
Code to switch on the actor
:param power: int value between 0 - 100
:return:
'''
print "ON"

def off(self):
print "OFF"


+ 0
- 2
modules/buzzer/__init__.py Переглянути файл

@@ -43,8 +43,6 @@ class Buzzer(object):

@cbpi.initalizer(order=0)
def init(cbpi):
print "INIT BUZZER"
cbpi.app.logger.info("INIT BUZZER")
gpio = cbpi.get_config_parameter("buzzer", 16)
cbpi.buzzer = Buzzer(gpio)
cbpi.beep()


+ 1
- 1
modules/config/__init__.py Переглянути файл

@@ -25,6 +25,7 @@ class ConfigView(BaseView):

if self.api.cache.get(self.cache_key) is not None:
#self.pre_post_callback(self.api.cache.get(self.cache_key)[name])
print self.api.cache.get(self.cache_key)[name]
self.api.cache.get(self.cache_key)[name].__dict__.update(**update_data)
m = self.model.update(**self.api.cache.get(self.cache_key)[name].__dict__)
self.post_put_callback(self.api.cache.get(self.cache_key)[name])
@@ -51,7 +52,6 @@ class ConfigView(BaseView):
cls.post_init_callback(value)
cls.api.cache[cls.cache_key][value.name] = value


@cbpi.initalizer(order=1)
def init(cbpi):
print "INITIALIZE CONFIG MODULE"


+ 17
- 7
modules/core/core.py Переглянути файл

@@ -1,3 +1,4 @@
import inspect
import pprint

import sqlite3
@@ -28,6 +29,7 @@ class ActorAPI(object):
self.app.logger.info("Init Actors")
t = self.cache.get("actor_types")
for key, value in t.iteritems():
value.get("class").api = self
value.get("class").init_global()

for key in self.cache.get("actors"):
@@ -73,12 +75,8 @@ class ActorAPI(object):

if actor.state == 0:
return


actor.instance.off()
actor.state = 0


self.emit("SWITCH_ACTOR", actor)

class SensorAPI(object):
@@ -231,7 +229,6 @@ class CraftBeerPi(ActorAPI, SensorAPI):
self.emit("NOTIFY", msg)

def beep(self):

if self.buzzer is not None:
self.buzzer.beep()

@@ -286,6 +283,15 @@ class CraftBeerPi(ActorAPI, SensorAPI):
def actor(self, cls):
return self.__parseProps("actor_types", cls)

def actor2(self, description="", power=True, **options):

def decorator(f):
print f()
print options
print description
return f
return decorator

def sensor(self, cls):
return self.__parseProps("sensor_types", cls)

@@ -320,9 +326,13 @@ class CraftBeerPi(ActorAPI, SensorAPI):
for m in members:
if isinstance(tmpObj.__getattribute__(m), StepProperty.Number):
t = tmpObj.__getattribute__(m)
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable})
print t.__dict__
#self.cache[key][name]["properties"].append(t.__dict__)
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable, "default_value": t.default_value})
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Text):
t = tmpObj.__getattribute__(m)
print t.__dict__
#self.cache[key][name]["properties"].append(t.__dict__)
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "text", "configurable": t.configurable})
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Select):
t = tmpObj.__getattribute__(m)
@@ -420,7 +430,7 @@ class CraftBeerPi(ActorAPI, SensorAPI):
self.app.logger.info("Invoke Init")
self.cache["init"] = sorted(self.cache["init"], key=lambda k: k['order'])
for i in self.cache.get("init"):
self.app.logger.info("-> %s " % i.get("function").__name__)
self.app.logger.info("INITIALIZER - METHOD %s PAHT %s: " % (i.get("function").__name__, str(inspect.getmodule(i.get("function")).__file__) ))
i.get("function")(self)




+ 4
- 2
modules/core/db.py Переглянути файл

@@ -42,13 +42,15 @@ class DBModel(object):
def get_all(cls):
cur = get_db().cursor()
if cls.__order_by__ is not None:
cur.execute("SELECT * FROM %s ORDER BY '%s'" % (cls.__table_name__,cls.__order_by__))

cur.execute("SELECT * FROM %s ORDER BY %s.'%s'" % (cls.__table_name__,cls.__table_name__,cls.__order_by__))
else:
cur.execute("SELECT * FROM %s" % cls.__table_name__)

if cls.__as_array__ is True:
result = []
for r in cur.fetchall():

result.append( cls(r))
else:
result = {}
@@ -104,7 +106,7 @@ class DBModel(object):
else:
data = data + (kwargs.get(f),)

print query, data
cur.execute(query, data)
get_db().commit()
i = cur.lastrowid


+ 3
- 0
modules/core/hardware.py Переглянути файл

@@ -10,6 +10,9 @@ class Base(object):
def get_config_parameter(self, key, default_value):
return self.api.get_config_parameter(key, default_value)

def sleep(self, seconds):
self.api.socketio.sleep(seconds)

def init(self):
print "INIT BASE"



+ 2
- 1
modules/core/props.py Переглянути файл

@@ -9,10 +9,11 @@ class Property(object):
self.options = options

class Number(PropertyType):
def __init__(self, label, configurable=False, default_value=0, unit=""):
def __init__(self, label, configurable=False, default_value=None, unit=""):
PropertyType.__init__(self)
self.label = label
self.configurable = configurable
self.default_value = default_value

class Text(PropertyType):
def __init__(self, label, configurable=False, default_value=""):


+ 3
- 4
modules/notification/__init__.py Переглянути файл

@@ -44,9 +44,8 @@ def init(cbpi):
:param app: the flask app
:return: None
"""
print "INITIALIZE MESSAGE MODULE"

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)
if cbpi.get_config_parameter("donation_notification", "YES") == "YES":
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')

+ 24
- 6
modules/steps/__init__.py Переглянути файл

@@ -1,5 +1,5 @@
import time
from flask import json
from flask import json, request
from flask_classy import route

from modules import DBModel, cbpi, get_db
@@ -21,9 +21,9 @@ class Step(DBModel):
return r.get("order")

@classmethod
def get_by_state(cls, state):
def get_by_state(cls, state, order=True):
cur = get_db().cursor()
cur.execute("SELECT * FROM %s WHERE state = ?" % cls.__table_name__, state)
cur.execute("SELECT * FROM %s WHERE state = ? ORDER BY %s.'order'" % (cls.__table_name__,cls.__table_name__,), state)
r = cur.fetchone()
if r is not None:
return cls(r)
@@ -54,6 +54,16 @@ class Step(DBModel):
cur.execute("UPDATE %s SET stepstate = ? WHERE id =?" % cls.__table_name__, (json.dumps(state),id))
get_db().commit()

@classmethod
def sort(cls, new_order):
cur = get_db().cursor()

for e in new_order:

cur.execute("UPDATE %s SET '%s' = ? WHERE id = ?" % (cls.__table_name__, "order"), (e[1], e[0]))
get_db().commit()


class StepView(BaseView):
model = Step
def pre_post_callback(self, data):
@@ -61,6 +71,14 @@ class StepView(BaseView):
data["order"] = 1 if order is None else order + 1
data["state"] = "I"

@route('/sort', methods=["POST"])
def sort_steps(self):

Step.sort(request.json)

cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
return ('', 204)

@route('/', methods=["DELETE"])
def deleteAll(self):
self.model.delete_all()
@@ -185,8 +203,10 @@ def init_after_startup():
if type_cfg is None:
# step type not found. cant restart step
return

print "STEP SATE......", step.stepstate
cfg = step.stepstate.copy()
cfg.update(dict(api=cbpi, id=step.id, timer_end=None, managed_fields=get_manged_fields_as_array(type_cfg)))
cfg.update(dict(api=cbpi, id=step.id, managed_fields=get_manged_fields_as_array(type_cfg)))
instance = type_cfg.get("class")(**cfg)
instance.init()
cbpi.cache["active_step"] = instance
@@ -214,9 +234,7 @@ def execute_step():
step = cbpi.cache.get("active_step")
if step is not None:
step.execute()

if step.is_dirty():

state = {}
for field in step.managed_fields:
state[field] = step.__getattribute__(field)


+ 2
- 0
modules/ui/endpoints.py Переглянути файл

@@ -9,6 +9,8 @@ def init(cbpi):
cbpi.app.register_blueprint(react, url_prefix='/ui')




@react.route('/')
def index():
return react.send_static_file("index.html")


+ 16
- 0
modules/ui/src/App.css Переглянути файл

@@ -0,0 +1,16 @@
.App {

}

.container {
padding-right: 5px;
padding-left: 5px;
margin-right: auto;
margin-left: auto;
}

.container-fluid {
padding-right: 5px;
padding-left: 5px;

}

+ 5
- 0
modules/ui/src/index.css Переглянути файл

@@ -0,0 +1,5 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}

+ 47
- 48
modules/ui/static/bundle.js
Різницю між файлами не показано, бо вона завелика
Переглянути файл


+ 2
- 0
modules/ui/static/index.html Переглянути файл

@@ -13,6 +13,8 @@
<style>




.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
position: relative;
min-height: 1px;


Завантаження…
Відмінити
Зберегти