mirror of
https://github.com/Manuel83/craftbeerpi3
synced 2026-08-09 18:52:40 +02:00
Compare commits
148
Commits
3.0
..
no_wiringpi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea5ee44295 | ||
|
|
16ad8daf44 | ||
|
|
67a103d1f3 | ||
|
|
274f282e11 | ||
|
|
4e432e05bd | ||
|
|
eda209422f | ||
|
|
5b47b94833 | ||
|
|
98409d9895 | ||
|
|
433b1c30f1 | ||
|
|
a477cb83a8 | ||
|
|
a22dcba77c | ||
|
|
c1d94a8f61 | ||
|
|
dd64f4ee16 | ||
|
|
1414b1ed3b | ||
|
|
acd404622b | ||
|
|
d657077570 | ||
|
|
0e6da89cfd | ||
|
|
faa2b9d32c | ||
|
|
83699f1933 | ||
|
|
fea3e85053 | ||
|
|
5efa1df350 | ||
|
|
dd5b5f456a | ||
|
|
e0e3aff929 | ||
|
|
2a02979e37 | ||
|
|
a1740074ac | ||
|
|
1b3042a082 | ||
|
|
b279dcf347 | ||
|
|
211edc1bbc | ||
|
|
b56c10a8a5 | ||
|
|
8d2c5c916a | ||
|
|
d637c08eba | ||
|
|
0447621895 | ||
|
|
03395647ed | ||
|
|
65833b1249 | ||
|
|
25ace8024c | ||
|
|
820061cf09 | ||
|
|
c1c5faa1f3 | ||
|
|
837b906af1 | ||
|
|
0f42153591 | ||
|
|
5fc5684e56 | ||
|
|
dc908268dd | ||
|
|
14e4e6d61a | ||
|
|
bf3fbddbe2 | ||
|
|
590ef4ca92 | ||
|
|
d3bc54001f | ||
|
|
25dead2e4d | ||
|
|
f771289224 | ||
|
|
bf02c08677 | ||
|
|
c765589984 | ||
|
|
ae7e3a2e7b | ||
|
|
3eee952a04 | ||
|
|
88b360a8d7 | ||
|
|
f62f9ba7ce | ||
|
|
909a6aa0ea | ||
|
|
40b7253a6d | ||
|
|
3761713219 | ||
|
|
a96c678dd9 | ||
|
|
e7765a2c4c | ||
|
|
2fe3d1d68e | ||
|
|
1f1f84018d | ||
|
|
f68e0f46ee | ||
|
|
b75926d23e | ||
|
|
aa3420efc8 | ||
|
|
124a03bd0f | ||
|
|
1c4e16e358 | ||
|
|
74b56937e7 | ||
|
|
805b3fae1b | ||
|
|
f8d4015b6e | ||
|
|
01997f741c | ||
|
|
1184107e50 | ||
|
|
ea793996ab | ||
|
|
bbcf0a40bf | ||
|
|
d6d023798a | ||
|
|
518a139b5d | ||
|
|
a97c5f4585 | ||
|
|
9c44d3ff62 | ||
|
|
544e9bb72a | ||
|
|
eee5680ba9 | ||
|
|
42e91de9eb | ||
|
|
3c541e0946 | ||
|
|
4d08b17abd | ||
|
|
be8506d95b | ||
|
|
98fbcaf910 | ||
|
|
b40e82a6bb | ||
|
|
a368213cd1 | ||
|
|
58fd9a659e | ||
|
|
e750fbb70e | ||
|
|
ed9bbed084 | ||
|
|
15d7906850 | ||
|
|
b9b3fee8c9 | ||
|
|
ad02e2490d | ||
|
|
eb715fabb6 | ||
|
|
5d6e32a57f | ||
|
|
85f0c6cbff | ||
|
|
f60658c84e | ||
|
|
b4ddc60c07 | ||
|
|
6fd39a81a9 | ||
|
|
bb6f6f2b36 | ||
|
|
ca9575ac29 | ||
|
|
9a1981fa46 | ||
|
|
46fc39c185 | ||
|
|
54dd31ea11 | ||
|
|
1d586b353e | ||
|
|
fc04ae01a7 | ||
|
|
929db1df81 | ||
|
|
fd5ac5ce77 | ||
|
|
91c0dc9a59 | ||
|
|
8c0f03c139 | ||
|
|
2398ce360d | ||
|
|
785e5c984c | ||
|
|
a978f43649 | ||
|
|
da6b107e94 | ||
|
|
47268d8d1c | ||
|
|
8916cd6054 | ||
|
|
ca7270b5ee | ||
|
|
2c941e01d5 | ||
|
|
7591d47add | ||
|
|
20b535b293 | ||
|
|
3af76d0a1e | ||
|
|
62f31e2dd4 | ||
|
|
88a9989c13 | ||
|
|
ba0e932239 | ||
|
|
734f0c6693 | ||
|
|
45c937825b | ||
|
|
291f09dcd1 | ||
|
|
e7de4d8965 | ||
|
|
c2aa5d8141 | ||
|
|
dc046e92c4 | ||
|
|
4470767a1c | ||
|
|
e67baeabb0 | ||
|
|
7d35538962 | ||
|
|
10a989cee9 | ||
|
|
3396e1b379 | ||
|
|
0749c55478 | ||
|
|
3b60c8cfd1 | ||
|
|
ef0f719c58 | ||
|
|
1ec6b9acf1 | ||
|
|
fc945f093c | ||
|
|
ebeb726dd9 | ||
|
|
00739ea38f | ||
|
|
71449740fa | ||
|
|
f1ec635f25 | ||
|
|
139f5e8f91 | ||
|
|
7594aecaee | ||
|
|
f0b7c6e640 | ||
|
|
26da7c66bc | ||
|
|
167a0bb8f8 | ||
|
|
c77fff6ffa |
@@ -8,3 +8,12 @@ craftbeerpi.db
|
|||||||
modules/plugins/*
|
modules/plugins/*
|
||||||
!modules/plugins/__init__.py
|
!modules/plugins/__init__.py
|
||||||
*.pyc
|
*.pyc
|
||||||
|
*.js
|
||||||
|
*.css
|
||||||
|
modules/ui/package.json
|
||||||
|
|
||||||
|
modules/ui/.babelrc
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
|
||||||
|
modules/ui/package-lock.json
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
# Dockerfile for development on a pc/mac
|
||||||
|
FROM python:2
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY requirements.txt ./
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["python", "run.py"]
|
||||||
@@ -18,10 +18,31 @@ Type <code>cd craftbeerpi3</code> to navigate into the craftbeerpi folder.
|
|||||||
|
|
||||||
Type <code>sudo ./install.sh</code>
|
Type <code>sudo ./install.sh</code>
|
||||||
|
|
||||||
|
## Hardware Wiring
|
||||||
|
|
||||||
|
Here you will find a guide how to wire everything up.
|
||||||
|
|
||||||
|
http://web.craftbeerpi.com/hardware/
|
||||||
|
|
||||||
## ATTENTION
|
## ATTENTION
|
||||||
|
|
||||||
CraftBeerPi 3.0 is a complete rewrite. Server as well as user interface. I recommend to use a second SD card for testing.
|
CraftBeerPi 3.0 is a complete rewrite. Server as well as user interface. I recommend to use a second SD card for testing.
|
||||||
|
|
||||||
|
## Docker-based development
|
||||||
|
|
||||||
|
For developing this application or its plugins on a PC/Mac you can use the docker-compose file:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ docker-compose up
|
||||||
|
...
|
||||||
|
Starting craftbeerpi3_app_1 ... done
|
||||||
|
Attaching to craftbeerpi3_app_1
|
||||||
|
app_1 | [2018-08-13 12:54:44,264] ERROR in __init__: BUZZER not working
|
||||||
|
app_1 | (1) wsgi starting up on http://0.0.0.0:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
The contents of this folder will be mounted to `/usr/src/craftbeerpi3` and the server will be accesible on `localhost:5000`.
|
||||||
|
|
||||||
## Donation
|
## Donation
|
||||||
|
|
||||||
CraftBeerPi is a free & open source project. If you like to support the project I happy about a donation:
|
CraftBeerPi is a free & open source project. If you like to support the project I happy about a donation:
|
||||||
|
|||||||
+19
-15
@@ -1,4 +1,4 @@
|
|||||||
CREATE TABLE kettle
|
CREATE TABLE IF NOT EXISTS kettle
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(80),
|
name VARCHAR(80),
|
||||||
@@ -13,7 +13,7 @@ CREATE TABLE kettle
|
|||||||
diameter INTEGER
|
diameter INTEGER
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE step
|
CREATE TABLE IF NOT EXISTS step
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
"order" INTEGER,
|
"order" INTEGER,
|
||||||
@@ -27,7 +27,7 @@ CREATE TABLE step
|
|||||||
kettleid INTEGER
|
kettleid INTEGER
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE sensor
|
CREATE TABLE IF NOT EXISTS sensor
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
type VARCHAR(100),
|
type VARCHAR(100),
|
||||||
@@ -36,7 +36,7 @@ CREATE TABLE sensor
|
|||||||
hide BOOLEAN
|
hide BOOLEAN
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE fermenter_step
|
CREATE TABLE IF NOT EXISTS fermenter_step
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(80),
|
name VARCHAR(80),
|
||||||
@@ -54,7 +54,7 @@ CREATE TABLE fermenter_step
|
|||||||
FOREIGN KEY (fermenter_id) REFERENCES fermenter (id)
|
FOREIGN KEY (fermenter_id) REFERENCES fermenter (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE fermenter
|
CREATE TABLE IF NOT EXISTS fermenter
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(80),
|
name VARCHAR(80),
|
||||||
@@ -69,7 +69,7 @@ CREATE TABLE fermenter
|
|||||||
target_temp INTEGER
|
target_temp INTEGER
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE config
|
CREATE TABLE IF NOT EXISTS config
|
||||||
(
|
(
|
||||||
name VARCHAR(50) PRIMARY KEY NOT NULL,
|
name VARCHAR(50) PRIMARY KEY NOT NULL,
|
||||||
value VARCHAR(255),
|
value VARCHAR(255),
|
||||||
@@ -79,19 +79,23 @@ CREATE TABLE config
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO config VALUES ('kettle_cols', 4, 'select', 'Adjust the width of a kettle widget on the brewing dashboard', '[1,2,3, 4, 5, 6, 7, 8, 9, 10, 11, 12]');
|
INSERT OR IGNORE INTO config VALUES ('kettle_cols', 4, 'select', 'Adjust the width of a kettle widget on the brewing dashboard', '[1,2,3, 4, 5, 6, 7, 8, 9, 10, 11, 12]');
|
||||||
INSERT INTO config VALUES ('actor_cols', 4, 'select', 'Adjust the width of a actor widget on the brewing dashboard', '[1,2,3, 4, 5, 6, 7, 8, 9, 10, 11, 12]');
|
INSERT OR IGNORE INTO config VALUES ('actor_cols', 4, 'select', 'Adjust the width of a actor widget on the brewing dashboard', '[1,2,3, 4, 5, 6, 7, 8, 9, 10, 11, 12]');
|
||||||
INSERT INTO config VALUES ('sensor_cols', 4, 'select', 'Adjust the width of a sensor widget on the brewing dashboard', '[1,2,3, 4, 5, 6, 7, 8, 9, 10, 11, 12]');
|
INSERT OR IGNORE INTO config VALUES ('sensor_cols', 4, 'select', 'Adjust the width of a sensor widget on the brewing dashboard', '[1,2,3, 4, 5, 6, 7, 8, 9, 10, 11, 12]');
|
||||||
INSERT INTO config VALUES ('unit', 'C', 'select', 'Temperature Unit', '["C","F"]');
|
INSERT OR IGNORE 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 OR IGNORE 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 OR IGNORE INTO config VALUES ('buzzer', 16, 'select', 'Buzzer GPIO', '[16,17,18,19,20,21,22,23]');
|
||||||
INSERT INTO config VALUES ('setup', 'YES', 'select', 'Show the Setup dialog', '["YES","NO"]');
|
INSERT OR IGNORE INTO config VALUES ('buzzer_beep_level', 'HIGH', 'select', 'Buzzer Logic Beep Level', '["HIGH", "LOW"]');
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('setup', 'YES', 'select', 'Show the Setup dialog', '["YES","NO"]');
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('brew_name', '', 'text', 'Brew Name', NULL);
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('donation_notification', 'YES', 'select', 'Disable Donation Notification', '["YES","NO"]');
|
||||||
|
|
||||||
CREATE TABLE actor
|
|
||||||
|
CREATE TABLE IF NOT EXISTS actor
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(50),
|
name VARCHAR(50),
|
||||||
type VARCHAR(100),
|
type VARCHAR(100),
|
||||||
config VARCHAR(500),
|
config VARCHAR(500),
|
||||||
hide BOOLEAN
|
hide BOOLEAN
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0.2
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# docker-compose file for development on a pc/mac
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
volumes:
|
||||||
|
- $PWD:/usr/src/app
|
||||||
+15
-11
@@ -19,7 +19,8 @@ show_menu () {
|
|||||||
"7" "Software Update (git pull)" \
|
"7" "Software Update (git pull)" \
|
||||||
"8" "Reset File Changes (git reset --hard)" \
|
"8" "Reset File Changes (git reset --hard)" \
|
||||||
"9" "Clear all logs" \
|
"9" "Clear all logs" \
|
||||||
"10" "Reboot Raspberry Pi" 3>&1 1>&2 2>&3)
|
"10" "Reboot Raspberry Pi" \
|
||||||
|
"11" "Stop CraftBeerPi, Clear logs, Start CraftBeerPi" 3>&1 1>&2 2>&3)
|
||||||
|
|
||||||
BUTTON=$?
|
BUTTON=$?
|
||||||
# Exit if user pressed cancel or escape
|
# Exit if user pressed cancel or escape
|
||||||
@@ -33,15 +34,7 @@ show_menu () {
|
|||||||
if [ $? = 0 ]; then
|
if [ $? = 0 ]; then
|
||||||
apt-get -y update; apt-get -y upgrade;
|
apt-get -y update; apt-get -y upgrade;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
confirmAnswer "Would you like to install wiringPI? This is required to control the GPIO"
|
|
||||||
if [ $? = 0 ]; then
|
|
||||||
git clone git://git.drogon.net/wiringPi;
|
|
||||||
cd wiringPi;
|
|
||||||
./build; cd ..;
|
|
||||||
rm -rf wiringPi;
|
|
||||||
fi
|
|
||||||
|
|
||||||
apt-get -y install python-setuptools
|
apt-get -y install python-setuptools
|
||||||
easy_install pip
|
easy_install pip
|
||||||
apt-get -y install python-dev
|
apt-get -y install python-dev
|
||||||
@@ -136,13 +129,24 @@ show_menu () {
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
10)
|
10)
|
||||||
confirmAnswer "Are you sure you want to reboo the Raspberry Pi?"
|
confirmAnswer "Are you sure you want to reboot the Raspberry Pi?"
|
||||||
if [ $? = 0 ]; then
|
if [ $? = 0 ]; then
|
||||||
sudo reboot
|
sudo reboot
|
||||||
else
|
else
|
||||||
show_menu
|
show_menu
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
11)
|
||||||
|
confirmAnswer "Are you sure you want to reboot CraftBeerPi and delete all log files?"
|
||||||
|
if [ $? = 0 ]; then
|
||||||
|
sudo /etc/init.d/craftbeerpiboot stop
|
||||||
|
sudo rm -rf logs/*.log
|
||||||
|
sudo /etc/init.d/craftbeerpiboot start
|
||||||
|
show_menu
|
||||||
|
else
|
||||||
|
show_menu
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -29,12 +29,15 @@ import modules.sensors
|
|||||||
import modules.actor
|
import modules.actor
|
||||||
import modules.notification
|
import modules.notification
|
||||||
import modules.fermenter
|
import modules.fermenter
|
||||||
import modules.addon
|
from modules.addon.endpoints import initPlugins
|
||||||
import modules.ui
|
import modules.ui
|
||||||
import modules.system
|
import modules.system
|
||||||
import modules.buzzer
|
import modules.buzzer
|
||||||
import modules.stats
|
import modules.stats
|
||||||
import modules.kettle
|
import modules.kettle
|
||||||
|
import modules.recipe_import
|
||||||
|
import modules.core.db_mirgrate
|
||||||
|
|
||||||
from app_config import cbpi
|
from app_config import cbpi
|
||||||
# Build the database:
|
# Build the database:
|
||||||
# This will create the database file using SQLAlchemy
|
# This will create the database file using SQLAlchemy
|
||||||
@@ -57,9 +60,13 @@ def init_db():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
init_db()
|
init_db()
|
||||||
|
initPlugins()
|
||||||
cbpi.run_init()
|
cbpi.run_init()
|
||||||
|
|
||||||
cbpi.run_background_processes()
|
cbpi.run_background_processes()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.logger.info("##########################################")
|
||||||
|
app.logger.info("### STARTUP COMPLETE")
|
||||||
|
app.logger.info("##########################################")
|
||||||
@@ -17,10 +17,10 @@ class ActorView(BaseView):
|
|||||||
obj.state = 0
|
obj.state = 0
|
||||||
obj.power = 100
|
obj.power = 100
|
||||||
|
|
||||||
def post_post_callback(self, m):
|
def _post_post_callback(self, m):
|
||||||
self.api.init_actor(m.id)
|
self.api.init_actor(m.id)
|
||||||
|
|
||||||
def post_put_callback(self, m):
|
def _post_put_callback(self, m):
|
||||||
|
|
||||||
self.api.init_actor(m.id)
|
self.api.init_actor(m.id)
|
||||||
|
|
||||||
@@ -60,10 +60,15 @@ class ActorView(BaseView):
|
|||||||
t = self.api.socketio.start_background_task(target=self.toggleTimeJob, id=id, t=t)
|
t = self.api.socketio.start_background_task(target=self.toggleTimeJob, id=id, t=t)
|
||||||
return ('', 204)
|
return ('', 204)
|
||||||
|
|
||||||
|
@route('<int:id>/action/<method>', methods=["POST"])
|
||||||
|
def action(self, id, method):
|
||||||
|
|
||||||
|
cbpi.cache.get("actors").get(id).instance.__getattribute__(method)()
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
|
|
||||||
@cbpi.initalizer(order=1000)
|
@cbpi.initalizer(order=1000)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE ACTOR MODULE"
|
|
||||||
cbpi.app.logger.info("INITIALIZE ACTOR MODULE")
|
|
||||||
ActorView.register(cbpi.app, route_base='/api/actor')
|
ActorView.register(cbpi.app, route_base='/api/actor')
|
||||||
ActorView.init_cache()
|
ActorView.init_cache()
|
||||||
cbpi.init_actors()
|
cbpi.init_actors()
|
||||||
@@ -166,7 +166,9 @@ def update_addon(name):
|
|||||||
|
|
||||||
def loadCorePlugins():
|
def loadCorePlugins():
|
||||||
for filename in os.listdir("./modules/base_plugins"):
|
for filename in os.listdir("./modules/base_plugins"):
|
||||||
if filename.endswith(".py") or filename.endswith(".pyc"):
|
|
||||||
|
|
||||||
|
if os.path.isdir("./modules/base_plugins/"+filename) is False:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
modules[filename] = import_module("modules.base_plugins.%s" % (filename))
|
modules[filename] = import_module("modules.base_plugins.%s" % (filename))
|
||||||
@@ -178,7 +180,7 @@ def loadCorePlugins():
|
|||||||
|
|
||||||
def loadPlugins():
|
def loadPlugins():
|
||||||
for filename in os.listdir("./modules/plugins"):
|
for filename in os.listdir("./modules/plugins"):
|
||||||
if filename.endswith(".py") or filename.endswith(".pyc"):
|
if os.path.isdir("./modules/plugins/" + filename) is False:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
modules[filename] = import_module("modules.plugins.%s" % (filename))
|
modules[filename] = import_module("modules.plugins.%s" % (filename))
|
||||||
@@ -186,13 +188,12 @@ def loadPlugins():
|
|||||||
cbpi.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None)
|
cbpi.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None)
|
||||||
cbpi.app.logger.error(e)
|
cbpi.app.logger.error(e)
|
||||||
|
|
||||||
@cbpi.initalizer(order=1)
|
#@cbpi.initalizer(order=1)
|
||||||
def initPlugins(app):
|
def initPlugins():
|
||||||
print "INITIALIZE CUSTOM PLUGINS"
|
|
||||||
loadCorePlugins()
|
loadCorePlugins()
|
||||||
loadPlugins()
|
loadPlugins()
|
||||||
|
|
||||||
@cbpi.initalizer(order=2)
|
@cbpi.initalizer(order=2)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE ADDON MODULE"
|
|
||||||
cbpi.app.register_blueprint(blueprint, url_prefix='/api/editor')
|
cbpi.app.register_blueprint(blueprint, url_prefix='/api/editor')
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ from modules.core.db import DBModel
|
|||||||
|
|
||||||
app = Flask(__name__)
|
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['SECRET_KEY'] = 'craftbeerpi'
|
||||||
app.config['UPLOAD_FOLDER'] = './upload'
|
app.config['UPLOAD_FOLDER'] = './upload'
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ class ComplexEncoder(json.JSONEncoder):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
app.json_encoder = ComplexEncoder
|
app.json_encoder = ComplexEncoder
|
||||||
socketio = SocketIO(app, json=json)
|
socketio = SocketIO(app, json=json, logging=False)
|
||||||
cbpi = CraftBeerPi(app, socketio)
|
cbpi = CraftBeerPi(app, socketio)
|
||||||
|
|
||||||
app.logger.info("##########################################")
|
app.logger.info("##########################################")
|
||||||
|
|||||||
@@ -7,20 +7,21 @@ from modules.core.step import StepBase
|
|||||||
from modules import cbpi
|
from modules import cbpi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@cbpi.step
|
@cbpi.step
|
||||||
class MashStep(StepBase):
|
class MashStep(StepBase):
|
||||||
'''
|
'''
|
||||||
Just put the decorator @cbpi.step on top of a method
|
Just put the decorator @cbpi.step on top of a method
|
||||||
'''
|
'''
|
||||||
# Properties
|
# Properties
|
||||||
temp = Property.Number("Temperature", configurable=True)
|
temp = Property.Number("Temperature", configurable=True, description="Target Temperature of Mash Step")
|
||||||
kettle = StepProperty.Kettle("Kettle")
|
kettle = StepProperty.Kettle("Kettle", description="Kettle in which the mashing takes place")
|
||||||
timer = Property.Number("Timer in Minutes", configurable=True)
|
timer = Property.Number("Timer in Minutes", configurable=True, description="Timer is started when the target temperature is reached")
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
'''
|
'''
|
||||||
Initialize Step. This method is called once at the beginning of the step
|
Initialize Step. This method is called once at the beginning of the step
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
# set target tep
|
# set target tep
|
||||||
self.set_target_temp(self.temp, self.kettle)
|
self.set_target_temp(self.temp, self.kettle)
|
||||||
@@ -30,7 +31,7 @@ class MashStep(StepBase):
|
|||||||
'''
|
'''
|
||||||
Custom Action which can be execute form the brewing dashboard.
|
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
|
All method with decorator @cbpi.action("YOUR CUSTOM NAME") will be available in the user interface
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
if self.is_timer_finished() is None:
|
if self.is_timer_finished() is None:
|
||||||
self.start_timer(int(self.timer) * 60)
|
self.start_timer(int(self.timer) * 60)
|
||||||
@@ -45,17 +46,18 @@ class MashStep(StepBase):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
'''
|
'''
|
||||||
This method is execute in an interval
|
This method is execute in an interval
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Check if Target Temp is reached
|
# Check if Target Temp is reached
|
||||||
if self.get_kettle_temp(self.kettle) >= int(self.temp):
|
if self.get_kettle_temp(self.kettle) >= float(self.temp):
|
||||||
# Check if Timer is Running
|
# Check if Timer is Running
|
||||||
if self.is_timer_finished() is None:
|
if self.is_timer_finished() is None:
|
||||||
self.start_timer(int(self.timer) * 60)
|
self.start_timer(int(self.timer) * 60)
|
||||||
|
|
||||||
# Check if timer finished and go to next step
|
# Check if timer finished and go to next step
|
||||||
if self.is_timer_finished() == True:
|
if self.is_timer_finished() == True:
|
||||||
|
self.notify("Mash Step Completed!", "Starting the next step", timeout=None)
|
||||||
self.next()
|
self.next()
|
||||||
|
|
||||||
|
|
||||||
@@ -65,14 +67,18 @@ class MashInStep(StepBase):
|
|||||||
Just put the decorator @cbpi.step on top of a method
|
Just put the decorator @cbpi.step on top of a method
|
||||||
'''
|
'''
|
||||||
# Properties
|
# Properties
|
||||||
temp = Property.Number("Temperature", configurable=True)
|
temp = Property.Number("Temperature", configurable=True, description="Target Temperature of Mash Step")
|
||||||
kettle = StepProperty.Kettle("Kettle")
|
kettle = StepProperty.Kettle("Kettle", description="Kettle in which the mashing takes place")
|
||||||
s = False
|
s = False
|
||||||
|
|
||||||
|
@cbpi.action("Change Power")
|
||||||
|
def change_power(self):
|
||||||
|
self.actor_power(1, 50)
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
'''
|
'''
|
||||||
Initialize Step. This method is called once at the beginning of the step
|
Initialize Step. This method is called once at the beginning of the step
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
# set target tep
|
# set target tep
|
||||||
self.s = False
|
self.s = False
|
||||||
@@ -83,13 +89,141 @@ class MashInStep(StepBase):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
'''
|
'''
|
||||||
This method is execute in an interval
|
This method is execute in an interval
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Check if Target Temp is reached
|
# Check if Target Temp is reached
|
||||||
if self.get_kettle_temp(self.kettle) >= int(self.temp) and self.s is False:
|
if self.get_kettle_temp(self.kettle) >= float(self.temp) and self.s is False:
|
||||||
self.s = True
|
self.s = True
|
||||||
self.notify("Step Temp Reached!", "Please press the next button to continue", timeout=None)
|
self.notify("Step Temp Reached!", "Please press the next button to continue", timeout=None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@cbpi.step
|
||||||
|
class ChilStep(StepBase):
|
||||||
|
|
||||||
|
timer = Property.Number("Timer in Minutes", configurable=True, default_value=0, description="Timer is started immediately")
|
||||||
|
|
||||||
|
@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", description="Pump actor gets toogled")
|
||||||
|
timer = Property.Number("Timer in Minutes", configurable=True, default_value=0, description="Timer is started immediately")
|
||||||
|
|
||||||
|
@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, description="Target temperature for boiling")
|
||||||
|
kettle = StepProperty.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.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) >= 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()
|
||||||
|
|||||||
@@ -10,21 +10,38 @@ from modules.core.props import Property
|
|||||||
|
|
||||||
@cbpi.sensor
|
@cbpi.sensor
|
||||||
class DummyTempSensor(SensorActive):
|
class DummyTempSensor(SensorActive):
|
||||||
temp = Property.Number("Temperature", configurable=True, default_value=5)
|
|
||||||
|
temp = Property.Number("Temperature", configurable=True, default_value=5, description="Dummy Temperature as decimal value")
|
||||||
|
|
||||||
|
@cbpi.action("My Custom Action")
|
||||||
|
def my_action(self):
|
||||||
|
print "HELLO WORLD"
|
||||||
|
pass
|
||||||
|
|
||||||
def get_unit(self):
|
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"
|
return "°C" if self.get_config_parameter("unit", "C") == "C" else "°F"
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
pass
|
SensorActive.stop(self)
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
while self.is_running():
|
'''
|
||||||
|
Active sensor has to handle his own loop
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
|
while self.is_running() is True:
|
||||||
self.data_received(self.temp)
|
self.data_received(self.temp)
|
||||||
socketio.sleep(5)
|
self.sleep(5)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_global(cls):
|
||||||
|
'''
|
||||||
|
Called one at the startup for all sensors
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ from modules.core.props import Property
|
|||||||
@cbpi.fermentation_controller
|
@cbpi.fermentation_controller
|
||||||
class Hysteresis(FermenterController):
|
class Hysteresis(FermenterController):
|
||||||
|
|
||||||
heater_offset_min = Property.Number("Heater Offset min", True, 0)
|
heater_offset_min = Property.Number("Heater Offset ON", True, 0, description="Offset as decimal number when the heater is switched on. Should be greater then 'Heater Offset OFF'. For example a value of 2 switches on the heater if the current temperature is 2 degrees below the target temperature")
|
||||||
heater_offset_max = Property.Number("Heater Offset max", True, 0)
|
heater_offset_max = Property.Number("Heater Offset OFF", True, 0, description="Offset as decimal number when the heater is switched off. Should be smaller then 'Heater Offset ON'. For example a value of 1 switches off the heater if the current temperature is 1 degree below the target temperature")
|
||||||
cooler_offset_min = Property.Number("Cooler Offset min", True, 0)
|
cooler_offset_min = Property.Number("Cooler Offset ON", True, 0, description="Offset as decimal number when the cooler is switched on. Should be greater then 'Cooler Offset OFF'. For example a value of 2 switches on the cooler if the current temperature is 2 degrees above the target temperature")
|
||||||
cooler_offset_max = Property.Number("Cooler Offset max", True, 0)
|
cooler_offset_max = Property.Number("Cooler Offset OFF", True, 0, description="Offset as decimal number when the cooler is switched off. Should be less then 'Cooler Offset ON'. For example a value of 1 switches off the cooler if the current temperature is 1 degree above the target temperature")
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
super(FermenterController, self).stop()
|
super(FermenterController, self).stop()
|
||||||
@@ -23,16 +23,16 @@ class Hysteresis(FermenterController):
|
|||||||
target_temp = self.get_target_temp()
|
target_temp = self.get_target_temp()
|
||||||
temp = self.get_temp()
|
temp = self.get_temp()
|
||||||
|
|
||||||
if temp + int(self.heater_offset_min) < target_temp:
|
if temp + float(self.heater_offset_min) <= target_temp:
|
||||||
self.heater_on(100)
|
self.heater_on(100)
|
||||||
|
|
||||||
if temp + int(self.heater_offset_max) > target_temp:
|
if temp + float(self.heater_offset_max) >= target_temp:
|
||||||
self.heater_off()
|
self.heater_off()
|
||||||
|
|
||||||
if temp > target_temp + int(self.cooler_offset_min):
|
if temp >= target_temp + float(self.cooler_offset_min):
|
||||||
self.cooler_on(100)
|
self.cooler_on(100)
|
||||||
|
|
||||||
if temp < target_temp + int(self.cooler_offset_max):
|
if temp <= target_temp + float(self.cooler_offset_max):
|
||||||
self.cooler_off()
|
self.cooler_off()
|
||||||
|
|
||||||
self.sleep(1)
|
self.sleep(1)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ except Exception as e:
|
|||||||
@cbpi.actor
|
@cbpi.actor
|
||||||
class GPIOSimple(ActorBase):
|
class GPIOSimple(ActorBase):
|
||||||
|
|
||||||
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27])
|
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27], description="GPIO to which the actor is connected")
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
GPIO.setup(int(self.gpio), GPIO.OUT)
|
GPIO.setup(int(self.gpio), GPIO.OUT)
|
||||||
@@ -35,11 +35,11 @@ class GPIOSimple(ActorBase):
|
|||||||
@cbpi.actor
|
@cbpi.actor
|
||||||
class GPIOPWM(ActorBase):
|
class GPIOPWM(ActorBase):
|
||||||
|
|
||||||
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27])
|
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27], description="GPIO to which the actor is connected")
|
||||||
duty_cylce = Property.Number("Duty Cycle", configurable=True)
|
frequency = Property.Number("Frequency (Hz)", configurable=True)
|
||||||
|
|
||||||
p = None
|
p = None
|
||||||
power = 100
|
power = 100 # duty cycle
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
GPIO.setup(int(self.gpio), GPIO.OUT)
|
GPIO.setup(int(self.gpio), GPIO.OUT)
|
||||||
@@ -50,13 +50,18 @@ class GPIOPWM(ActorBase):
|
|||||||
if power is not None:
|
if power is not None:
|
||||||
self.power = int(power)
|
self.power = int(power)
|
||||||
|
|
||||||
if self.duty_cylce is None:
|
if self.frequency is None:
|
||||||
duty_cylce = 50
|
self.frequency = 0.5 # 2 sec
|
||||||
|
|
||||||
self.p = GPIO.PWM(int(self.gpio), int(self.duty_cylce))
|
self.p = GPIO.PWM(int(self.gpio), float(self.frequency))
|
||||||
self.p.start(int(self.power))
|
self.p.start(int(self.power))
|
||||||
|
|
||||||
def set_power(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:
|
if power is not None:
|
||||||
self.power = int(power)
|
self.power = int(power)
|
||||||
self.p.ChangeDutyCycle(self.power)
|
self.p.ChangeDutyCycle(self.power)
|
||||||
@@ -69,7 +74,7 @@ class GPIOPWM(ActorBase):
|
|||||||
@cbpi.actor
|
@cbpi.actor
|
||||||
class RelayBoard(ActorBase):
|
class RelayBoard(ActorBase):
|
||||||
|
|
||||||
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27])
|
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27], description="GPIO to which the actor is connected")
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
GPIO.setup(int(self.gpio), GPIO.OUT)
|
GPIO.setup(int(self.gpio), GPIO.OUT)
|
||||||
@@ -86,8 +91,17 @@ class RelayBoard(ActorBase):
|
|||||||
@cbpi.actor
|
@cbpi.actor
|
||||||
class Dummy(ActorBase):
|
class Dummy(ActorBase):
|
||||||
|
|
||||||
|
|
||||||
def on(self, power=100):
|
def on(self, power=100):
|
||||||
|
'''
|
||||||
|
Code to switch on the actor
|
||||||
|
:param power: int value between 0 - 100
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
print "ON"
|
print "ON"
|
||||||
|
|
||||||
def off(self):
|
def off(self):
|
||||||
print "OFF"
|
print "OFF"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ class Hysteresis(KettleController):
|
|||||||
|
|
||||||
# Custom Properties
|
# Custom Properties
|
||||||
|
|
||||||
on = Property.Number("Offset On", True, 0)
|
on = Property.Number("Offset On", True, 0, description="Offset below target temp when heater should switched on. Should be bigger then Offset Off")
|
||||||
off = Property.Number("Offset Off", True, 0)
|
off = Property.Number("Offset Off", True, 0, description="Offset below target temp when heater should switched off. Should be smaller then Offset Off")
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
'''
|
'''
|
||||||
@@ -29,11 +29,10 @@ class Hysteresis(KettleController):
|
|||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
while self.is_running():
|
while self.is_running():
|
||||||
if self.get_temp() < self.get_target_temp() - int(self.on):
|
|
||||||
|
if self.get_temp() < self.get_target_temp() - float(self.on):
|
||||||
self.heater_on(100)
|
self.heater_on(100)
|
||||||
elif self.get_temp() >= self.get_target_temp() - int(self.off):
|
elif self.get_temp() >= self.get_target_temp() - float(self.off):
|
||||||
self.heater_off()
|
|
||||||
else:
|
|
||||||
self.heater_off()
|
self.heater_off()
|
||||||
self.sleep(1)
|
self.sleep(1)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ def getSensors():
|
|||||||
arr = []
|
arr = []
|
||||||
for dirname in os.listdir('/sys/bus/w1/devices'):
|
for dirname in os.listdir('/sys/bus/w1/devices'):
|
||||||
if (dirname.startswith("28") or dirname.startswith("10")):
|
if (dirname.startswith("28") or dirname.startswith("10")):
|
||||||
|
cbpi.app.logger.info("Device %s Found (Family: 28/10, Thermometer on GPIO4 (w1))" % dirname)
|
||||||
arr.append(dirname)
|
arr.append(dirname)
|
||||||
return arr
|
return arr
|
||||||
except:
|
except:
|
||||||
@@ -66,7 +67,8 @@ class myThread (threading.Thread):
|
|||||||
@cbpi.sensor
|
@cbpi.sensor
|
||||||
class ONE_WIRE_SENSOR(SensorPassive):
|
class ONE_WIRE_SENSOR(SensorPassive):
|
||||||
|
|
||||||
sensor_name = Property.Select("Sensor", getSensors())
|
sensor_name = Property.Select("Sensor", getSensors(), description="The OneWire sensor address.")
|
||||||
|
offset = Property.Number("Offset", True, 0, description="Offset which is added to the received sensor data. Positive and negative values are both allowed.")
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
|
|
||||||
@@ -86,10 +88,14 @@ class ONE_WIRE_SENSOR(SensorPassive):
|
|||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
if self.get_config_parameter("unit", "C") == "C":
|
if self.get_config_parameter("unit", "C") == "C":
|
||||||
self.data_received(self.t.value)
|
self.data_received(round(self.t.value + self.offset_value(), 2))
|
||||||
else:
|
else:
|
||||||
self.data_received(format(9.0 / 5.0 * self.t.value + 32, '.2f'))
|
self.data_received(round(9.0 / 5.0 * self.t.value + 32 + self.offset_value(), 2))
|
||||||
|
|
||||||
|
@cbpi.try_catch(0)
|
||||||
|
def offset_value(self):
|
||||||
|
return float(self.offset)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init_global(self):
|
def init_global(self):
|
||||||
try:
|
try:
|
||||||
@@ -108,5 +114,5 @@ def set_temp(t):
|
|||||||
|
|
||||||
@cbpi.initalizer()
|
@cbpi.initalizer()
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
cbpi.app.logger.info("INITIALIZE ONE WIRE MODULE")
|
|
||||||
cbpi.app.register_blueprint(blueprint, url_prefix='/api/one_wire')
|
cbpi.app.register_blueprint(blueprint, url_prefix='/api/one_wire')
|
||||||
|
|||||||
+17
-11
@@ -9,16 +9,18 @@ except Exception as e:
|
|||||||
|
|
||||||
class Buzzer(object):
|
class Buzzer(object):
|
||||||
|
|
||||||
sound = ["H", 0.1, "L"]
|
sound = ["H", 0.1, "L", 0.1, "H", 0.1, "L", 0.1, "H", 0.1, "L"]
|
||||||
def __init__(self, gpio):
|
def __init__(self, gpio, beep_level):
|
||||||
try:
|
try:
|
||||||
cbpi.app.logger.info("INIT BUZZER NOW GPIO%s" % gpio)
|
cbpi.app.logger.info("INIT BUZZER NOW GPIO%s" % gpio)
|
||||||
self.gpio = gpio
|
self.gpio = int(gpio)
|
||||||
|
self.beep_level = beep_level
|
||||||
GPIO.setmode(GPIO.BCM)
|
GPIO.setmode(GPIO.BCM)
|
||||||
GPIO.setup(gpio, GPIO.OUT)
|
GPIO.setup(self.gpio, GPIO.OUT)
|
||||||
self.state = True
|
self.state = True
|
||||||
cbpi.app.logger.info("BUZZER SETUP OK")
|
cbpi.app.logger.info("BUZZER SETUP OK")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
cbpi.app.logger.info("BUZZER EXCEPTION %s" % str(e))
|
||||||
self.state = False
|
self.state = False
|
||||||
|
|
||||||
def beep(self):
|
def beep(self):
|
||||||
@@ -30,10 +32,14 @@ class Buzzer(object):
|
|||||||
try:
|
try:
|
||||||
for i in sound:
|
for i in sound:
|
||||||
if (isinstance(i, str)):
|
if (isinstance(i, str)):
|
||||||
if i == "H":
|
if i == "H" and self.beep_level == "HIGH":
|
||||||
GPIO.output(int(self.gpio), GPIO.HIGH)
|
GPIO.output(int(self.gpio), GPIO.HIGH)
|
||||||
else:
|
elif i == "H" and self.beep_level != "HIGH":
|
||||||
GPIO.output(int(self.gpio), GPIO.LOW)
|
GPIO.output(int(self.gpio), GPIO.LOW)
|
||||||
|
elif i == "L" and self.beep_level == "HIGH":
|
||||||
|
GPIO.output(int(self.gpio), GPIO.LOW)
|
||||||
|
else:
|
||||||
|
GPIO.output(int(self.gpio), GPIO.HIGH)
|
||||||
else:
|
else:
|
||||||
time.sleep(i)
|
time.sleep(i)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -41,11 +47,11 @@ class Buzzer(object):
|
|||||||
|
|
||||||
start_new_thread(play, (self.sound,))
|
start_new_thread(play, (self.sound,))
|
||||||
|
|
||||||
@cbpi.initalizer(order=0)
|
@cbpi.initalizer(order=1)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INIT BUZZER"
|
gpio = cbpi.get_config_parameter("buzzer", 22)
|
||||||
cbpi.app.logger.info("INIT BUZZER")
|
beep_level = cbpi.get_config_parameter("buzzer_beep_level", "HIGH")
|
||||||
gpio = cbpi.get_config_parameter("buzzer", 16)
|
|
||||||
cbpi.buzzer = Buzzer(gpio)
|
cbpi.buzzer = Buzzer(gpio, beep_level)
|
||||||
cbpi.beep()
|
cbpi.beep()
|
||||||
cbpi.app.logger.info("INIT OK")
|
cbpi.app.logger.info("INIT OK")
|
||||||
|
|||||||
@@ -24,10 +24,9 @@ class ConfigView(BaseView):
|
|||||||
update_data = {"name": data["name"], "value": data["value"]}
|
update_data = {"name": data["name"], "value": data["value"]}
|
||||||
|
|
||||||
if self.api.cache.get(self.cache_key) is not None:
|
if self.api.cache.get(self.cache_key) is not None:
|
||||||
#self.pre_post_callback(self.api.cache.get(self.cache_key)[name])
|
|
||||||
self.api.cache.get(self.cache_key)[name].__dict__.update(**update_data)
|
self.api.cache.get(self.cache_key)[name].__dict__.update(**update_data)
|
||||||
m = self.model.update(**self.api.cache.get(self.cache_key)[name].__dict__)
|
m = self.model.update(**self.api.cache.get(self.cache_key)[name].__dict__)
|
||||||
self.post_put_callback(self.api.cache.get(self.cache_key)[name])
|
self._post_put_callback(self.api.cache.get(self.cache_key)[name])
|
||||||
return json.dumps(self.api.cache.get(self.cache_key)[name].__dict__)
|
return json.dumps(self.api.cache.get(self.cache_key)[name].__dict__)
|
||||||
|
|
||||||
@route('/<id>', methods=["GET"])
|
@route('/<id>', methods=["GET"])
|
||||||
@@ -51,9 +50,8 @@ class ConfigView(BaseView):
|
|||||||
cls.post_init_callback(value)
|
cls.post_init_callback(value)
|
||||||
cls.api.cache[cls.cache_key][value.name] = value
|
cls.api.cache[cls.cache_key][value.name] = value
|
||||||
|
|
||||||
|
@cbpi.initalizer(order=0)
|
||||||
@cbpi.initalizer(order=1)
|
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE CONFIG MODULE"
|
|
||||||
ConfigView.register(cbpi.app, route_base='/api/config')
|
ConfigView.register(cbpi.app, route_base='/api/config')
|
||||||
ConfigView.init_cache()
|
ConfigView.init_cache()
|
||||||
|
|||||||
+15
-15
@@ -9,7 +9,7 @@ class BaseView(FlaskView):
|
|||||||
cache_key = None
|
cache_key = None
|
||||||
api = cbpi
|
api = cbpi
|
||||||
|
|
||||||
@route('/<int:id>')
|
@route('/<int:id>', methods=["GET"])
|
||||||
def getOne(self, id):
|
def getOne(self, id):
|
||||||
|
|
||||||
if self.api.cache.get(self.cache_key) is not None:
|
if self.api.cache.get(self.cache_key) is not None:
|
||||||
@@ -17,36 +17,36 @@ class BaseView(FlaskView):
|
|||||||
else:
|
else:
|
||||||
return json.dumps(self.model.get_one(id))
|
return json.dumps(self.model.get_one(id))
|
||||||
|
|
||||||
@route('/')
|
@route('/', methods=["GET"])
|
||||||
def getAll(self):
|
def getAll(self):
|
||||||
if self.api.cache.get(self.cache_key) is not None:
|
if self.api.cache.get(self.cache_key) is not None:
|
||||||
return json.dumps(self.api.cache.get(self.cache_key))
|
return json.dumps(self.api.cache.get(self.cache_key))
|
||||||
else:
|
else:
|
||||||
return json.dumps(self.model.get_all())
|
return json.dumps(self.model.get_all())
|
||||||
|
|
||||||
def pre_post_callback(self, data):
|
def _pre_post_callback(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def post_post_callback(self, m):
|
def _post_post_callback(self, m):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@route('/', methods=["POST"])
|
@route('/', methods=["POST"])
|
||||||
def post(self):
|
def post(self):
|
||||||
data = request.json
|
data = request.json
|
||||||
self.pre_post_callback(data)
|
self._pre_post_callback(data)
|
||||||
m = self.model.insert(**data)
|
m = self.model.insert(**data)
|
||||||
if self.api.cache.get(self.cache_key) is not None:
|
if self.api.cache.get(self.cache_key) is not None:
|
||||||
self.api.cache.get(self.cache_key)[m.id] = m
|
self.api.cache.get(self.cache_key)[m.id] = m
|
||||||
|
|
||||||
self.post_post_callback(m)
|
self._post_post_callback(m)
|
||||||
|
|
||||||
return json.dumps(m)
|
return json.dumps(m)
|
||||||
|
|
||||||
def pre_put_callback(self, m):
|
def _pre_put_callback(self, m):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def post_put_callback(self, m):
|
def _post_put_callback(self, m):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -59,32 +59,32 @@ class BaseView(FlaskView):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
if self.api.cache.get(self.cache_key) is not None:
|
if self.api.cache.get(self.cache_key) is not None:
|
||||||
self.pre_put_callback(self.api.cache.get(self.cache_key)[id])
|
self._pre_put_callback(self.api.cache.get(self.cache_key)[id])
|
||||||
self.api.cache.get(self.cache_key)[id].__dict__.update(**data)
|
self.api.cache.get(self.cache_key)[id].__dict__.update(**data)
|
||||||
m = self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__)
|
m = self.model.update(**self.api.cache.get(self.cache_key)[id].__dict__)
|
||||||
self.post_put_callback(self.api.cache.get(self.cache_key)[id])
|
self._post_put_callback(self.api.cache.get(self.cache_key)[id])
|
||||||
return json.dumps(self.api.cache.get(self.cache_key)[id])
|
return json.dumps(self.api.cache.get(self.cache_key)[id])
|
||||||
else:
|
else:
|
||||||
m = self.model.update(**data)
|
m = self.model.update(**data)
|
||||||
|
|
||||||
self.post_put_callback(m)
|
self._post_put_callback(m)
|
||||||
return json.dumps(m)
|
return json.dumps(m)
|
||||||
|
|
||||||
|
|
||||||
def pre_delete_callback(self, m):
|
def _pre_delete_callback(self, m):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def post_delete_callback(self, id):
|
def _post_delete_callback(self, id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@route('/<int:id>', methods=["DELETE"])
|
@route('/<int:id>', methods=["DELETE"])
|
||||||
def delete(self, id):
|
def delete(self, id):
|
||||||
if self.api.cache.get(self.cache_key) is not None:
|
if self.api.cache.get(self.cache_key) is not None:
|
||||||
self.pre_delete_callback(self.api.cache.get(self.cache_key)[id])
|
self._pre_delete_callback(self.api.cache.get(self.cache_key)[id])
|
||||||
del self.api.cache.get(self.cache_key)[id]
|
del self.api.cache.get(self.cache_key)[id]
|
||||||
m = self.model.delete(id)
|
m = self.model.delete(id)
|
||||||
|
|
||||||
def post_delete_callback(self, id):
|
def _post_delete_callback(self, id):
|
||||||
pass
|
pass
|
||||||
return ('',204)
|
return ('',204)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ class ActorController(object):
|
|||||||
|
|
||||||
self.api.switch_actor_off(int(id))
|
self.api.switch_actor_off(int(id))
|
||||||
|
|
||||||
|
@cbpi.try_catch(None)
|
||||||
|
def actor_power(self, power, id=None):
|
||||||
|
if id is None:
|
||||||
|
id = self.heater
|
||||||
|
self.api.actor_power(int(id), power)
|
||||||
|
|
||||||
|
|
||||||
class SensorController(object):
|
class SensorController(object):
|
||||||
|
|
||||||
@@ -68,6 +74,14 @@ class ControllerBase(object):
|
|||||||
|
|
||||||
class KettleController(ControllerBase, ActorController, SensorController):
|
class KettleController(ControllerBase, ActorController, SensorController):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def chart(kettle):
|
||||||
|
result = []
|
||||||
|
result.append({"name": "Temp", "data_type": "sensor", "data_id": kettle.sensor})
|
||||||
|
result.append({"name": "Target Temp", "data_type": "kettle", "data_id": kettle.id})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
def __init__(self, *args, **kwds):
|
||||||
ControllerBase.__init__(self, *args, **kwds)
|
ControllerBase.__init__(self, *args, **kwds)
|
||||||
self.kettle_id = kwds.get("kettle_id")
|
self.kettle_id = kwds.get("kettle_id")
|
||||||
@@ -101,6 +115,13 @@ class KettleController(ControllerBase, ActorController, SensorController):
|
|||||||
|
|
||||||
class FermenterController(ControllerBase, ActorController, SensorController):
|
class FermenterController(ControllerBase, ActorController, SensorController):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def chart(fermenter):
|
||||||
|
result = []
|
||||||
|
result.append({"name": "Temp", "data_type": "sensor", "data_id": fermenter.sensor})
|
||||||
|
result.append({"name": "Target Temp", "data_type": "fermenter", "data_id": fermenter.id})
|
||||||
|
return result
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
def __init__(self, *args, **kwds):
|
||||||
ControllerBase.__init__(self, *args, **kwds)
|
ControllerBase.__init__(self, *args, **kwds)
|
||||||
self.fermenter_id = kwds.get("fermenter_id")
|
self.fermenter_id = kwds.get("fermenter_id")
|
||||||
@@ -118,7 +139,7 @@ class FermenterController(ControllerBase, ActorController, SensorController):
|
|||||||
def heater_on(self, power=100):
|
def heater_on(self, power=100):
|
||||||
f = self.api.cache.get("fermenter").get(self.fermenter_id)
|
f = self.api.cache.get("fermenter").get(self.fermenter_id)
|
||||||
if f.heater is not None:
|
if f.heater is not None:
|
||||||
self.actor_on(int(f.heater))
|
self.actor_on(power, int(f.heater))
|
||||||
|
|
||||||
@cbpi.try_catch(None)
|
@cbpi.try_catch(None)
|
||||||
def heater_off(self):
|
def heater_off(self):
|
||||||
@@ -129,13 +150,13 @@ class FermenterController(ControllerBase, ActorController, SensorController):
|
|||||||
@cbpi.try_catch(None)
|
@cbpi.try_catch(None)
|
||||||
def cooler_on(self, power=100):
|
def cooler_on(self, power=100):
|
||||||
f = self.api.cache.get("fermenter").get(self.fermenter_id)
|
f = self.api.cache.get("fermenter").get(self.fermenter_id)
|
||||||
if f.heater is not None:
|
if f.cooler is not None:
|
||||||
self.actor_on(power, int(f.cooler))
|
self.actor_on(power, int(f.cooler))
|
||||||
|
|
||||||
@cbpi.try_catch(None)
|
@cbpi.try_catch(None)
|
||||||
def cooler_off(self):
|
def cooler_off(self):
|
||||||
f = self.api.cache.get("fermenter").get(self.fermenter_id)
|
f = self.api.cache.get("fermenter").get(self.fermenter_id)
|
||||||
if f.heater is not None:
|
if f.cooler is not None:
|
||||||
self.actor_off(int(f.cooler))
|
self.actor_off(int(f.cooler))
|
||||||
|
|
||||||
@cbpi.try_catch(None)
|
@cbpi.try_catch(None)
|
||||||
|
|||||||
+56
-24
@@ -1,3 +1,4 @@
|
|||||||
|
import inspect
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
@@ -28,6 +29,7 @@ class ActorAPI(object):
|
|||||||
self.app.logger.info("Init Actors")
|
self.app.logger.info("Init Actors")
|
||||||
t = self.cache.get("actor_types")
|
t = self.cache.get("actor_types")
|
||||||
for key, value in t.iteritems():
|
for key, value in t.iteritems():
|
||||||
|
value.get("class").api = self
|
||||||
value.get("class").init_global()
|
value.get("class").init_global()
|
||||||
|
|
||||||
for key in self.cache.get("actors"):
|
for key in self.cache.get("actors"):
|
||||||
@@ -38,7 +40,6 @@ class ActorAPI(object):
|
|||||||
value = self.cache.get("actors").get(int(id))
|
value = self.cache.get("actors").get(int(id))
|
||||||
cfg = value.config.copy()
|
cfg = value.config.copy()
|
||||||
cfg.update(dict(api=self, id=id, name=value.name))
|
cfg.update(dict(api=self, id=id, name=value.name))
|
||||||
cfg.update(dict(api=self, id=id, name=value.name))
|
|
||||||
clazz = self.cache.get("actor_types").get(value.type).get("class")
|
clazz = self.cache.get("actor_types").get(value.type).get("class")
|
||||||
value.instance = clazz(**cfg)
|
value.instance = clazz(**cfg)
|
||||||
value.instance.init()
|
value.instance.init()
|
||||||
@@ -73,12 +74,8 @@ class ActorAPI(object):
|
|||||||
|
|
||||||
if actor.state == 0:
|
if actor.state == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
actor.instance.off()
|
actor.instance.off()
|
||||||
actor.state = 0
|
actor.state = 0
|
||||||
|
|
||||||
|
|
||||||
self.emit("SWITCH_ACTOR", actor)
|
self.emit("SWITCH_ACTOR", actor)
|
||||||
|
|
||||||
class SensorAPI(object):
|
class SensorAPI(object):
|
||||||
@@ -161,9 +158,8 @@ class SensorAPI(object):
|
|||||||
def log_action(self, text):
|
def log_action(self, text):
|
||||||
filename = "./logs/action.log"
|
filename = "./logs/action.log"
|
||||||
formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
|
formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
|
||||||
|
|
||||||
|
|
||||||
with open(filename, "a") as file:
|
with open(filename, "a") as file:
|
||||||
|
text = text.encode("utf-8")
|
||||||
file.write("%s,%s\n" % (formatted_time, text))
|
file.write("%s,%s\n" % (formatted_time, text))
|
||||||
|
|
||||||
def shutdown_sensor(self, id):
|
def shutdown_sensor(self, id):
|
||||||
@@ -173,7 +169,7 @@ class SensorAPI(object):
|
|||||||
def get_sensor_value(self, id):
|
def get_sensor_value(self, id):
|
||||||
try:
|
try:
|
||||||
id = int(id)
|
id = int(id)
|
||||||
return int(self.cache.get("sensors")[id].instance.last_value)
|
return float(self.cache.get("sensors")[id].instance.last_value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
||||||
return None
|
return None
|
||||||
@@ -226,12 +222,9 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
def notify(self, headline, message, type="success", timeout=5000):
|
def notify(self, headline, message, type="success", timeout=5000):
|
||||||
self.beep()
|
self.beep()
|
||||||
msg = {"id": str(uuid.uuid1()), "type": type, "headline": headline, "message": message, "timeout": timeout}
|
msg = {"id": str(uuid.uuid1()), "type": type, "headline": headline, "message": message, "timeout": timeout}
|
||||||
if timeout is None:
|
self.emit_message(msg)
|
||||||
self.cache["messages"].append(msg)
|
|
||||||
self.emit("NOTIFY", msg)
|
|
||||||
|
|
||||||
def beep(self):
|
def beep(self):
|
||||||
|
|
||||||
if self.buzzer is not None:
|
if self.buzzer is not None:
|
||||||
self.buzzer.beep()
|
self.buzzer.beep()
|
||||||
|
|
||||||
@@ -248,6 +241,15 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
else:
|
else:
|
||||||
return cfg.value
|
return cfg.value
|
||||||
|
|
||||||
|
def set_config_parameter(self, name, value):
|
||||||
|
from modules.config import Config
|
||||||
|
with self.app.app_context():
|
||||||
|
update_data = {"name": name, "value": value}
|
||||||
|
self.cache.get("config")[name].__dict__.update(**update_data)
|
||||||
|
c = Config.update(**update_data)
|
||||||
|
self.emit("UPDATE_CONFIG", c)
|
||||||
|
|
||||||
|
|
||||||
def add_config_parameter(self, name, value, type, description, options=None):
|
def add_config_parameter(self, name, value, type, description, options=None):
|
||||||
from modules.config import Config
|
from modules.config import Config
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
@@ -264,28 +266,56 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
# helper method for parsing props
|
# helper method for parsing props
|
||||||
def __parseProps(self, key, cls):
|
def __parseProps(self, key, cls):
|
||||||
name = cls.__name__
|
name = cls.__name__
|
||||||
self.cache[key][name] = {"name": name, "class": cls, "properties": []}
|
self.cache[key][name] = {"name": name, "class": cls, "properties": [], "actions": []}
|
||||||
tmpObj = cls()
|
tmpObj = cls()
|
||||||
members = [attr for attr in dir(tmpObj) if not callable(getattr(tmpObj, attr)) and not attr.startswith("__")]
|
members = [attr for attr in dir(tmpObj) if not callable(getattr(tmpObj, attr)) and not attr.startswith("__")]
|
||||||
for m in members:
|
for m in members:
|
||||||
if isinstance(tmpObj.__getattribute__(m), Property.Number):
|
if isinstance(tmpObj.__getattribute__(m), Property.Number):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append(
|
self.cache[key][name]["properties"].append(
|
||||||
{"name": m, "label": t.label, "type": "number", "configurable": t.configurable})
|
{"name": m, "label": t.label, "type": "number", "configurable": t.configurable, "description": t.description, "default_value": t.default_value})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), Property.Text):
|
elif isinstance(tmpObj.__getattribute__(m), Property.Text):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append(
|
self.cache[key][name]["properties"].append(
|
||||||
{"name": m, "label": t.label, "type": "text", "configurable": t.configurable})
|
{"name": m, "label": t.label, "type": "text", "configurable": t.configurable, "default_value": t.default_value, "description": t.description})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), Property.Select):
|
elif isinstance(tmpObj.__getattribute__(m), Property.Select):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append(
|
self.cache[key][name]["properties"].append(
|
||||||
{"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options})
|
{"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options, "description": t.description})
|
||||||
|
elif isinstance(tmpObj.__getattribute__(m), Property.Actor):
|
||||||
|
t = tmpObj.__getattribute__(m)
|
||||||
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "actor", "configurable": t.configurable, "description": t.description})
|
||||||
|
elif isinstance(tmpObj.__getattribute__(m), Property.Sensor):
|
||||||
|
t = tmpObj.__getattribute__(m)
|
||||||
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable, "description": t.description})
|
||||||
|
elif isinstance(tmpObj.__getattribute__(m), Property.Kettle):
|
||||||
|
t = tmpObj.__getattribute__(m)
|
||||||
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable, "description": t.description})
|
||||||
|
|
||||||
|
for name, method in cls.__dict__.iteritems():
|
||||||
|
if hasattr(method, "action"):
|
||||||
|
label = method.__getattribute__("label")
|
||||||
|
self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label})
|
||||||
|
|
||||||
|
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
def actor(self, cls):
|
def actor(self, cls):
|
||||||
return self.__parseProps("actor_types", cls)
|
return self.__parseProps("actor_types", cls)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def actor2(self, description="", power=True, **options):
|
||||||
|
|
||||||
|
def decorator(f):
|
||||||
|
print f()
|
||||||
|
print f
|
||||||
|
print options
|
||||||
|
print description
|
||||||
|
return f
|
||||||
|
return decorator
|
||||||
|
|
||||||
def sensor(self, cls):
|
def sensor(self, cls):
|
||||||
return self.__parseProps("sensor_types", cls)
|
return self.__parseProps("sensor_types", cls)
|
||||||
|
|
||||||
@@ -300,6 +330,8 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
|
|
||||||
def get_fermentation_controller(self, name):
|
def get_fermentation_controller(self, name):
|
||||||
return self.cache["fermentation_controller_types"].get(name)
|
return self.cache["fermentation_controller_types"].get(name)
|
||||||
|
|
||||||
|
|
||||||
# Step action
|
# Step action
|
||||||
def action(self,label):
|
def action(self,label):
|
||||||
def real_decorator(func):
|
def real_decorator(func):
|
||||||
@@ -320,22 +352,22 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
for m in members:
|
for m in members:
|
||||||
if isinstance(tmpObj.__getattribute__(m), StepProperty.Number):
|
if isinstance(tmpObj.__getattribute__(m), StepProperty.Number):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable})
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "number", "configurable": t.configurable, "default_value": t.default_value, "description": t.description})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Text):
|
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Text):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "text", "configurable": t.configurable})
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "text", "configurable": t.configurable, "default_value": t.default_value, "description": t.description})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Select):
|
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Select):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "select", "options": t.options})
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options, "description": t.description})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Actor):
|
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Actor):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "actor", "configurable": t.configurable})
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "actor", "configurable": t.configurable, "description": t.description})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Sensor):
|
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Sensor):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable})
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable, "description": t.description})
|
||||||
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Kettle):
|
elif isinstance(tmpObj.__getattribute__(m), StepProperty.Kettle):
|
||||||
t = tmpObj.__getattribute__(m)
|
t = tmpObj.__getattribute__(m)
|
||||||
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable})
|
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable, "description": t.description})
|
||||||
|
|
||||||
for name, method in cls.__dict__.iteritems():
|
for name, method in cls.__dict__.iteritems():
|
||||||
if hasattr(method, "action"):
|
if hasattr(method, "action"):
|
||||||
@@ -420,7 +452,7 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
self.app.logger.info("Invoke Init")
|
self.app.logger.info("Invoke Init")
|
||||||
self.cache["init"] = sorted(self.cache["init"], key=lambda k: k['order'])
|
self.cache["init"] = sorted(self.cache["init"], key=lambda k: k['order'])
|
||||||
for i in self.cache.get("init"):
|
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)
|
i.get("function")(self)
|
||||||
|
|
||||||
|
|
||||||
@@ -451,7 +483,7 @@ class CraftBeerPi(ActorAPI, SensorAPI):
|
|||||||
def job(interval, method):
|
def job(interval, method):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
method()
|
method(self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.app.logger.error("Exception" + method.__name__ + ": " + str(e))
|
self.app.logger.error("Exception" + method.__name__ + ": " + str(e))
|
||||||
self.socketio.sleep(interval)
|
self.socketio.sleep(interval)
|
||||||
|
|||||||
+4
-2
@@ -42,13 +42,15 @@ class DBModel(object):
|
|||||||
def get_all(cls):
|
def get_all(cls):
|
||||||
cur = get_db().cursor()
|
cur = get_db().cursor()
|
||||||
if cls.__order_by__ is not None:
|
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:
|
else:
|
||||||
cur.execute("SELECT * FROM %s" % cls.__table_name__)
|
cur.execute("SELECT * FROM %s" % cls.__table_name__)
|
||||||
|
|
||||||
if cls.__as_array__ is True:
|
if cls.__as_array__ is True:
|
||||||
result = []
|
result = []
|
||||||
for r in cur.fetchall():
|
for r in cur.fetchall():
|
||||||
|
|
||||||
result.append( cls(r))
|
result.append( cls(r))
|
||||||
else:
|
else:
|
||||||
result = {}
|
result = {}
|
||||||
@@ -104,7 +106,7 @@ class DBModel(object):
|
|||||||
else:
|
else:
|
||||||
data = data + (kwargs.get(f),)
|
data = data + (kwargs.get(f),)
|
||||||
|
|
||||||
print query, data
|
|
||||||
cur.execute(query, data)
|
cur.execute(query, data)
|
||||||
get_db().commit()
|
get_db().commit()
|
||||||
i = cur.lastrowid
|
i = cur.lastrowid
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
from modules import cbpi
|
||||||
|
from db import get_db
|
||||||
|
|
||||||
|
def execute_file(curernt_version, data):
|
||||||
|
if curernt_version >= data["version"]:
|
||||||
|
cbpi.app.logger.info("SKIP DB FILE: %s" % data["file"])
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with sqlite3.connect("craftbeerpi.db") as conn:
|
||||||
|
with open('./update/%s' % data["file"], 'r') as f:
|
||||||
|
d = f.read()
|
||||||
|
sqlCommands = d.split(";")
|
||||||
|
cur = conn.cursor()
|
||||||
|
for s in sqlCommands:
|
||||||
|
cur.execute(s)
|
||||||
|
cur.execute("INSERT INTO schema_info (version,filename) values (?,?)", (data["version"], data["file"]))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
except sqlite3.OperationalError as err:
|
||||||
|
print "EXCEPT"
|
||||||
|
print err
|
||||||
|
|
||||||
|
@cbpi.initalizer(order=-9999)
|
||||||
|
def init(app=None):
|
||||||
|
|
||||||
|
with cbpi.app.app_context():
|
||||||
|
conn = get_db()
|
||||||
|
cur = conn.cursor()
|
||||||
|
current_version = None
|
||||||
|
try:
|
||||||
|
cur.execute("SELECT max(version) as m FROM schema_info")
|
||||||
|
m = cur.fetchone()
|
||||||
|
current_version = m["m"]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
result = []
|
||||||
|
for filename in os.listdir("./update"):
|
||||||
|
if filename.endswith(".sql"):
|
||||||
|
d = {"version": int(filename[:filename.index('_')]), "file": filename}
|
||||||
|
result.append(d)
|
||||||
|
execute_file(current_version, d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -5,16 +5,19 @@ class Base(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init_global(cls):
|
def init_global(cls):
|
||||||
print "GLOBAL ACTOR INIT"
|
pass
|
||||||
|
|
||||||
def get_config_parameter(self, key, default_value):
|
def get_config_parameter(self, key, default_value):
|
||||||
return self.api.get_config_parameter(key, default_value)
|
return self.api.get_config_parameter(key, default_value)
|
||||||
|
|
||||||
|
def sleep(self, seconds):
|
||||||
|
self.api.socketio.sleep(seconds)
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
print "INIT BASE"
|
pass
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
print "STOP HARDWARE"
|
pass
|
||||||
|
|
||||||
def update(self, **kwds):
|
def update(self, **kwds):
|
||||||
pass
|
pass
|
||||||
@@ -67,15 +70,16 @@ class SensorActive(SensorBase):
|
|||||||
__running = False
|
__running = False
|
||||||
|
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
|
|
||||||
return self.__running
|
return self.__running
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self.__running = True
|
self.__running = True
|
||||||
print "INIT ACTIVE SENSOR"
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.__running = False
|
self.__running = False
|
||||||
|
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -95,10 +99,10 @@ class ActorBase(Base):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
def set_power(self, power):
|
def set_power(self, power):
|
||||||
print "SET POWER TO %s" % power
|
pass
|
||||||
|
|
||||||
def on(self, power=0):
|
def on(self, power=0):
|
||||||
print "ON"
|
pass
|
||||||
|
|
||||||
def off(self):
|
def off(self):
|
||||||
print "OFF"
|
pass
|
||||||
|
|||||||
+37
-7
@@ -3,36 +3,66 @@ class PropertyType(object):
|
|||||||
|
|
||||||
class Property(object):
|
class Property(object):
|
||||||
class Select(PropertyType):
|
class Select(PropertyType):
|
||||||
def __init__(self, label, options):
|
def __init__(self, label, options, description=""):
|
||||||
PropertyType.__init__(self)
|
PropertyType.__init__(self)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.options = options
|
self.options = options
|
||||||
|
self.description = description
|
||||||
|
|
||||||
class Number(PropertyType):
|
class Number(PropertyType):
|
||||||
def __init__(self, label, configurable=False, default_value=0, unit=""):
|
def __init__(self, label, configurable=False, default_value=None, unit="", description=""):
|
||||||
PropertyType.__init__(self)
|
PropertyType.__init__(self)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.configurable = configurable
|
self.configurable = configurable
|
||||||
|
self.default_value = default_value
|
||||||
|
self.description = description
|
||||||
|
|
||||||
class Text(PropertyType):
|
class Text(PropertyType):
|
||||||
def __init__(self, label, configurable=False, default_value=""):
|
def __init__(self, label, configurable=False, default_value="", description=""):
|
||||||
PropertyType.__init__(self)
|
PropertyType.__init__(self)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.configurable = configurable
|
self.configurable = configurable
|
||||||
|
self.default_value = default_value
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
class Actor(PropertyType):
|
||||||
|
def __init__(self, label, description=""):
|
||||||
|
PropertyType.__init__(self)
|
||||||
|
self.label = label
|
||||||
|
self.configurable = True
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
class Sensor(PropertyType):
|
||||||
|
def __init__(self, label, description=""):
|
||||||
|
PropertyType.__init__(self)
|
||||||
|
self.label = label
|
||||||
|
self.configurable = True
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
class Kettle(PropertyType):
|
||||||
|
def __init__(self, label, description=""):
|
||||||
|
PropertyType.__init__(self)
|
||||||
|
self.label = label
|
||||||
|
self.configurable = True
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
|
||||||
class StepProperty(Property):
|
class StepProperty(Property):
|
||||||
class Actor(PropertyType):
|
class Actor(PropertyType):
|
||||||
def __init__(self, label):
|
def __init__(self, label, description=""):
|
||||||
PropertyType.__init__(self)
|
PropertyType.__init__(self)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.configurable = True
|
self.configurable = True
|
||||||
|
self.description = description
|
||||||
class Sensor(PropertyType):
|
class Sensor(PropertyType):
|
||||||
def __init__(self, label):
|
def __init__(self, label, description=""):
|
||||||
PropertyType.__init__(self)
|
PropertyType.__init__(self)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.configurable = True
|
self.configurable = True
|
||||||
|
self.description = description
|
||||||
class Kettle(PropertyType):
|
class Kettle(PropertyType):
|
||||||
def __init__(self, label):
|
def __init__(self, label, description=""):
|
||||||
PropertyType.__init__(self)
|
PropertyType.__init__(self)
|
||||||
self.label = label
|
self.label = label
|
||||||
self.configurable = True
|
self.configurable = True
|
||||||
|
self.description = description
|
||||||
|
|||||||
+19
-5
@@ -17,6 +17,10 @@ class ActorAPI(NotificationAPI):
|
|||||||
def actor_off(self, id):
|
def actor_off(self, id):
|
||||||
self.api.switch_actor_off(int(id))
|
self.api.switch_actor_off(int(id))
|
||||||
|
|
||||||
|
@cbpi.try_catch(None)
|
||||||
|
def actor_power(self, id, power):
|
||||||
|
self.api.actor_power(int(id), power)
|
||||||
|
|
||||||
class SensorAPI(NotificationAPI):
|
class SensorAPI(NotificationAPI):
|
||||||
|
|
||||||
@cbpi.try_catch(None)
|
@cbpi.try_catch(None)
|
||||||
@@ -40,7 +44,7 @@ class KettleAPI(NotificationAPI):
|
|||||||
return self.api.cache.get("kettle").get(id).target_temp
|
return self.api.cache.get("kettle").get(id).target_temp
|
||||||
|
|
||||||
def set_target_temp(self, temp, id=None):
|
def set_target_temp(self, temp, id=None):
|
||||||
temp = int(temp)
|
temp = float(temp)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if id is None:
|
if id is None:
|
||||||
@@ -55,6 +59,7 @@ class Timer(object):
|
|||||||
timer_end = Property.Number("TIMER_END", configurable=False)
|
timer_end = Property.Number("TIMER_END", configurable=False)
|
||||||
|
|
||||||
def start_timer(self, timer):
|
def start_timer(self, timer):
|
||||||
|
|
||||||
if self.timer_end is not None:
|
if self.timer_end is not None:
|
||||||
return
|
return
|
||||||
self.timer_end = int(time.time()) + timer
|
self.timer_end = int(time.time()) + timer
|
||||||
@@ -70,6 +75,12 @@ class Timer(object):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def timer_remaining(self):
|
||||||
|
if self.timer_end is not None:
|
||||||
|
return self.timer_end - int(time.time())
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def is_timer_finished(self):
|
def is_timer_finished(self):
|
||||||
if self.timer_end is None:
|
if self.timer_end is None:
|
||||||
return None
|
return None
|
||||||
@@ -89,13 +100,13 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI):
|
|||||||
self.n = True
|
self.n = True
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
print "INIT STEP"
|
pass
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
print "FINSIH STEP"
|
pass
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
print "REST STEP"
|
pass
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
print "-------------"
|
print "-------------"
|
||||||
@@ -105,11 +116,14 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI):
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
def __init__(self, *args, **kwds):
|
||||||
|
|
||||||
for a in kwds:
|
for a in kwds:
|
||||||
|
|
||||||
super(StepBase, self).__setattr__(a, kwds.get(a))
|
super(StepBase, self).__setattr__(a, kwds.get(a))
|
||||||
|
|
||||||
|
|
||||||
self.api = kwds.get("api")
|
self.api = kwds.get("api")
|
||||||
self.id = kwds.get("id")
|
self.id = kwds.get("id")
|
||||||
|
|
||||||
self.name = kwds.get("name")
|
self.name = kwds.get("name")
|
||||||
self.kettle_id = kwds.get("kettleid")
|
self.kettle_id = kwds.get("kettleid")
|
||||||
self.value = None
|
self.value = None
|
||||||
|
|||||||
@@ -63,19 +63,21 @@ class FermenterView(BaseView):
|
|||||||
model = Fermenter
|
model = Fermenter
|
||||||
cache_key = "fermenter"
|
cache_key = "fermenter"
|
||||||
|
|
||||||
def post_post_callback(self, m):
|
|
||||||
|
def _post_post_callback(self, m):
|
||||||
m.state = False
|
m.state = False
|
||||||
m.steps = []
|
m.steps = []
|
||||||
|
|
||||||
def pre_put_callback(self, m):
|
def _pre_put_callback(self, m):
|
||||||
m.state = False
|
m.state = False
|
||||||
try:
|
try:
|
||||||
m.instance.stop()
|
m.instance.stop()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def post_put_callback(self, m):
|
def _post_put_callback(self, m):
|
||||||
m.state = False
|
m.state = False
|
||||||
|
self.reset(int(m.id))
|
||||||
|
|
||||||
@route('/<int:id>/targettemp/<temp>', methods=['POST'])
|
@route('/<int:id>/targettemp/<temp>', methods=['POST'])
|
||||||
def postTargetTemp(self, id, temp):
|
def postTargetTemp(self, id, temp):
|
||||||
@@ -282,7 +284,7 @@ class FermenterView(BaseView):
|
|||||||
|
|
||||||
|
|
||||||
@cbpi.backgroundtask(key="read_target_temps_fermenter", interval=5)
|
@cbpi.backgroundtask(key="read_target_temps_fermenter", interval=5)
|
||||||
def read_target_temps():
|
def read_target_temps(api):
|
||||||
"""
|
"""
|
||||||
background process that reads all passive sensors in interval of 1 second
|
background process that reads all passive sensors in interval of 1 second
|
||||||
:return: None
|
:return: None
|
||||||
@@ -295,7 +297,7 @@ def read_target_temps():
|
|||||||
instance = FermenterView()
|
instance = FermenterView()
|
||||||
|
|
||||||
@cbpi.backgroundtask(key="fermentation_task", interval=1)
|
@cbpi.backgroundtask(key="fermentation_task", interval=1)
|
||||||
def execute_fermentation_step():
|
def execute_fermentation_step(api):
|
||||||
with cbpi.app.app_context():
|
with cbpi.app.app_context():
|
||||||
instance.check_step()
|
instance.check_step()
|
||||||
|
|
||||||
@@ -310,6 +312,6 @@ def init_active_steps():
|
|||||||
|
|
||||||
@cbpi.initalizer(order=1)
|
@cbpi.initalizer(order=1)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE CONFIG MODULE"
|
|
||||||
FermenterView.register(cbpi.app, route_base='/api/fermenter')
|
FermenterView.register(cbpi.app, route_base='/api/fermenter')
|
||||||
FermenterView.init_cache()
|
FermenterView.init_cache()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Kettle2View(BaseView):
|
|||||||
cache_key = "kettle"
|
cache_key = "kettle"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def pre_post_callback(self, data):
|
def _pre_post_callback(self, data):
|
||||||
data["target_temp"] = 0
|
data["target_temp"] = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -23,16 +23,16 @@ class Kettle2View(BaseView):
|
|||||||
obj.state = False
|
obj.state = False
|
||||||
|
|
||||||
|
|
||||||
def post_post_callback(self, m):
|
def _post_post_callback(self, m):
|
||||||
m.state = False
|
m.state = False
|
||||||
|
|
||||||
def pre_put_callback(self, m):
|
def _pre_put_callback(self, m):
|
||||||
try:
|
try:
|
||||||
m.instance.stop()
|
m.instance.stop()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def post_put_callback(self, m):
|
def _post_put_callback(self, m):
|
||||||
m.state = False
|
m.state = False
|
||||||
|
|
||||||
@route('/<int:id>/targettemp/<temp>', methods=['POST'])
|
@route('/<int:id>/targettemp/<temp>', methods=['POST'])
|
||||||
@@ -76,11 +76,11 @@ def set_target_temp(id, temp):
|
|||||||
:param temp: target temp to set
|
:param temp: target temp to set
|
||||||
:return: None
|
:return: None
|
||||||
'''
|
'''
|
||||||
print "GOT EVENT %s %s" % (id, temp)
|
|
||||||
Kettle2View().postTargetTemp(id,temp)
|
Kettle2View().postTargetTemp(id,temp)
|
||||||
|
|
||||||
@cbpi.backgroundtask(key="read_target_temps", interval=5)
|
@cbpi.backgroundtask(key="read_target_temps", interval=5)
|
||||||
def read_target_temps():
|
def read_target_temps(api):
|
||||||
"""
|
"""
|
||||||
background process that reads all passive sensors in interval of 1 second
|
background process that reads all passive sensors in interval of 1 second
|
||||||
:return: None
|
:return: None
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ class LogView(FlaskView):
|
|||||||
pass
|
pass
|
||||||
return array
|
return array
|
||||||
|
|
||||||
|
def convert_chart_data_to_json(self, chart_data):
|
||||||
|
return {"name": chart_data["name"], "data": self.read_log_as_json(chart_data["data_type"], chart_data["data_id"])}
|
||||||
|
|
||||||
@route('/<t>/<int:id>', methods=["POST"])
|
@route('/<t>/<int:id>', methods=["POST"])
|
||||||
def get_logs_as_json(self, t, id):
|
def get_logs_as_json(self, t, id):
|
||||||
@@ -76,13 +78,12 @@ class LogView(FlaskView):
|
|||||||
|
|
||||||
if t == "k":
|
if t == "k":
|
||||||
kettle = cbpi.cache.get("kettle").get(id)
|
kettle = cbpi.cache.get("kettle").get(id)
|
||||||
result.append({"name": "Temp", "data": self.read_log_as_json("sensor", kettle.sensor)})
|
result = map(self.convert_chart_data_to_json, cbpi.get_controller(kettle.logic).get("class").chart(kettle))
|
||||||
result.append({"name": "Target Temp", "data": self.read_log_as_json("kettle", kettle.id)})
|
|
||||||
|
|
||||||
if t == "f":
|
if t == "f":
|
||||||
fermenter = cbpi.cache.get("fermenter").get(id)
|
fermenter = cbpi.cache.get("fermenter").get(id)
|
||||||
result.append({"name": "Temp", "data": self.read_log_as_json("sensor", fermenter.sensor)})
|
result = map(self.convert_chart_data_to_json, cbpi.get_fermentation_controller(fermenter.logic).get("class").chart(fermenter))
|
||||||
result.append({"name": "Target Temp", "data": self.read_log_as_json("fermenter", fermenter.id)})
|
|
||||||
return json.dumps(result)
|
return json.dumps(result)
|
||||||
|
|
||||||
@route('/download/<file>')
|
@route('/download/<file>')
|
||||||
@@ -105,5 +106,4 @@ def init(app):
|
|||||||
:param app: the flask app
|
:param app: the flask app
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
print "INITIALIZE LOG MODULE"
|
|
||||||
LogView.register(cbpi.app, route_base='/api/logs')
|
LogView.register(cbpi.app, route_base='/api/logs')
|
||||||
|
|||||||
@@ -32,10 +32,9 @@ def messageEvent(message, **kwargs):
|
|||||||
:param kwargs: other parameter
|
:param kwargs: other parameter
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
if message["timeout"] is None:
|
||||||
msg = {"id": len(cbpi.cache["messages"]), "type": "info", "message": message, "read": False}
|
cbpi.cache["messages"].append(message)
|
||||||
cbpi.cache["messages"].append(msg)
|
cbpi.emit("NOTIFY", message)
|
||||||
cbpi.emit('MESSAGE', msg,)
|
|
||||||
|
|
||||||
@cbpi.initalizer(order=2)
|
@cbpi.initalizer(order=2)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
@@ -44,9 +43,8 @@ def init(cbpi):
|
|||||||
:param app: the flask app
|
:param app: the flask app
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
print "INITIALIZE MESSAGE MODULE"
|
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}
|
||||||
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)
|
||||||
cbpi.cache["messages"].append(msg)
|
|
||||||
|
|
||||||
NotificationView.register(cbpi.app, route_base='/api/notification')
|
NotificationView.register(cbpi.app, route_base='/api/notification')
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import beerxml
|
||||||
|
import kbh
|
||||||
|
import restapi
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
from flask import json, request
|
||||||
|
from flask_classy import FlaskView, route
|
||||||
|
from git import Repo, Git
|
||||||
|
import sqlite3
|
||||||
|
from modules.app_config import cbpi
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import pprint
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
from modules.steps import Step,StepView
|
||||||
|
import xml.etree.ElementTree
|
||||||
|
|
||||||
|
class BeerXMLImport(FlaskView):
|
||||||
|
BEER_XML_FILE = "./upload/beer.xml"
|
||||||
|
@route('/', methods=['GET'])
|
||||||
|
def get(self):
|
||||||
|
if not os.path.exists(self.BEER_XML_FILE):
|
||||||
|
self.api.notify(headline="File Not Found", message="Please upload a Beer.xml File",
|
||||||
|
type="danger")
|
||||||
|
return ('', 404)
|
||||||
|
result = []
|
||||||
|
|
||||||
|
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
|
||||||
|
result = []
|
||||||
|
for idx, val in enumerate(e.findall('RECIPE')):
|
||||||
|
result.append({"id": idx+1, "name": val.find("NAME").text})
|
||||||
|
return json.dumps(result)
|
||||||
|
|
||||||
|
def allowed_file(self, filename):
|
||||||
|
return '.' in filename and filename.rsplit('.', 1)[1] in set(['xml'])
|
||||||
|
|
||||||
|
@route('/upload', methods=['POST'])
|
||||||
|
def upload_file(self):
|
||||||
|
try:
|
||||||
|
if request.method == 'POST':
|
||||||
|
file = request.files['file']
|
||||||
|
if file and self.allowed_file(file.filename):
|
||||||
|
file.save(os.path.join(self.api.app.config['UPLOAD_FOLDER'], "beer.xml"))
|
||||||
|
self.api.notify(headline="Upload Successful", message="The Beer XML file was uploaded succesfully")
|
||||||
|
return ('', 204)
|
||||||
|
return ('', 404)
|
||||||
|
except Exception as e:
|
||||||
|
self.api.notify(headline="Upload Failed", message="Failed to upload Beer xml", type="danger")
|
||||||
|
return ('', 500)
|
||||||
|
|
||||||
|
@route('/<int:id>', methods=['POST'])
|
||||||
|
def load(self, id):
|
||||||
|
|
||||||
|
|
||||||
|
steps = self.getSteps(id)
|
||||||
|
boil_time_alerts = self.getBoilAlerts(id)
|
||||||
|
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)
|
||||||
|
|
||||||
|
boilstep_type = cbpi.get_config_parameter("step_boil", "BoilStep")
|
||||||
|
boil_kettle = cbpi.get_config_parameter("step_boil_kettle", None)
|
||||||
|
boil_temp = 100 if cbpi.get_config_parameter("unit", "C") == "C" else 212
|
||||||
|
|
||||||
|
# READ KBH DATABASE
|
||||||
|
Step.delete_all()
|
||||||
|
StepView().reset()
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
for row in steps:
|
||||||
|
Step.insert(**{"name": row.get("name"), "type": mashstep_type, "config": {"kettle": mash_kettle, "temp": float(row.get("temp")), "timer": row.get("timer")}})
|
||||||
|
Step.insert(**{"name": "ChilStep", "type": "ChilStep", "config": {"timer": 15}})
|
||||||
|
## Add boiling step
|
||||||
|
Step.insert(**{
|
||||||
|
"name": "Boil",
|
||||||
|
"type": boilstep_type,
|
||||||
|
"config": {
|
||||||
|
"kettle": boil_kettle,
|
||||||
|
"temp": boil_temp,
|
||||||
|
"timer": boil_time,
|
||||||
|
## Beer XML defines additions as the total time spent in boiling,
|
||||||
|
## CBP defines it as time-until-alert
|
||||||
|
|
||||||
|
## Also, The model supports five boil-time additions.
|
||||||
|
## Set the rest to None to signal them being absent
|
||||||
|
"hop_1": boil_time - boil_time_alerts[0] if len(boil_time_alerts) >= 1 else None,
|
||||||
|
"hop_2": boil_time - boil_time_alerts[1] if len(boil_time_alerts) >= 2 else None,
|
||||||
|
"hop_3": boil_time - boil_time_alerts[2] if len(boil_time_alerts) >= 3 else None,
|
||||||
|
"hop_4": boil_time - boil_time_alerts[3] if len(boil_time_alerts) >= 4 else None,
|
||||||
|
"hop_5": boil_time - boil_time_alerts[4] if len(boil_time_alerts) >= 5 else None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
## Add Whirlpool step
|
||||||
|
Step.insert(**{"name": "Whirlpool", "type": "ChilStep", "config": {"timer": 15}})
|
||||||
|
StepView().reset()
|
||||||
|
self.api.emit("UPDATE_ALL_STEPS", Step.get_all())
|
||||||
|
self.api.notify(headline="Recipe %s loaded successfully" % name, message="")
|
||||||
|
except Exception as e:
|
||||||
|
self.api.notify(headline="Failed to load Recipe", message=e.message, type="danger")
|
||||||
|
return ('', 500)
|
||||||
|
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
|
def getRecipeName(self, id):
|
||||||
|
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
|
||||||
|
return e.find('./RECIPE[%s]/NAME' % (str(id))).text
|
||||||
|
|
||||||
|
def getBoilTime(self, id):
|
||||||
|
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
|
||||||
|
return float(e.find('./RECIPE[%s]/BOIL_TIME' % (str(id))).text)
|
||||||
|
|
||||||
|
def getBoilAlerts(self, id):
|
||||||
|
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
|
||||||
|
|
||||||
|
recipe = e.find('./RECIPE[%s]' % (str(id)))
|
||||||
|
alerts = []
|
||||||
|
for e in recipe.findall('./HOPS/HOP'):
|
||||||
|
use = e.find('USE').text
|
||||||
|
## Hops which are not used in the boil step should not cause alerts
|
||||||
|
if use != 'Aroma' and use != 'Boil':
|
||||||
|
continue
|
||||||
|
|
||||||
|
alerts.append(float(e.find('TIME').text))
|
||||||
|
|
||||||
|
## There might also be miscelaneous additions during boild time
|
||||||
|
for e in recipe.findall('MISCS/MISC[USE="Boil"]'):
|
||||||
|
alerts.append(float(e.find('TIME').text))
|
||||||
|
|
||||||
|
## Dedupe and order the additions by their time, to prevent multiple alerts at the same time
|
||||||
|
alerts = sorted(list(set(alerts)))
|
||||||
|
|
||||||
|
## CBP should have these additions in reverse
|
||||||
|
alerts.reverse()
|
||||||
|
|
||||||
|
return alerts
|
||||||
|
|
||||||
|
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))):
|
||||||
|
if self.api.get_config_parameter("unit", "C") == "C":
|
||||||
|
temp = float(e.find("STEP_TEMP").text)
|
||||||
|
else:
|
||||||
|
temp = round(9.0 / 5.0 * float(e.find("STEP_TEMP").text) + 32, 2)
|
||||||
|
|
||||||
|
steps.append({"name": e.find("NAME").text, "temp": temp, "timer": float(e.find("STEP_TIME").text)})
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
@cbpi.initalizer()
|
||||||
|
def init(cbpi):
|
||||||
|
|
||||||
|
BeerXMLImport.api = cbpi
|
||||||
|
BeerXMLImport.register(cbpi.app, route_base='/api/beerxml')
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
from flask import json, request
|
||||||
|
from flask_classy import FlaskView, route
|
||||||
|
from git import Repo, Git
|
||||||
|
import sqlite3
|
||||||
|
from modules.app_config import cbpi
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import pprint
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
from modules.steps import Step, StepView
|
||||||
|
|
||||||
|
|
||||||
|
class KBH(FlaskView):
|
||||||
|
|
||||||
|
@route('/', methods=['GET'])
|
||||||
|
def get(self):
|
||||||
|
conn = None
|
||||||
|
try:
|
||||||
|
if not os.path.exists(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db'):
|
||||||
|
self.api.notify(headline="File Not Found", message="Please upload a Kleiner Brauhelfer Database", type="danger")
|
||||||
|
return ('', 404)
|
||||||
|
|
||||||
|
conn = sqlite3.connect(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db')
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('SELECT ID, Sudname, BierWurdeGebraut FROM Sud')
|
||||||
|
data = c.fetchall()
|
||||||
|
result = []
|
||||||
|
for row in data:
|
||||||
|
result.append({"id": row[0], "name": row[1], "brewed": row[2]})
|
||||||
|
return json.dumps(result)
|
||||||
|
except Exception as e:
|
||||||
|
print e
|
||||||
|
self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger")
|
||||||
|
return ('', 500)
|
||||||
|
finally:
|
||||||
|
if conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def allowed_file(self, filename):
|
||||||
|
return '.' in filename and filename.rsplit('.', 1)[1] in set(['sqlite'])
|
||||||
|
|
||||||
|
@route('/upload', methods=['POST'])
|
||||||
|
def upload_file(self):
|
||||||
|
try:
|
||||||
|
if request.method == 'POST':
|
||||||
|
file = request.files['file']
|
||||||
|
if file and self.allowed_file(file.filename):
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
file.save(os.path.join(self.api.app.config['UPLOAD_FOLDER'], "kbh.db"))
|
||||||
|
self.api.notify(headline="Upload Successful", message="The Kleiner Brauhelfer Database was uploaded succesfully")
|
||||||
|
return ('', 204)
|
||||||
|
return ('', 404)
|
||||||
|
except Exception as e:
|
||||||
|
self.api.notify(headline="Upload Failed", message="Failed to upload Kleiner Brauhelfer", type="danger")
|
||||||
|
|
||||||
|
return ('', 500)
|
||||||
|
|
||||||
|
@route('/<int:id>', methods=['POST'])
|
||||||
|
def load(self, id):
|
||||||
|
mashstep_type = cbpi.get_config_parameter("step_mash", "MashStep")
|
||||||
|
mashinstep_type = cbpi.get_config_parameter("step_mashin", "MashInStep")
|
||||||
|
chilstep_type = cbpi.get_config_parameter("step_chil", "ChilStep")
|
||||||
|
boilstep_type = cbpi.get_config_parameter("step_boil", "BoilStep")
|
||||||
|
mash_kettle = cbpi.get_config_parameter("step_mash_kettle", None)
|
||||||
|
boil_kettle = cbpi.get_config_parameter("step_boil_kettle", None)
|
||||||
|
boil_temp = 100 if cbpi.get_config_parameter("unit", "C") == "C" else 212
|
||||||
|
|
||||||
|
# READ KBH DATABASE
|
||||||
|
Step.delete_all()
|
||||||
|
StepView().reset()
|
||||||
|
conn = None
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(self.api.app.config['UPLOAD_FOLDER'] + '/kbh.db')
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('SELECT EinmaischenTemp, Sudname FROM Sud WHERE ID = ?', (id,))
|
||||||
|
row = c.fetchone()
|
||||||
|
name = row[1]
|
||||||
|
|
||||||
|
self.api.set_config_parameter("brew_name", name)
|
||||||
|
Step.insert(**{"name": "MashIn", "type": mashinstep_type, "config": {"kettle": mash_kettle, "temp": row[0]}})
|
||||||
|
### add rest step
|
||||||
|
for row in c.execute('SELECT * FROM Rasten WHERE SudID = ?', (id,)):
|
||||||
|
Step.insert(**{"name": row[5], "type": mashstep_type, "config": {"kettle": mash_kettle, "temp": row[3], "timer": row[4]}})
|
||||||
|
Step.insert(**{"name": "Chil", "type": chilstep_type, "config": {"timer": 15}})
|
||||||
|
## Add cooking step
|
||||||
|
c.execute('SELECT max(Zeit) FROM Hopfengaben WHERE SudID = ?', (id,))
|
||||||
|
row = c.fetchone()
|
||||||
|
Step.insert(**{"name": "Boil", "type": boilstep_type, "config": {"kettle": boil_kettle, "temp": boil_temp, "timer": row[0]}})
|
||||||
|
## Add Whirlpool step
|
||||||
|
Step.insert(**{"name": "Whirlpool", "type": chilstep_type, "config": {"timer": 15}})
|
||||||
|
|
||||||
|
#setBrewName(name)
|
||||||
|
self.api.emit("UPDATE_ALL_STEPS", Step.get_all())
|
||||||
|
self.api.notify(headline="Recipe %s loaded successfully" % name, message="")
|
||||||
|
except Exception as e:
|
||||||
|
self.api.notify(headline="Failed to load Recipe", message=e.message, type="danger")
|
||||||
|
return ('', 500)
|
||||||
|
finally:
|
||||||
|
if conn:
|
||||||
|
conn.close()
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@cbpi.initalizer()
|
||||||
|
def init(cbpi):
|
||||||
|
|
||||||
|
KBH.api = cbpi
|
||||||
|
KBH.register(cbpi.app, route_base='/api/kbh')
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
from flask import json, request
|
||||||
|
from flask_classy import FlaskView, route
|
||||||
|
from git import Repo, Git
|
||||||
|
import sqlite3
|
||||||
|
from modules.app_config import cbpi
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import pprint
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
from modules.steps import Step,StepView
|
||||||
|
import xml.etree.ElementTree
|
||||||
|
|
||||||
|
|
||||||
|
class RESTImport(FlaskView):
|
||||||
|
|
||||||
|
|
||||||
|
@route('/', methods=['POST'])
|
||||||
|
def load(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = request.json
|
||||||
|
|
||||||
|
name = data.get("name", "No Name")
|
||||||
|
|
||||||
|
self.api.set_config_parameter("brew_name", name)
|
||||||
|
chilstep_type = cbpi.get_config_parameter("step_chil", "ChilStep")
|
||||||
|
mashstep_type = cbpi.get_config_parameter("step_mash", "MashStep")
|
||||||
|
mash_kettle = cbpi.get_config_parameter("step_mash_kettle", None)
|
||||||
|
|
||||||
|
boilstep_type = cbpi.get_config_parameter("step_boil", "BoilStep")
|
||||||
|
boil_kettle = cbpi.get_config_parameter("step_boil_kettle", None)
|
||||||
|
boil_temp = 100 if cbpi.get_config_parameter("unit", "C") == "C" else 212
|
||||||
|
|
||||||
|
# READ KBH DATABASE
|
||||||
|
Step.delete_all()
|
||||||
|
StepView().reset()
|
||||||
|
|
||||||
|
|
||||||
|
for step in data.get("steps"):
|
||||||
|
if step.get("type", None) == "MASH":
|
||||||
|
Step.insert(**{"name": step.get("name","Mash Step"), "type": mashstep_type, "config": {"kettle": mash_kettle, "temp": step.get("temp",0), "timer": step.get("timer",0)}})
|
||||||
|
elif step.get("type", None) == "CHIL":
|
||||||
|
Step.insert(**{"name": step.get("name","Chil"), "type": chilstep_type, "config": {"timer": step.get("timer")}})
|
||||||
|
elif step.get("type", None) == "BOIL":
|
||||||
|
Step.insert(**{"name": step.get("name", "Boil"), "type": boilstep_type, "config": {"kettle": boil_kettle, "timer": step.get("timer"), "temp": boil_temp}})
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.api.emit("UPDATE_ALL_STEPS", Step.get_all())
|
||||||
|
self.api.notify(headline="Recipe %s loaded successfully" % name, message="")
|
||||||
|
except Exception as e:
|
||||||
|
self.api.notify(headline="Failed to load Recipe", type="danger", message=str(e))
|
||||||
|
m = str(e.message)
|
||||||
|
return (str(e), 500)
|
||||||
|
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
|
|
||||||
|
@cbpi.initalizer()
|
||||||
|
def init(cbpi):
|
||||||
|
RESTImport.api = cbpi
|
||||||
|
RESTImport.register(cbpi.app, route_base='/api/recipe/import/v1')
|
||||||
@@ -12,27 +12,32 @@ class SensorView(BaseView):
|
|||||||
model = Sensor
|
model = Sensor
|
||||||
cache_key = "sensors"
|
cache_key = "sensors"
|
||||||
|
|
||||||
|
@route('<int:id>/action/<method>', methods=["POST"])
|
||||||
|
def action(self, id, method):
|
||||||
|
|
||||||
def post_post_callback(self, m):
|
cbpi.cache.get("sensors").get(id).instance.__getattribute__(method)()
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
|
def _post_post_callback(self, m):
|
||||||
cbpi.init_sensor(m.id)
|
cbpi.init_sensor(m.id)
|
||||||
|
|
||||||
def post_put_callback(self, m):
|
def _post_put_callback(self, m):
|
||||||
cbpi.stop_sensor(m.id)
|
cbpi.stop_sensor(m.id)
|
||||||
cbpi.init_sensor(m.id)
|
cbpi.init_sensor(m.id)
|
||||||
|
|
||||||
def pre_delete_callback(self, m):
|
def _pre_delete_callback(self, m):
|
||||||
cbpi.stop_sensor(m.id)
|
cbpi.stop_sensor(m.id)
|
||||||
|
|
||||||
@cbpi.initalizer(order=1000)
|
@cbpi.initalizer(order=1000)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE SENSOR MODULE"
|
|
||||||
SensorView.register(cbpi.app, route_base='/api/sensor')
|
SensorView.register(cbpi.app, route_base='/api/sensor')
|
||||||
SensorView.init_cache()
|
SensorView.init_cache()
|
||||||
cbpi.init_sensors()
|
cbpi.init_sensors()
|
||||||
|
|
||||||
|
|
||||||
@cbpi.backgroundtask(key="read_passiv_sensor", interval=5)
|
@cbpi.backgroundtask(key="read_passiv_sensor", interval=5)
|
||||||
def read_passive_sensor():
|
def read_passive_sensor(api):
|
||||||
"""
|
"""
|
||||||
background process that reads all passive sensors in interval of 1 second
|
background process that reads all passive sensors in interval of 1 second
|
||||||
:return: None
|
:return: None
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def getserial():
|
|||||||
|
|
||||||
@cbpi.initalizer(order=9999)
|
@cbpi.initalizer(order=9999)
|
||||||
def sendStats(cbpi):
|
def sendStats(cbpi):
|
||||||
print "INITIALIZE STATS"
|
|
||||||
try:
|
try:
|
||||||
serial = getserial()
|
serial = getserial()
|
||||||
|
|
||||||
|
|||||||
+24
-15
@@ -1,5 +1,5 @@
|
|||||||
import time
|
import time
|
||||||
from flask import json
|
from flask import json, request
|
||||||
from flask_classy import route
|
from flask_classy import route
|
||||||
|
|
||||||
from modules import DBModel, cbpi, get_db
|
from modules import DBModel, cbpi, get_db
|
||||||
@@ -21,9 +21,9 @@ class Step(DBModel):
|
|||||||
return r.get("order")
|
return r.get("order")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_state(cls, state):
|
def get_by_state(cls, state, order=True):
|
||||||
cur = get_db().cursor()
|
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()
|
r = cur.fetchone()
|
||||||
if r is not None:
|
if r is not None:
|
||||||
return cls(r)
|
return cls(r)
|
||||||
@@ -54,13 +54,29 @@ class Step(DBModel):
|
|||||||
cur.execute("UPDATE %s SET stepstate = ? WHERE id =?" % cls.__table_name__, (json.dumps(state),id))
|
cur.execute("UPDATE %s SET stepstate = ? WHERE id =?" % cls.__table_name__, (json.dumps(state),id))
|
||||||
get_db().commit()
|
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):
|
class StepView(BaseView):
|
||||||
model = Step
|
model = Step
|
||||||
def pre_post_callback(self, data):
|
def _pre_post_callback(self, data):
|
||||||
order = self.model.get_max_order()
|
order = self.model.get_max_order()
|
||||||
data["order"] = 1 if order is None else order + 1
|
data["order"] = 1 if order is None else order + 1
|
||||||
data["state"] = "I"
|
data["state"] = "I"
|
||||||
|
|
||||||
|
@route('/sort', methods=["POST"])
|
||||||
|
def sort_steps(self):
|
||||||
|
Step.sort(request.json)
|
||||||
|
cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
@route('/', methods=["DELETE"])
|
@route('/', methods=["DELETE"])
|
||||||
def deleteAll(self):
|
def deleteAll(self):
|
||||||
self.model.delete_all()
|
self.model.delete_all()
|
||||||
@@ -69,16 +85,12 @@ class StepView(BaseView):
|
|||||||
|
|
||||||
@route('/action/<method>', methods=["POST"])
|
@route('/action/<method>', methods=["POST"])
|
||||||
def action(self, method):
|
def action(self, method):
|
||||||
|
|
||||||
cbpi.cache["active_step"].__getattribute__(method)()
|
cbpi.cache["active_step"].__getattribute__(method)()
|
||||||
|
|
||||||
return ('', 204)
|
return ('', 204)
|
||||||
|
|
||||||
@route('/reset', methods=["POST"])
|
@route('/reset', methods=["POST"])
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
|
||||||
self.model.reset_all_steps()
|
self.model.reset_all_steps()
|
||||||
#db.session.commit()
|
|
||||||
self.stop_step()
|
self.stop_step()
|
||||||
cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
|
cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
|
||||||
return ('', 204)
|
return ('', 204)
|
||||||
@@ -115,7 +127,6 @@ class StepView(BaseView):
|
|||||||
return ('', 204)
|
return ('', 204)
|
||||||
|
|
||||||
def init_step(self, step):
|
def init_step(self, step):
|
||||||
|
|
||||||
cbpi.log_action("Start Step %s" % step.name)
|
cbpi.log_action("Start Step %s" % step.name)
|
||||||
type_cfg = cbpi.cache.get("step_types").get(step.type)
|
type_cfg = cbpi.cache.get("step_types").get(step.type)
|
||||||
if type_cfg is None:
|
if type_cfg is None:
|
||||||
@@ -128,7 +139,6 @@ class StepView(BaseView):
|
|||||||
cfg.update(dict(name=step.name, api=cbpi, id=step.id, timer_end=None, managed_fields=get_manged_fields_as_array(type_cfg)))
|
cfg.update(dict(name=step.name, api=cbpi, id=step.id, timer_end=None, managed_fields=get_manged_fields_as_array(type_cfg)))
|
||||||
instance = type_cfg.get("class")(**cfg)
|
instance = type_cfg.get("class")(**cfg)
|
||||||
instance.init()
|
instance.init()
|
||||||
|
|
||||||
# set step instance to ache
|
# set step instance to ache
|
||||||
cbpi.cache["active_step"] = instance
|
cbpi.cache["active_step"] = instance
|
||||||
|
|
||||||
@@ -185,15 +195,16 @@ def init_after_startup():
|
|||||||
if type_cfg is None:
|
if type_cfg is None:
|
||||||
# step type not found. cant restart step
|
# step type not found. cant restart step
|
||||||
return
|
return
|
||||||
|
|
||||||
cfg = step.stepstate.copy()
|
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 = type_cfg.get("class")(**cfg)
|
||||||
instance.init()
|
instance.init()
|
||||||
cbpi.cache["active_step"] = instance
|
cbpi.cache["active_step"] = instance
|
||||||
|
|
||||||
@cbpi.initalizer(order=2000)
|
@cbpi.initalizer(order=2000)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE STEPS MODULE"
|
|
||||||
StepView.register(cbpi.app, route_base='/api/step')
|
StepView.register(cbpi.app, route_base='/api/step')
|
||||||
|
|
||||||
def get_all():
|
def get_all():
|
||||||
@@ -205,7 +216,7 @@ def init(cbpi):
|
|||||||
cbpi.add_cache_callback("steps", get_all)
|
cbpi.add_cache_callback("steps", get_all)
|
||||||
|
|
||||||
@cbpi.backgroundtask(key="step_task", interval=0.1)
|
@cbpi.backgroundtask(key="step_task", interval=0.1)
|
||||||
def execute_step():
|
def execute_step(api):
|
||||||
'''
|
'''
|
||||||
Background job which executes the step
|
Background job which executes the step
|
||||||
:return:
|
:return:
|
||||||
@@ -214,9 +225,7 @@ def execute_step():
|
|||||||
step = cbpi.cache.get("active_step")
|
step = cbpi.cache.get("active_step")
|
||||||
if step is not None:
|
if step is not None:
|
||||||
step.execute()
|
step.execute()
|
||||||
|
|
||||||
if step.is_dirty():
|
if step.is_dirty():
|
||||||
|
|
||||||
state = {}
|
state = {}
|
||||||
for field in step.managed_fields:
|
for field in step.managed_fields:
|
||||||
state[field] = step.__getattribute__(field)
|
state[field] = step.__getattribute__(field)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from flask import json
|
import yaml
|
||||||
|
from flask import json, url_for, Response
|
||||||
from flask_classy import FlaskView, route
|
from flask_classy import FlaskView, route
|
||||||
from git import Repo, Git
|
from git import Repo, Git
|
||||||
|
|
||||||
@@ -101,7 +102,44 @@ class SystemView(FlaskView):
|
|||||||
def dump(self):
|
def dump(self):
|
||||||
return json.dumps(cbpi.cache)
|
return json.dumps(cbpi.cache)
|
||||||
|
|
||||||
|
@route('/endpoints', methods=['GET'])
|
||||||
|
def endpoints(self):
|
||||||
|
import urllib
|
||||||
|
output = []
|
||||||
|
vf = self.api.app.view_functions
|
||||||
|
|
||||||
|
for f in self.api.app.view_functions:
|
||||||
|
print f
|
||||||
|
endpoints = {}
|
||||||
|
re = {
|
||||||
|
"swagger": "2.0",
|
||||||
|
"host": "",
|
||||||
|
"info": {
|
||||||
|
"description":"",
|
||||||
|
"version": "",
|
||||||
|
"title": "CraftBeerPi"
|
||||||
|
},
|
||||||
|
"schemes": ["http"],
|
||||||
|
"paths": endpoints}
|
||||||
|
for rule in self.api.app.url_map.iter_rules():
|
||||||
|
r = rule
|
||||||
|
endpoints[rule.rule] = {}
|
||||||
|
if "HEAD" in r.methods: r.methods.remove("HEAD")
|
||||||
|
if "OPTIONS" in r.methods: r.methods.remove("OPTIONS")
|
||||||
|
for m in rule.methods:
|
||||||
|
endpoints[rule.rule][m] = dict(summary="", description="", consumes=["application/json"],produces=["application/json"])
|
||||||
|
|
||||||
|
with open("config/version.yaml", 'r') as stream:
|
||||||
|
|
||||||
|
y = yaml.load(stream)
|
||||||
|
pprint.pprint(y)
|
||||||
|
pprint.pprint(re)
|
||||||
|
return Response(yaml.dump(re), mimetype='text/yaml')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@cbpi.initalizer()
|
@cbpi.initalizer()
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE SYSTEM MODULE"
|
|
||||||
|
SystemView.api = cbpi
|
||||||
SystemView.register(cbpi.app, route_base='/api/system')
|
SystemView.register(cbpi.app, route_base='/api/system')
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
from flask import Blueprint, render_template, request
|
from flask import Blueprint
|
||||||
from modules import cbpi
|
|
||||||
|
from modules import cbpi
|
||||||
|
|
||||||
react = Blueprint('react', __name__, template_folder='templates', static_folder='static')
|
react = Blueprint('react', __name__, template_folder='templates', static_folder='static')
|
||||||
|
|
||||||
@cbpi.initalizer(order=10)
|
@cbpi.initalizer(order=10)
|
||||||
def init(cbpi):
|
def init(cbpi):
|
||||||
print "INITIALIZE UI"
|
|
||||||
cbpi.app.register_blueprint(react, url_prefix='/ui')
|
cbpi.app.register_blueprint(react, url_prefix='/ui')
|
||||||
|
|
||||||
|
|
||||||
@react.route('/')
|
|
||||||
|
|
||||||
|
@react.route('/', methods=["GET"])
|
||||||
def index():
|
def index():
|
||||||
return react.send_static_file("index.html")
|
return react.send_static_file("index.html")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
.container {
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-heading {
|
||||||
|
padding: 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
padding: 2px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
+49
-52
File diff suppressed because one or more lines are too long
@@ -10,61 +10,12 @@
|
|||||||
<link rel="stylesheet" href="static/bootstrap.dark.css">
|
<link rel="stylesheet" href="static/bootstrap.dark.css">
|
||||||
|
|
||||||
|
|
||||||
<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;
|
|
||||||
padding-right: 8px;
|
|
||||||
padding-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-heading {
|
|
||||||
padding: 5px 5px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-footer {
|
|
||||||
padding: 2px 2px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex !important;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row > [class*='col-'] {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<title>CraftBeerPi 3.0</title>
|
<title>CraftBeerPi 3.0</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root" ></div>
|
||||||
<script src="static/bundle.js" type="text/javascript"></script>
|
<script src="static/bundle.js" type="text/javascript"></script>
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start`.
|
|
||||||
To create a production bundle, use `npm run build`.
|
|
||||||
-->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+3
-4
@@ -1,4 +1,4 @@
|
|||||||
Flask==0.11.1
|
Flask==0.12.4
|
||||||
Flask-SocketIO==2.6.2
|
Flask-SocketIO==2.6.2
|
||||||
eventlet==0.19.0
|
eventlet==0.19.0
|
||||||
greenlet==0.4.10
|
greenlet==0.4.10
|
||||||
@@ -6,10 +6,9 @@ python-dateutil==2.5.3
|
|||||||
python-engineio==0.9.2
|
python-engineio==0.9.2
|
||||||
python-mimeparse==1.5.2
|
python-mimeparse==1.5.2
|
||||||
python-socketio==1.4.4
|
python-socketio==1.4.4
|
||||||
PyYAML==3.11
|
PyYAML==4.2b1
|
||||||
requests==2.11.0
|
requests==2.20.0
|
||||||
Werkzeug==0.11.10
|
Werkzeug==0.11.10
|
||||||
httplib2==0.9.2
|
httplib2==0.9.2
|
||||||
flask-classy==0.6.10
|
flask-classy==0.6.10
|
||||||
GitPython==2.1.3
|
GitPython==2.1.3
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from modules import socketio, app
|
from modules import socketio, app, cbpi
|
||||||
|
|
||||||
socketio.run(app, host='0.0.0.0')
|
try:
|
||||||
|
port = int(cbpi.get_config_parameter('port', '5000'))
|
||||||
|
except ValueError:
|
||||||
|
port = 5000
|
||||||
|
|
||||||
|
socketio.run(app, host='0.0.0.0', port=port)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS schema_info
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
|
version INTEGER,
|
||||||
|
filename VARCHAR(80),
|
||||||
|
creation_data DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('step_mashin', 'MashStep', 'step', 'Default MashIn Step type for import', NULL );
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('step_mash', 'MashStep', 'step', 'Default Mash Step type for import', NULL);
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('step_boil', 'BoilStep', 'step', 'Default Boil Step type for import', NULL);
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('step_chil', 'ChilStep', 'step', 'Default Chil Step type for import', NULL);
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('step_mash_kettle', 1, 'kettle', 'Default Mash Tun', NULL);
|
||||||
|
INSERT OR IGNORE INTO config VALUES ('step_boil_kettle', 1, 'kettle', 'Default Boil Tun ', NULL);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
UPDATE config set `options` = '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]' WHERE name ='buzzer';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
INSERT OR IGNORE INTO config VALUES ('port', 5000, 'number', 'Port to run server (requires restart)', NULL );
|
||||||
Reference in New Issue
Block a user