Author SHA1 Message Date
manuel83 5e960dce1c cleanup 2019-08-19 23:25:02 +02:00
manuel83 04f67754e5 remove wiringpi 2019-08-19 23:03:48 +02:00
manuel83 bf705e3957 update requirements 2019-08-19 23:03:01 +02:00
manuel83 ecd71e8073 python 3 migration 2019-08-19 22:54:59 +02:00
manuel83 9f2d45d834 python 3 migration 2019-08-19 22:51:47 +02:00
manuel83 996bc54768 python 3 migration 2019-08-19 22:51:08 +02:00
Manuel83andGitHub 63f9f5468b Update __init__.py 2019-08-08 21:29:01 +02:00
Manuel83andGitHub 16ad8daf44 Change Target Temp for Fermentation to 50
Change Target Temp for Fermentation to 50
2019-06-18 22:21:28 +02:00
Manuel83andGitHub 67a103d1f3 Update schema.sql 2019-05-18 10:09:31 +02:00
Manuel83andGitHub 274f282e11 Update __init__.py 2019-05-18 10:08:32 +02:00
manuel83 4e432e05bd pyyaml update 2019-02-07 21:24:31 +01:00
Manuel83 eda209422f Update package because of vulnerability
Flask:
CVE-2018-1000656 More information
moderate severity
Vulnerable versions: < 0.12.3
Patched version: 0.12.3
The Pallets Project flask version Before 0.12.3 contains a CWE-20:
Improper Input Validation vulnerability in flask that can result in
Large amount of memory usage possibly leading to denial of service.
This attack appear to be exploitable via Attacker provides JSON data in
incorrect encoding. This vulnerability appears to have been fixed in
0.12.3.

Requests:
CVE-2018-18074 More information
moderate severity
Vulnerable versions: <= 2.19.1
Patched version: 2.20.0
The Requests package through 2.19.1 before 2018-09-14 for Python sends
an HTTP Authorization header to an http URI upon receiving a
same-hostname https-to-http redirect, which makes it easier for remote
attackers to discover credentials by sniffing the network.
2018-10-29 22:15:03 +01:00
Manuel83andGitHub 5b47b94833 Merge pull request #213 from cgspeck/fix-typo
Fix typo in README
2018-09-04 06:32:32 +02:00
Manuel83andGitHub 98409d9895 Merge pull request #206 from swimIan/swimIan-patch-1
Remove "Steps started on xml imported" bug
2018-09-04 06:32:15 +02:00
Chris Speck 433b1c30f1 Fix typo which resulted in reference to incorrect port 2018-08-15 22:47:36 +10:00
Manuel83andGitHub a477cb83a8 Merge pull request #211 from cgspeck/docker-dev-env
Docker files for ease of development on PCs/macs
2018-08-15 07:00:19 +02:00
Chris Speck a22dcba77c Docker files for ease of development on PCs/macs 2018-08-13 23:13:13 +10:00
swimIanandGitHub c1d94a8f61 Remove "Steps started on xml imported" bug 2018-08-03 09:12:45 -07:00
swimIanandGitHub dd64f4ee16 Merge pull request #16 from Manuel83/master
August 2 update
2018-08-03 08:40:43 -07:00
Manuel83andGitHub 1414b1ed3b Merge pull request #166 from DrRataplan/master
Import hop alerts from BeerXML
2018-08-02 12:54:14 +02:00
Manuel83andGitHub acd404622b Merge pull request #182 from archonic/patch-1
Remove duplicate line
2018-05-23 10:19:17 +02:00
Manuel83andGitHub d657077570 Merge pull request #181 from clearwaterbrewer/patch-1
Fix hysteresis turning off heater
2018-05-23 10:18:41 +02:00
JoshandGitHub 0e6da89cfd Remove duplicate line 2018-04-05 08:53:19 -07:00
clearwaterbrewerandGitHub faa2b9d32c Update __init__.py
removed lines 37 and 38 to make hysteresis work properly.
2018-04-05 10:49:35 -04:00
Martin 83699f1933 Import boil time alerts from BeerXML
By reading the HOP and MISC elements, the 'please add your hop' alerts may be imported.
2018-01-07 11:45:15 +00:00
swimIanandGitHub fea3e85053 Merge pull request #15 from Manuel83/master
Update swimIan master
2017-11-15 09:31:46 -08:00
Manuel83andGitHub 5efa1df350 Merge pull request #133 from JamFfm/JamFfm-install
Added a combinaton of stop-del logfiles-start
2017-11-08 19:45:08 +01:00
Manuel83andGitHub dd5b5f456a Merge pull request #151 from mb-tec/master
Buzzer Beep Level
2017-11-08 19:44:32 +01:00
Matthias e0e3aff929 Buzzer Beep Level 2017-10-28 22:08:30 +02:00
Manuel83andGitHub 2a02979e37 Merge pull request #142 from Manuel83/revert-105-patch2
Revert "Updates the boilstep and beerxml recipe import"
2017-10-15 14:48:45 +02:00
swimIanandGitHub 1b3042a082 Merge pull request #13 from Manuel83/master
Update
2017-10-14 07:55:06 -07:00
JamFfmandGitHub 211edc1bbc Added a combinaton of stop-del logfiles-start
This one makes it easier to test plugins.
2017-10-01 14:11:52 +02:00
swimIanandGitHub 0447621895 Merge pull request #10 from Manuel83/master
Most recent
2017-09-28 21:44:17 -07:00
34 changed files with 180 additions and 108 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ craftbeerpi.db
**/node_modules/* **/node_modules/*
*.pyc *.pyc
*.templog *.templog
venv/
modules/plugins/* modules/plugins/*
!modules/plugins/__init__.py !modules/plugins/__init__.py
*.pyc *.pyc
+14
View File
@@ -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"]
+15
View File
@@ -28,6 +28,21 @@ http://web.craftbeerpi.com/hardware/
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:
+2 -1
View File
@@ -84,7 +84,8 @@ INSERT OR IGNORE INTO config VALUES ('actor_cols', 4, 'select', 'Adjust the widt
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 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 OR IGNORE INTO config VALUES ('unit', 'C', 'select', 'Temperature Unit', '["C","F"]'); INSERT OR IGNORE INTO config VALUES ('unit', 'C', 'select', 'Temperature Unit', '["C","F"]');
INSERT OR IGNORE 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 OR IGNORE 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 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 ('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 ('brew_name', '', 'text', 'Brew Name', NULL);
INSERT OR IGNORE INTO config VALUES ('donation_notification', 'YES', 'select', 'Disable Donation Notification', '["YES","NO"]'); INSERT OR IGNORE INTO config VALUES ('donation_notification', 'YES', 'select', 'Disable Donation Notification', '["YES","NO"]');
+9
View File
@@ -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
+13 -8
View File
@@ -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
@@ -34,13 +35,6 @@ show_menu () {
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
@@ -143,6 +137,17 @@ show_menu () {
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
} }
+5 -12
View File
@@ -1,16 +1,9 @@
import json
import pprint
import sys, os
from flask import Flask, render_template, redirect
from flask_socketio import SocketIO, emit
import logging
# Define the WSGI application object # Define the WSGI application object
from app_config import *
import pprint import pprint
from modules.core.db import get_db from modules.core.db import get_db
from .app_config import *
@app.route('/') @app.route('/')
@@ -30,15 +23,15 @@ import modules.actor
import modules.notification import modules.notification
import modules.fermenter import modules.fermenter
from modules.addon.endpoints import initPlugins from modules.addon.endpoints import initPlugins
import modules.ui import modules.ui.endpoints
import modules.system import modules.system.endpoints
import modules.buzzer import modules.buzzer
import modules.stats import modules.stats
import modules.kettle import modules.kettle
import modules.recipe_import import modules.recipe_import
import modules.core.db_mirgrate 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
@@ -47,7 +40,7 @@ pp = pprint.PrettyPrinter(indent=6)
def init_db(): def init_db():
print "INIT DB" print("INIT DB")
with app.app_context(): with app.app_context():
db = get_db() db = get_db()
-1
View File
@@ -1 +0,0 @@
import endpoints
+1 -1
View File
@@ -134,7 +134,7 @@ def plugins():
""" """
response = requests.get("https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins.yaml") response = requests.get("https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins.yaml")
cbpi.cache["plugins"] = merge(yaml.load(response.text), cbpi.cache["plugins"]) cbpi.cache["plugins"] = merge(yaml.load(response.text), cbpi.cache["plugins"])
for key, value in cbpi.cache["plugins"].iteritems(): for key, value in cbpi.cache["plugins"].items():
value["installed"] = os.path.isdir("./modules/plugins/%s/" % (key)) value["installed"] = os.path.isdir("./modules/plugins/%s/" % (key))
return json.dumps(cbpi.cache["plugins"]) return json.dumps(cbpi.cache["plugins"])
-2
View File
@@ -8,8 +8,6 @@ from flask_socketio import SocketIO, emit
import logging import logging
from modules.core.core import CraftBeerPi, ActorBase, SensorBase from modules.core.core import CraftBeerPi, ActorBase, SensorBase
from modules.core.db import DBModel from modules.core.db import DBModel
+1 -1
View File
@@ -15,7 +15,7 @@ class DummyTempSensor(SensorActive):
@cbpi.action("My Custom Action") @cbpi.action("My Custom Action")
def my_action(self): def my_action(self):
print "HELLO WORLD"
pass pass
def get_unit(self): def get_unit(self):
+6 -6
View File
@@ -10,7 +10,7 @@ try:
GPIO.setmode(GPIO.BCM) GPIO.setmode(GPIO.BCM)
except Exception as e: except Exception as e:
print e print(e)
pass pass
@@ -25,11 +25,11 @@ class GPIOSimple(ActorBase):
GPIO.output(int(self.gpio), 0) GPIO.output(int(self.gpio), 0)
def on(self, power=0): def on(self, power=0):
print "GPIO ON %s" % str(self.gpio) print("GPIO ON %s" % str(self.gpio))
GPIO.output(int(self.gpio), 1) GPIO.output(int(self.gpio), 1)
def off(self): def off(self):
print "GPIO OFF" print("GPIO OFF")
GPIO.output(int(self.gpio), 0) GPIO.output(int(self.gpio), 0)
@cbpi.actor @cbpi.actor
@@ -67,7 +67,7 @@ class GPIOPWM(ActorBase):
self.p.ChangeDutyCycle(self.power) self.p.ChangeDutyCycle(self.power)
def off(self): def off(self):
print "GPIO OFF" print("GPIO OFF")
self.p.stop() self.p.stop()
@@ -98,10 +98,10 @@ class Dummy(ActorBase):
:param power: int value between 0 - 100 :param power: int value between 0 - 100
:return: :return:
''' '''
print "ON" print("ON")
def off(self): def off(self):
print "OFF" print("OFF")
@@ -34,7 +34,5 @@ class Hysteresis(KettleController):
self.heater_on(100) self.heater_on(100)
elif self.get_temp() >= self.get_target_temp() - float(self.off): elif self.get_temp() >= self.get_target_temp() - float(self.off):
self.heater_off() self.heater_off()
else:
self.heater_off()
self.sleep(1) self.sleep(1)
+15 -8
View File
@@ -1,5 +1,5 @@
import time import time
from thread import start_new_thread #from threading import start_new_thread
from modules import cbpi from modules import cbpi
try: try:
@@ -10,10 +10,11 @@ except Exception as e:
class Buzzer(object): class Buzzer(object):
sound = ["H", 0.1, "L", 0.1, "H", 0.1, "L", 0.1, "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 = int(gpio) self.gpio = int(gpio)
self.beep_level = beep_level
GPIO.setmode(GPIO.BCM) GPIO.setmode(GPIO.BCM)
GPIO.setup(self.gpio, GPIO.OUT) GPIO.setup(self.gpio, GPIO.OUT)
self.state = True self.state = True
@@ -31,20 +32,26 @@ 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:
pass pass
## TODO
start_new_thread(play, (self.sound,)) #start_new_thread(play, (self.sound,))
@cbpi.initalizer(order=1) @cbpi.initalizer(order=1)
def init(cbpi): def init(cbpi):
gpio = cbpi.get_config_parameter("buzzer", 16) gpio = cbpi.get_config_parameter("buzzer", 22)
cbpi.buzzer = Buzzer(gpio) beep_level = cbpi.get_config_parameter("buzzer_beep_level", "HIGH")
cbpi.buzzer = Buzzer(gpio, beep_level)
cbpi.beep() cbpi.beep()
cbpi.app.logger.info("INIT OK") cbpi.app.logger.info("INIT OK")
+1 -1
View File
@@ -46,7 +46,7 @@ class ConfigView(BaseView):
with cls.api.app.app_context(): with cls.api.app.app_context():
cls.api.cache[cls.cache_key] = {} cls.api.cache[cls.cache_key] = {}
for key, value in cls.model.get_all().iteritems(): for key, value in cls.model.get_all().items():
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
+1 -1
View File
@@ -104,6 +104,6 @@ class BaseView(FlaskView):
cls.api.cache[cls.cache_key].append(value) cls.api.cache[cls.cache_key].append(value)
else: else:
cls.api.cache[cls.cache_key] = {} cls.api.cache[cls.cache_key] = {}
for key, value in cls.model.get_all().iteritems(): for key, value in cls.model.get_all().items():
cls.post_init_callback(value) cls.post_init_callback(value)
cls.api.cache[cls.cache_key][key] = value cls.api.cache[cls.cache_key][key] = value
+1 -1
View File
@@ -42,7 +42,7 @@ class ControllerBase(object):
@staticmethod @staticmethod
def init_global(): def init_global():
print "GLOBAL CONTROLLER INIT" print("GLOBAL CONTROLLER INIT")
def notify(self, headline, message, type="success", timeout=5000): def notify(self, headline, message, type="success", timeout=5000):
self.api.notify(headline, message, type, timeout) self.api.notify(headline, message, type, timeout)
+7 -11
View File
@@ -12,9 +12,9 @@ from time import localtime, strftime
from functools import wraps, update_wrapper from functools import wraps, update_wrapper
from props import * from .props import *
from hardware import * from .hardware import *
import time import time
import uuid import uuid
@@ -28,7 +28,7 @@ class ActorAPI(object):
def init_actors(self): def init_actors(self):
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.items():
value.get("class").api = self value.get("class").api = self
value.get("class").init_global() value.get("class").init_global()
@@ -40,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()
@@ -90,7 +89,7 @@ class SensorAPI(object):
self.app.logger.info("Init Sensors") self.app.logger.info("Init Sensors")
t = self.cache.get("sensor_types") t = self.cache.get("sensor_types")
for key, value in t.iteritems(): for key, value in t.items():
value.get("class").init_global() value.get("class").init_global()
for key in self.cache.get("sensors"): for key in self.cache.get("sensors"):
@@ -293,7 +292,7 @@ class CraftBeerPi(ActorAPI, SensorAPI):
t = tmpObj.__getattribute__(m) t = tmpObj.__getattribute__(m)
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable, "description": t.description}) 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__.items():
if hasattr(method, "action"): if hasattr(method, "action"):
label = method.__getattribute__("label") label = method.__getattribute__("label")
self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label}) self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label})
@@ -310,10 +309,7 @@ class CraftBeerPi(ActorAPI, SensorAPI):
def actor2(self, description="", power=True, **options): def actor2(self, description="", power=True, **options):
def decorator(f): def decorator(f):
print f()
print f
print options
print description
return f return f
return decorator return decorator
@@ -370,7 +366,7 @@ class CraftBeerPi(ActorAPI, SensorAPI):
t = tmpObj.__getattribute__(m) t = tmpObj.__getattribute__(m)
self.cache[key][name]["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable, "description": t.description}) 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__.items():
if hasattr(method, "action"): if hasattr(method, "action"):
label = method.__getattribute__("label") label = method.__getattribute__("label")
self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label}) self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label})
+1 -1
View File
@@ -79,7 +79,7 @@ class DBModel(object):
cur = get_db().cursor() cur = get_db().cursor()
if cls.__priamry_key__ is not None and kwargs.has_key(cls.__priamry_key__): if cls.__priamry_key__ is not None and cls.__priamry_key__ in kwargs:
query = "INSERT INTO %s (%s, %s) VALUES (?, %s)" % ( query = "INSERT INTO %s (%s, %s) VALUES (?, %s)" % (
cls.__table_name__, cls.__table_name__,
cls.__priamry_key__, cls.__priamry_key__,
+6 -3
View File
@@ -1,12 +1,15 @@
import sqlite3 import sqlite3
import os import os
from modules import cbpi from modules import cbpi
from db import get_db from .db import get_db
def execute_file(curernt_version, data): def execute_file(curernt_version, data):
# TODO add if block
'''
if curernt_version >= data["version"]: if curernt_version >= data["version"]:
cbpi.app.logger.info("SKIP DB FILE: %s" % data["file"]) cbpi.app.logger.info("SKIP DB FILE: %s" % data["file"])
return return
'''
try: try:
with sqlite3.connect("craftbeerpi.db") as conn: with sqlite3.connect("craftbeerpi.db") as conn:
with open('./update/%s' % data["file"], 'r') as f: with open('./update/%s' % data["file"], 'r') as f:
@@ -19,8 +22,8 @@ def execute_file(curernt_version, data):
conn.commit() conn.commit()
except sqlite3.OperationalError as err: except sqlite3.OperationalError as err:
print "EXCEPT" print("EXCEPT")
print err print(err)
@cbpi.initalizer(order=-9999) @cbpi.initalizer(order=-9999)
def init(app=None): def init(app=None):
+4 -8
View File
@@ -40,18 +40,16 @@ class Base(object):
class SensorBase(Base): class SensorBase(Base):
last_value = 0 last_value = 0
def init(self): def init(self):
print "INIT Base SENSOR" print("INIT Base SENSOR")
def stop(self): def stop(self):
print "STOP SENSOR" print("STOP SENSOR")
def data_received(self, data): def data_received(self, data):
self.last_value = data self.last_value = data
self.api.receive_sensor_value(self.id, data) self.api.receive_sensor_value(self.id, data)
@@ -65,12 +63,11 @@ class SensorBase(Base):
return {"value": self.last_value, "unit": self.get_unit()} return {"value": self.last_value, "unit": self.get_unit()}
class SensorActive(SensorBase):
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):
@@ -79,14 +76,13 @@ class SensorActive(SensorBase):
def stop(self): def stop(self):
self.__running = False self.__running = False
def execute(self): def execute(self):
pass pass
class SensorPassive(SensorBase): class SensorPassive(SensorBase):
def init(self): def init(self):
print "INIT PASSIV SENSOR" print("INIT PASSIV SENSOR")
pass pass
def read(self): def read(self):
+4 -4
View File
@@ -109,10 +109,10 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI):
pass pass
def execute(self): def execute(self):
print "-------------" print("-------------")
print "Step Info" print("Step Info")
print "Kettle ID: %s" % self.kettle_id print("Kettle ID: %s" % self.kettle_id)
print "ID: %s" % self.id print("ID: %s" % self.id)
def __init__(self, *args, **kwds): def __init__(self, *args, **kwds):
+6 -4
View File
@@ -210,7 +210,7 @@ class FermenterView(BaseView):
def toggle(self, id): def toggle(self, id):
fermenter = cbpi.cache.get(self.cache_key)[id] fermenter = cbpi.cache.get(self.cache_key)[id]
try: try:
print fermenter.state print(fermenter.state)
if fermenter.state is False: if fermenter.state is False:
# Start controller # Start controller
if fermenter.logic is not None: if fermenter.logic is not None:
@@ -223,6 +223,8 @@ class FermenterView(BaseView):
def run(instance): def run(instance):
instance.run() instance.run()
fermenter.state = not fermenter.state
cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id))
t = cbpi.socketio.start_background_task(target=run, instance=instance) t = cbpi.socketio.start_background_task(target=run, instance=instance)
fermenter.state = not fermenter.state fermenter.state = not fermenter.state
@@ -234,7 +236,7 @@ class FermenterView(BaseView):
cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id)) cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id))
except Exception as e: except Exception as e:
print e print(e)
cbpi.notify("Toogle Fementer Controller failed", "Pleae check the %s configuration" % fermenter.name, cbpi.notify("Toogle Fementer Controller failed", "Pleae check the %s configuration" % fermenter.name,
type="danger", timeout=None) type="danger", timeout=None)
return ('', 500) return ('', 500)
@@ -259,7 +261,7 @@ class FermenterView(BaseView):
cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id])
def check_step(self): def check_step(self):
for key, value in cbpi.cache["fermenter_task"].iteritems(): for key, value in cbpi.cache["fermenter_task"].items():
try: try:
fermenter = self.get_fermenter(key) fermenter = self.get_fermenter(key)
@@ -290,7 +292,7 @@ def read_target_temps(api):
:return: None :return: None
""" """
result = {} result = {}
for key, value in cbpi.cache.get("fermenter").iteritems(): for key, value in cbpi.cache.get("fermenter").items():
cbpi.save_to_file(key, value.target_temp, prefix="fermenter") cbpi.save_to_file(key, value.target_temp, prefix="fermenter")
+1 -1
View File
@@ -86,7 +86,7 @@ def read_target_temps(api):
:return: None :return: None
""" """
result = {} result = {}
for key, value in cbpi.cache.get("kettle").iteritems(): for key, value in cbpi.cache.get("kettle").items():
cbpi.save_to_file(key, value.target_temp, prefix="kettle") cbpi.save_to_file(key, value.target_temp, prefix="kettle")
@cbpi.initalizer() @cbpi.initalizer()
-1
View File
@@ -1 +0,0 @@
import endpoints
-3
View File
@@ -1,3 +0,0 @@
import beerxml
import kbh
import restapi
+48 -6
View File
@@ -48,6 +48,7 @@ class BeerXMLImport(FlaskView):
steps = self.getSteps(id) steps = self.getSteps(id)
boil_time_alerts = self.getBoilAlerts(id)
name = self.getRecipeName(id) name = self.getRecipeName(id)
self.api.set_config_parameter("brew_name", name) self.api.set_config_parameter("brew_name", name)
boil_time = self.getBoilTime(id) boil_time = self.getBoilTime(id)
@@ -67,10 +68,29 @@ class BeerXMLImport(FlaskView):
for row in steps: 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": 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}}) Step.insert(**{"name": "ChilStep", "type": "ChilStep", "config": {"timer": 15}})
## Add cooking step ## Add boiling step
Step.insert(**{"name": "Boil", "type": boilstep_type, "config": {"kettle": boil_kettle, "temp": boil_temp, "timer": boil_time}}) 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 ## Add Whirlpool step
Step.insert(**{"name": "Whirlpool", "type": "ChilStep", "config": {"timer": 15}}) Step.insert(**{"name": "Whirlpool", "type": "ChilStep", "config": {"timer": 15}})
StepView().reset()
self.api.emit("UPDATE_ALL_STEPS", Step.get_all()) self.api.emit("UPDATE_ALL_STEPS", Step.get_all())
self.api.notify(headline="Recipe %s loaded successfully" % name, message="") self.api.notify(headline="Recipe %s loaded successfully" % name, message="")
except Exception as e: except Exception as e:
@@ -87,18 +107,40 @@ class BeerXMLImport(FlaskView):
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot() e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
return float(e.find('./RECIPE[%s]/BOIL_TIME' % (str(id))).text) 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): def getSteps(self, id):
e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot() e = xml.etree.ElementTree.parse(self.BEER_XML_FILE).getroot()
steps = [] steps = []
for e in e.findall('./RECIPE[%s]/MASH/MASH_STEPS/MASH_STEP' % (str(id))): for e in e.findall('./RECIPE[%s]/MASH/MASH_STEPS/MASH_STEP' % (str(id))):
if self.api.get_config_parameter("unit", "C") == "C": if self.api.get_config_parameter("unit", "C") == "C":
temp = float(e.find("STEP_TEMP").text) temp = float(e.find("STEP_TEMP").text)
else: else:
temp = round(9.0 / 5.0 * float(e.find("STEP_TEMP").text) + 32, 2) 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)}) steps.append({"name": e.find("NAME").text, "temp": temp, "timer": float(e.find("STEP_TIME").text)})
return steps return steps
+1 -1
View File
@@ -29,7 +29,7 @@ class KBH(FlaskView):
result.append({"id": row[0], "name": row[1], "brewed": row[2]}) result.append({"id": row[0], "name": row[1], "brewed": row[2]})
return json.dumps(result) return json.dumps(result)
except Exception as e: except Exception as e:
print e print(e)
self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger") self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger")
return ('', 500) return ('', 500)
finally: finally:
+1 -1
View File
@@ -43,6 +43,6 @@ def read_passive_sensor(api):
:return: None :return: None
""" """
for key, value in cbpi.cache.get("sensors").iteritems(): for key, value in cbpi.cache.get("sensors").items():
if value.mode == "P": if value.mode == "P":
value.instance.read() value.instance.read()
-1
View File
@@ -1 +0,0 @@
import endpoints
+1 -1
View File
@@ -109,7 +109,7 @@ class SystemView(FlaskView):
vf = self.api.app.view_functions vf = self.api.app.view_functions
for f in self.api.app.view_functions: for f in self.api.app.view_functions:
print f print(f)
endpoints = {} endpoints = {}
re = { re = {
"swagger": "2.0", "swagger": "2.0",
-1
View File
@@ -1 +0,0 @@
import endpoints
File diff suppressed because one or more lines are too long
+14 -14
View File
@@ -1,14 +1,14 @@
Flask==0.11.1 eventlet==0.25.0
Flask-SocketIO==2.6.2 Flask==1.1.1
eventlet==0.19.0 Flask-Classy==0.6.10
greenlet==0.4.10 Flask-SocketIO==4.2.1
python-dateutil==2.5.3 GitPython==3.0.1
python-engineio==0.9.2 greenlet==0.4.15
python-mimeparse==1.5.2 httplib2==0.13.1
python-socketio==1.4.4 python-dateutil==2.8.0
PyYAML==3.11 python-engineio==3.9.3
requests==2.11.0 python-mimeparse==1.6.0
Werkzeug==0.11.10 python-socketio==4.3.1
httplib2==0.9.2 PyYAML==5.1.2
flask-classy==0.6.10 requests==2.22.0
GitPython==2.1.3 Werkzeug==0.15.5