From 79ff35ff2b751ebf99b5a355ce325a17ac6dc108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Thu, 14 Nov 2019 09:11:12 -0300 Subject: [PATCH 01/34] check_hop_timer skip empty values --- modules/base_plugins/brew_steps/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/base_plugins/brew_steps/__init__.py b/modules/base_plugins/brew_steps/__init__.py index 16dd65b..02aaa92 100644 --- a/modules/base_plugins/brew_steps/__init__.py +++ b/modules/base_plugins/brew_steps/__init__.py @@ -201,8 +201,8 @@ class BoilStep(StepBase): def check_hop_timer(self, number, value): - - if self.__getattribute__("hop_%s_added" % number) is not True and time.time() > ( + if isinstance(value, int) and \ + 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) From ed441f36493f548d2f1b67efc73781ee121cc5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Thu, 14 Nov 2019 09:12:31 -0300 Subject: [PATCH 02/34] - GPIOPWM only set power if the actor is on - create DummyPWM for PWM testing --- modules/base_plugins/gpio_actor/__init__.py | 27 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/modules/base_plugins/gpio_actor/__init__.py b/modules/base_plugins/gpio_actor/__init__.py index 820a55e..8184130 100644 --- a/modules/base_plugins/gpio_actor/__init__.py +++ b/modules/base_plugins/gpio_actor/__init__.py @@ -62,9 +62,10 @@ class GPIOPWM(ActorBase): :param power: int value between 0 - 100 :return: ''' - if power is not None: - self.power = int(power) - self.p.ChangeDutyCycle(self.power) + if self.p: + if power is not None: + self.power = int(power) + self.p.ChangeDutyCycle(self.power) def off(self): print "GPIO OFF" @@ -104,4 +105,24 @@ class Dummy(ActorBase): print "OFF" +@cbpi.actor +class DummyPWM(ActorBase): + + power = 100 + + def on(self, power=100): + ''' + Code to switch on the actor + :param power: int value between 0 - 100 + :return: + ''' + self.power = int(power) if power is not None else 100 + print "DummyPWM ON %s" % self.power + + def off(self): + self.power = 100 + print "OFF" + def set_power(self, power): + self.power = int(power) + print "DummyPWM POWER %s" % self.power From 1397284160a590879ad35a7e0a6ad5c57ebdca45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Thu, 14 Nov 2019 09:16:23 -0300 Subject: [PATCH 03/34] add space to propper logging the failing method name --- modules/core/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/core.py b/modules/core/core.py index ff6dfe6..383ef3f 100644 --- a/modules/core/core.py +++ b/modules/core/core.py @@ -485,7 +485,7 @@ class CraftBeerPi(ActorAPI, SensorAPI): try: method(self) 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) From 511c119c4c3b5da370c46d9e6303d735d5e78596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Fri, 31 Jul 2020 18:09:56 -0300 Subject: [PATCH 04/34] migrate to py3.5 --- Dockerfile | 4 +- modules/__init__.py | 142 +++++----- modules/addon/__init__.py | 2 +- modules/addon/endpoints.py | 7 +- modules/base_plugins/brew_steps/__init__.py | 8 +- modules/base_plugins/dummy_temp/__init__.py | 2 +- modules/base_plugins/gpio_actor/__init__.py | 13 +- modules/buzzer/__init__.py | 2 +- modules/config/__init__.py | 2 +- modules/core/baseview.py | 2 +- modules/core/controller.py | 2 +- modules/core/core.py | 20 +- modules/core/db.py | 2 +- modules/core/db_mirgrate.py | 18 +- modules/core/hardware.py | 6 +- modules/core/step.py | 10 +- modules/fermenter/__init__.py | 8 +- modules/kettle/__init__.py | 2 +- modules/logs/__init__.py | 2 +- modules/logs/endpoints.py | 4 +- modules/recipe_import/__init__.py | 6 +- modules/recipe_import/kbh.py | 2 +- modules/sensors/__init__.py | 2 +- modules/system/__init__.py | 2 +- modules/system/endpoints.py | 290 ++++++++++---------- modules/ui/__init__.py | 2 +- requirements.txt | 2 +- 27 files changed, 281 insertions(+), 283 deletions(-) diff --git a/Dockerfile b/Dockerfile index ff2bfe5..455b74c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Dockerfile for development on a pc/mac -FROM python:2 +FROM python:3.5 EXPOSE 5000 @@ -11,4 +11,4 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . -CMD ["python", "run.py"] \ No newline at end of file +CMD ["python", "run.py"] diff --git a/modules/__init__.py b/modules/__init__.py index bf9d008..4544695 100755 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -1,72 +1,72 @@ -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 - -from app_config import * -import pprint - -from modules.core.db import get_db - - -@app.route('/') -def index(): - return redirect('ui') - - -# Define the database object which is imported -# by modules and controllers - - -import modules.steps -import modules.config -import modules.logs -import modules.sensors -import modules.actor -import modules.notification -import modules.fermenter -from modules.addon.endpoints import initPlugins -import modules.ui -import modules.system -import modules.buzzer -import modules.stats -import modules.kettle -import modules.recipe_import -import modules.core.db_mirgrate - -from app_config import cbpi -# Build the database: -# This will create the database file using SQLAlchemy - - -pp = pprint.PrettyPrinter(indent=6) - - -def init_db(): - print "INIT DB" - with app.app_context(): - db = get_db() - - try: - with app.open_resource('../config/schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - - db.commit() - except Exception as e: - pass - -init_db() -initPlugins() -cbpi.run_init() - -cbpi.run_background_processes() - - - -app.logger.info("##########################################") -app.logger.info("### STARTUP COMPLETE") +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 + +from modules.app_config import * +import pprint + +from modules.core.db import get_db + + +@app.route('/') +def index(): + return redirect('ui') + + +# Define the database object which is imported +# by modules and controllers + + +import modules.steps +import modules.config +import modules.logs +import modules.sensors +import modules.actor +import modules.notification +import modules.fermenter +from modules.addon.endpoints import initPlugins +import modules.ui +import modules.system +import modules.buzzer +import modules.stats +import modules.kettle +import modules.recipe_import +import modules.core.db_mirgrate + +from .app_config import cbpi +# Build the database: +# This will create the database file using SQLAlchemy + + +pp = pprint.PrettyPrinter(indent=6) + + +def init_db(): + print("INIT DB") + with app.app_context(): + db = get_db() + + try: + with app.open_resource('../config/schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + + db.commit() + except Exception as e: + pass + +init_db() +initPlugins() +cbpi.run_init() + +cbpi.run_background_processes() + + + +app.logger.info("##########################################") +app.logger.info("### STARTUP COMPLETE") app.logger.info("##########################################") \ No newline at end of file diff --git a/modules/addon/__init__.py b/modules/addon/__init__.py index b89697f..5f9c459 100644 --- a/modules/addon/__init__.py +++ b/modules/addon/__init__.py @@ -1 +1 @@ -import endpoints \ No newline at end of file +import modules.addon.endpoints \ No newline at end of file diff --git a/modules/addon/endpoints.py b/modules/addon/endpoints.py index 83397b9..44c8066 100644 --- a/modules/addon/endpoints.py +++ b/modules/addon/endpoints.py @@ -11,6 +11,7 @@ import os import requests import yaml import shutil +import imp blueprint = Blueprint('addon', __name__) @@ -24,7 +25,7 @@ def merge(source, destination): :param destination: :return: """ - for key, value in source.items(): + for key, value in list(source.items()): if isinstance(value, dict): # get node or create one node = destination.setdefault(key, {}) @@ -115,7 +116,7 @@ def reload(name): """ try: if name in cache["modules"]: - reload(cache["modules"][name]) + imp.reload(cache["modules"][name]) cbpi.emit_message("REALOD OF PLUGIN %s SUCCESSFUL" % (name)) return ('', 204) else: @@ -134,7 +135,7 @@ def plugins(): """ response = requests.get("https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins.yaml") 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)) return json.dumps(cbpi.cache["plugins"]) diff --git a/modules/base_plugins/brew_steps/__init__.py b/modules/base_plugins/brew_steps/__init__.py index 16dd65b..60724a7 100644 --- a/modules/base_plugins/brew_steps/__init__.py +++ b/modules/base_plugins/brew_steps/__init__.py @@ -58,7 +58,7 @@ class MashStep(StepBase): # Check if timer finished and go to next step if self.is_timer_finished() == True: self.notify("Mash Step Completed!", "Starting the next step", timeout=None) - self.next() + next(self) @cbpi.step @@ -121,7 +121,7 @@ class ChilStep(StepBase): self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: - self.next() + next(self) @cbpi.step class PumpStep(StepBase): @@ -149,7 +149,7 @@ class PumpStep(StepBase): self.start_timer(int(self.timer) * 60) if self.is_timer_finished() == True: - self.next() + next(self) @cbpi.step class BoilStep(StepBase): @@ -226,4 +226,4 @@ class BoilStep(StepBase): # 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() + next(self) diff --git a/modules/base_plugins/dummy_temp/__init__.py b/modules/base_plugins/dummy_temp/__init__.py index 2ed3f47..9ef3a94 100644 --- a/modules/base_plugins/dummy_temp/__init__.py +++ b/modules/base_plugins/dummy_temp/__init__.py @@ -15,7 +15,7 @@ class DummyTempSensor(SensorActive): @cbpi.action("My Custom Action") def my_action(self): - print "HELLO WORLD" + print("HELLO WORLD") pass def get_unit(self): diff --git a/modules/base_plugins/gpio_actor/__init__.py b/modules/base_plugins/gpio_actor/__init__.py index 820a55e..1017fe4 100644 --- a/modules/base_plugins/gpio_actor/__init__.py +++ b/modules/base_plugins/gpio_actor/__init__.py @@ -10,7 +10,7 @@ try: GPIO.setmode(GPIO.BCM) except Exception as e: - print e + print(e) pass @@ -25,11 +25,11 @@ class GPIOSimple(ActorBase): GPIO.output(int(self.gpio), 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) def off(self): - print "GPIO OFF" + print("GPIO OFF") GPIO.output(int(self.gpio), 0) @cbpi.actor @@ -67,7 +67,7 @@ class GPIOPWM(ActorBase): self.p.ChangeDutyCycle(self.power) def off(self): - print "GPIO OFF" + print("GPIO OFF") self.p.stop() @@ -98,10 +98,9 @@ class Dummy(ActorBase): :param power: int value between 0 - 100 :return: ''' - print "ON" + print("ON") def off(self): - print "OFF" - + print("OFF") diff --git a/modules/buzzer/__init__.py b/modules/buzzer/__init__.py index bfbe1b6..4a091ab 100644 --- a/modules/buzzer/__init__.py +++ b/modules/buzzer/__init__.py @@ -1,5 +1,5 @@ import time -from thread import start_new_thread +from _thread import start_new_thread from modules import cbpi try: diff --git a/modules/config/__init__.py b/modules/config/__init__.py index 1adf054..d4507c7 100644 --- a/modules/config/__init__.py +++ b/modules/config/__init__.py @@ -46,7 +46,7 @@ class ConfigView(BaseView): with cls.api.app.app_context(): 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.api.cache[cls.cache_key][value.name] = value diff --git a/modules/core/baseview.py b/modules/core/baseview.py index 9bdf8a3..d380422 100644 --- a/modules/core/baseview.py +++ b/modules/core/baseview.py @@ -104,6 +104,6 @@ class BaseView(FlaskView): cls.api.cache[cls.cache_key].append(value) else: cls.api.cache[cls.cache_key] = {} - for key, value in cls.model.get_all().iteritems(): + for key, value in list(cls.model.get_all().items()): cls.post_init_callback(value) cls.api.cache[cls.cache_key][key] = value \ No newline at end of file diff --git a/modules/core/controller.py b/modules/core/controller.py index 05f21fd..64daba0 100644 --- a/modules/core/controller.py +++ b/modules/core/controller.py @@ -42,7 +42,7 @@ class ControllerBase(object): @staticmethod def init_global(): - print "GLOBAL CONTROLLER INIT" + print("GLOBAL CONTROLLER INIT") def notify(self, headline, message, type="success", timeout=5000): self.api.notify(headline, message, type, timeout) diff --git a/modules/core/core.py b/modules/core/core.py index ff6dfe6..571bdc3 100644 --- a/modules/core/core.py +++ b/modules/core/core.py @@ -12,9 +12,9 @@ from time import localtime, strftime from functools import wraps, update_wrapper -from props import * +from modules.core.props import * -from hardware import * +from modules.core.hardware import * import time import uuid @@ -28,7 +28,7 @@ class ActorAPI(object): def init_actors(self): self.app.logger.info("Init Actors") t = self.cache.get("actor_types") - for key, value in t.iteritems(): + for key, value in list(t.items()): value.get("class").api = self value.get("class").init_global() @@ -89,7 +89,7 @@ class SensorAPI(object): self.app.logger.info("Init Sensors") t = self.cache.get("sensor_types") - for key, value in t.iteritems(): + for key, value in list(t.items()): value.get("class").init_global() for key in self.cache.get("sensors"): @@ -292,7 +292,7 @@ class CraftBeerPi(ActorAPI, SensorAPI): 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(): + for name, method in list(cls.__dict__.items()): if hasattr(method, "action"): label = method.__getattribute__("label") self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label}) @@ -309,10 +309,10 @@ class CraftBeerPi(ActorAPI, SensorAPI): def actor2(self, description="", power=True, **options): def decorator(f): - print f() - print f - print options - print description + print((f())) + print(f) + print(options) + print(description) return f return decorator @@ -369,7 +369,7 @@ class CraftBeerPi(ActorAPI, SensorAPI): 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(): + for name, method in list(cls.__dict__.items()): if hasattr(method, "action"): label = method.__getattribute__("label") self.cache[key][cls.__name__]["actions"].append({"method": name, "label": label}) diff --git a/modules/core/db.py b/modules/core/db.py index ef111ba..e8402ab 100644 --- a/modules/core/db.py +++ b/modules/core/db.py @@ -79,7 +79,7 @@ class DBModel(object): 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)" % ( cls.__table_name__, cls.__priamry_key__, diff --git a/modules/core/db_mirgrate.py b/modules/core/db_mirgrate.py index bdb850e..9811bd4 100644 --- a/modules/core/db_mirgrate.py +++ b/modules/core/db_mirgrate.py @@ -1,10 +1,11 @@ import sqlite3 import os from modules import cbpi -from db import get_db +from .db import get_db -def execute_file(curernt_version, data): - if curernt_version >= data["version"]: + +def execute_file(current_version, data): + if current_version >= data["version"]: cbpi.app.logger.info("SKIP DB FILE: %s" % data["file"]) return try: @@ -19,8 +20,9 @@ def execute_file(curernt_version, data): conn.commit() except sqlite3.OperationalError as err: - print "EXCEPT" - print err + print("EXCEPT") + print(err) + @cbpi.initalizer(order=-9999) def init(app=None): @@ -28,7 +30,7 @@ def init(app=None): with cbpi.app.app_context(): conn = get_db() cur = conn.cursor() - current_version = None + current_version = 0 try: cur.execute("SELECT max(version) as m FROM schema_info") m = cur.fetchone() @@ -41,7 +43,3 @@ def init(app=None): d = {"version": int(filename[:filename.index('_')]), "file": filename} result.append(d) execute_file(current_version, d) - - - - diff --git a/modules/core/hardware.py b/modules/core/hardware.py index e329f9a..8ef6420 100644 --- a/modules/core/hardware.py +++ b/modules/core/hardware.py @@ -44,10 +44,10 @@ class SensorBase(Base): last_value = 0 def init(self): - print "INIT Base SENSOR" + print("INIT Base SENSOR") def stop(self): - print "STOP SENSOR" + print("STOP SENSOR") def data_received(self, data): @@ -86,7 +86,7 @@ class SensorActive(SensorBase): class SensorPassive(SensorBase): def init(self): - print "INIT PASSIV SENSOR" + print("INIT PASSIV SENSOR") pass def read(self): diff --git a/modules/core/step.py b/modules/core/step.py index 0084a37..d12b99b 100644 --- a/modules/core/step.py +++ b/modules/core/step.py @@ -96,7 +96,7 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI): managed_fields = [] n = False - def next(self): + def __next__(self): self.n = True def init(self): @@ -109,10 +109,10 @@ class StepBase(Timer, ActorAPI, SensorAPI, KettleAPI): pass def execute(self): - print "-------------" - print "Step Info" - print "Kettle ID: %s" % self.kettle_id - print "ID: %s" % self.id + print("-------------") + print("Step Info") + print(("Kettle ID: %s" % self.kettle_id)) + print(("ID: %s" % self.id)) def __init__(self, *args, **kwds): diff --git a/modules/fermenter/__init__.py b/modules/fermenter/__init__.py index 80c652e..e14154e 100755 --- a/modules/fermenter/__init__.py +++ b/modules/fermenter/__init__.py @@ -210,7 +210,7 @@ class FermenterView(BaseView): def toggle(self, id): fermenter = cbpi.cache.get(self.cache_key)[id] try: - print fermenter.state + print((fermenter.state)) if fermenter.state is False: # Start controller if fermenter.logic is not None: @@ -236,7 +236,7 @@ class FermenterView(BaseView): cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key).get(id)) except Exception as e: - print e + print(e) cbpi.notify("Toogle Fementer Controller failed", "Pleae check the %s configuration" % fermenter.name, type="danger", timeout=None) return ('', 500) @@ -261,7 +261,7 @@ class FermenterView(BaseView): cbpi.emit("UPDATE_FERMENTER", cbpi.cache.get(self.cache_key)[id]) def check_step(self): - for key, value in cbpi.cache["fermenter_task"].iteritems(): + for key, value in cbpi.cache["fermenter_task"].items(): try: fermenter = self.get_fermenter(key) @@ -292,7 +292,7 @@ def read_target_temps(api): :return: None """ 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") diff --git a/modules/kettle/__init__.py b/modules/kettle/__init__.py index c8bff56..f81019b 100644 --- a/modules/kettle/__init__.py +++ b/modules/kettle/__init__.py @@ -86,7 +86,7 @@ def read_target_temps(api): :return: None """ 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.initalizer() diff --git a/modules/logs/__init__.py b/modules/logs/__init__.py index b89697f..4fb94dd 100644 --- a/modules/logs/__init__.py +++ b/modules/logs/__init__.py @@ -1 +1 @@ -import endpoints \ No newline at end of file +import modules.logs.endpoints \ No newline at end of file diff --git a/modules/logs/endpoints.py b/modules/logs/endpoints.py index 5f8b29e..397545e 100644 --- a/modules/logs/endpoints.py +++ b/modules/logs/endpoints.py @@ -78,11 +78,11 @@ class LogView(FlaskView): if t == "k": kettle = cbpi.cache.get("kettle").get(id) - result = map(self.convert_chart_data_to_json, cbpi.get_controller(kettle.logic).get("class").chart(kettle)) + result = list(map(self.convert_chart_data_to_json, cbpi.get_controller(kettle.logic).get("class").chart(kettle))) if t == "f": fermenter = cbpi.cache.get("fermenter").get(id) - result = map(self.convert_chart_data_to_json, cbpi.get_fermentation_controller(fermenter.logic).get("class").chart(fermenter)) + result = list(map(self.convert_chart_data_to_json, cbpi.get_fermentation_controller(fermenter.logic).get("class").chart(fermenter))) return json.dumps(result) diff --git a/modules/recipe_import/__init__.py b/modules/recipe_import/__init__.py index f3b440c..73ead7d 100644 --- a/modules/recipe_import/__init__.py +++ b/modules/recipe_import/__init__.py @@ -1,3 +1,3 @@ -import beerxml -import kbh -import restapi \ No newline at end of file +from . import beerxml +from . import kbh +from . import restapi \ No newline at end of file diff --git a/modules/recipe_import/kbh.py b/modules/recipe_import/kbh.py index d6c23cb..02ce596 100644 --- a/modules/recipe_import/kbh.py +++ b/modules/recipe_import/kbh.py @@ -29,7 +29,7 @@ class KBH(FlaskView): result.append({"id": row[0], "name": row[1], "brewed": row[2]}) return json.dumps(result) except Exception as e: - print e + print(e) self.api.notify(headline="Failed to load KHB database", message="ERROR", type="danger") return ('', 500) finally: diff --git a/modules/sensors/__init__.py b/modules/sensors/__init__.py index dfbe744..aee5379 100755 --- a/modules/sensors/__init__.py +++ b/modules/sensors/__init__.py @@ -43,6 +43,6 @@ def read_passive_sensor(api): :return: None """ - for key, value in cbpi.cache.get("sensors").iteritems(): + for key, value in cbpi.cache.get("sensors").items(): if value.mode == "P": value.instance.read() diff --git a/modules/system/__init__.py b/modules/system/__init__.py index b89697f..c74368f 100755 --- a/modules/system/__init__.py +++ b/modules/system/__init__.py @@ -1 +1 @@ -import endpoints \ No newline at end of file +import modules.system.endpoints \ No newline at end of file diff --git a/modules/system/endpoints.py b/modules/system/endpoints.py index bfb23b3..1cca9d0 100755 --- a/modules/system/endpoints.py +++ b/modules/system/endpoints.py @@ -1,145 +1,145 @@ -import yaml -from flask import json, url_for, Response -from flask_classy import FlaskView, route -from git import Repo, Git - -from modules.app_config import cbpi - -import pprint -import time - - -class SystemView(FlaskView): - def doShutdown(self): - time.sleep(5) - from subprocess import call - call("halt") - - @route('/shutdown', methods=['POST']) - def shutdown(self): - """ - Shutdown hook - :return: HTTP 204 - """ - self.doShutdown() - - return ('', 204) - - def doReboot(self): - time.sleep(5) - from subprocess import call - call("reboot") - - @route('/reboot', methods=['POST']) - def reboot(self): - """ - Reboot hook - :return: HTTP 204 - """ - self.doReboot() - - return ('', 204) - - @route('/tags/', methods=['GET']) - def checkout_tag(self,name): - repo = Repo('./') - repo.git.reset('--hard') - o = repo.remotes.origin - o.fetch() - g = Git('./') - g.checkout(name) - cbpi.notify("Checkout successful", "Please restart the system") - return ('', 204) - - @route('/git/status', methods=['GET']) - def git_status(self): - repo = Repo('./') - o = repo.remotes.origin - o.fetch() - # Tags - tags = [] - for t in repo.tags: - tags.append({"name": t.name, "commit": str(t.commit), "date": t.commit.committed_date, - "committer": t.commit.committer.name, "message": t.commit.message}) - try: - branch_name = repo.active_branch.name - # test1 - except: - branch_name = None - - changes = [] - commits_behind = repo.iter_commits('master..origin/master') - - for c in list(commits_behind): - changes.append({"committer": c.committer.name, "message": c.message}) - - return json.dumps({"tags": tags, "headcommit": str(repo.head.commit), "branchname": branch_name, - "master": {"changes": changes}}) - - @route('/check_update', methods=['GET']) - def check_update(self): - - repo = Repo('./') - o = repo.remotes.origin - o.fetch() - changes = [] - commits_behind = repo.iter_commits('master..origin/master') - - for c in list(commits_behind): - changes.append({"committer": c.committer.name, "message": c.message}) - - return json.dumps(changes) - - @route('/git/pull', methods=['POST']) - def update(self): - repo = Repo('./') - o = repo.remotes.origin - info = o.pull() - cbpi.notify("Pull successful", "The lasted updated was downloaded. Please restart the system") - return ('', 204) - - @route('/dump', methods=['GET']) - def dump(self): - 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() -def init(cbpi): - - SystemView.api = cbpi - SystemView.register(cbpi.app, route_base='/api/system') +import yaml +from flask import json, url_for, Response +from flask_classy import FlaskView, route +from git import Repo, Git + +from modules.app_config import cbpi + +import pprint +import time + + +class SystemView(FlaskView): + def doShutdown(self): + time.sleep(5) + from subprocess import call + call("halt") + + @route('/shutdown', methods=['POST']) + def shutdown(self): + """ + Shutdown hook + :return: HTTP 204 + """ + self.doShutdown() + + return ('', 204) + + def doReboot(self): + time.sleep(5) + from subprocess import call + call("reboot") + + @route('/reboot', methods=['POST']) + def reboot(self): + """ + Reboot hook + :return: HTTP 204 + """ + self.doReboot() + + return ('', 204) + + @route('/tags/', methods=['GET']) + def checkout_tag(self,name): + repo = Repo('./') + repo.git.reset('--hard') + o = repo.remotes.origin + o.fetch() + g = Git('./') + g.checkout(name) + cbpi.notify("Checkout successful", "Please restart the system") + return ('', 204) + + @route('/git/status', methods=['GET']) + def git_status(self): + repo = Repo('./') + o = repo.remotes.origin + o.fetch() + # Tags + tags = [] + for t in repo.tags: + tags.append({"name": t.name, "commit": str(t.commit), "date": t.commit.committed_date, + "committer": t.commit.committer.name, "message": t.commit.message}) + try: + branch_name = repo.active_branch.name + # test1 + except: + branch_name = None + + changes = [] + commits_behind = repo.iter_commits('master..origin/master') + + for c in list(commits_behind): + changes.append({"committer": c.committer.name, "message": c.message}) + + return json.dumps({"tags": tags, "headcommit": str(repo.head.commit), "branchname": branch_name, + "master": {"changes": changes}}) + + @route('/check_update', methods=['GET']) + def check_update(self): + + repo = Repo('./') + o = repo.remotes.origin + o.fetch() + changes = [] + commits_behind = repo.iter_commits('master..origin/master') + + for c in list(commits_behind): + changes.append({"committer": c.committer.name, "message": c.message}) + + return json.dumps(changes) + + @route('/git/pull', methods=['POST']) + def update(self): + repo = Repo('./') + o = repo.remotes.origin + info = o.pull() + cbpi.notify("Pull successful", "The lasted updated was downloaded. Please restart the system") + return ('', 204) + + @route('/dump', methods=['GET']) + def dump(self): + return json.dumps(cbpi.cache) + + @route('/endpoints', methods=['GET']) + def endpoints(self): + import urllib.request, urllib.parse, urllib.error + 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() +def init(cbpi): + + SystemView.api = cbpi + SystemView.register(cbpi.app, route_base='/api/system') diff --git a/modules/ui/__init__.py b/modules/ui/__init__.py index b89697f..591df15 100755 --- a/modules/ui/__init__.py +++ b/modules/ui/__init__.py @@ -1 +1 @@ -import endpoints \ No newline at end of file +import modules.ui.endpoints \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2fda55c..104f8d4 100755 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,4 @@ requests==2.20.0 Werkzeug==0.11.10 httplib2==0.9.2 flask-classy==0.6.10 -GitPython==2.1.3 +GitPython From 16cd2e188c4315d6ba17f6a6387a4289352e48ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 1 Aug 2020 19:26:43 -0300 Subject: [PATCH 05/34] update ignores --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 928ac45..f42fbc9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ yarn.lock modules/ui/package-lock.json + +.python-version +upload/* +*.bak From f9a92fb4747762fc0497c444b92b07ff0d62d9c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2020 22:38:52 +0000 Subject: [PATCH 06/34] Bump python-engineio from 0.9.2 to 3.8.2.post1 Bumps [python-engineio](https://github.com/miguelgrinberg/python-engineio) from 0.9.2 to 3.8.2.post1. - [Release notes](https://github.com/miguelgrinberg/python-engineio/releases) - [Changelog](https://github.com/miguelgrinberg/python-engineio/blob/master/CHANGES.md) - [Commits](https://github.com/miguelgrinberg/python-engineio/commits) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt old mode 100755 new mode 100644 index 104f8d4..c8e55f3 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ Flask-SocketIO==2.6.2 eventlet==0.19.0 greenlet==0.4.10 python-dateutil==2.5.3 -python-engineio==0.9.2 +python-engineio==3.8.2.post1 python-mimeparse==1.5.2 python-socketio==1.4.4 PyYAML==4.2b1 From d1ff30d7d0119b1daffdd583721d26fc2c58ce8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2020 22:39:33 +0000 Subject: [PATCH 07/34] Bump httplib2 from 0.9.2 to 0.18.0 Bumps [httplib2](https://github.com/httplib2/httplib2) from 0.9.2 to 0.18.0. - [Release notes](https://github.com/httplib2/httplib2/releases) - [Changelog](https://github.com/httplib2/httplib2/blob/master/CHANGELOG) - [Commits](https://github.com/httplib2/httplib2/compare/0.9.2...v0.18.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt old mode 100755 new mode 100644 index 104f8d4..35683cb --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,6 @@ python-socketio==1.4.4 PyYAML==4.2b1 requests==2.20.0 Werkzeug==0.11.10 -httplib2==0.9.2 +httplib2==0.18.0 flask-classy==0.6.10 GitPython From 9d33987d1660c41138e0602aeb6139c073fdc9bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2020 22:43:56 +0000 Subject: [PATCH 08/34] Bump flask from 0.12.4 to 1.0 Bumps [flask](https://github.com/pallets/flask) from 0.12.4 to 1.0. - [Release notes](https://github.com/pallets/flask/releases) - [Changelog](https://github.com/pallets/flask/blob/master/CHANGES.rst) - [Commits](https://github.com/pallets/flask/compare/0.12.4...1.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 35683cb..3a2f3a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Flask==0.12.4 +Flask==1.0 Flask-SocketIO==2.6.2 eventlet==0.19.0 greenlet==0.4.10 From b5626d56d336e98774796747a8c0e538c7850613 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2020 22:44:04 +0000 Subject: [PATCH 09/34] Bump werkzeug from 0.11.10 to 0.15.3 Bumps [werkzeug](https://github.com/pallets/werkzeug) from 0.11.10 to 0.15.3. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/master/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/0.11.10...0.15.3) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 35683cb..9e3fd41 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ python-mimeparse==1.5.2 python-socketio==1.4.4 PyYAML==4.2b1 requests==2.20.0 -Werkzeug==0.11.10 +Werkzeug==0.15.3 httplib2==0.18.0 flask-classy==0.6.10 GitPython From 4093b6c41fb189f216d4d0747afadf1a03c86b68 Mon Sep 17 00:00:00 2001 From: Maxim Strinzha Date: Wed, 8 Apr 2020 12:20:16 +0300 Subject: [PATCH 10/34] Add support for SSL. Remove hardcoded http protocol for polling. bundle.js are using location.protocol instead of "http://" now. --- modules/ui/static/bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/static/bundle.js b/modules/ui/static/bundle.js index aeb963f..196be2a 100644 --- a/modules/ui/static/bundle.js +++ b/modules/ui/static/bundle.js @@ -43,7 +43,7 @@ return 3===e.nodeType?void(e.nodeValue=t):void i(e,o(t))})),e.exports=a},functio }:1===t.length?t[0]:t.reduce(function(e,t){return function(){return e(t.apply(void 0,arguments))}})}t.__esModule=!0,t.default=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t,n){function r(){y===v&&(y=v.slice())}function i(){return m}function s(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return r(),y.push(e),function(){if(t){t=!1,r();var n=y.indexOf(e);y.splice(n,1)}}}function c(e){if(!(0,a.default)(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if("undefined"==typeof e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(g)throw new Error("Reducers may not dispatch actions.");try{g=!0,m=h(m,e)}finally{g=!1}for(var t=v=y,n=0;n0&&!this.encoding){var e=this.packetBuffer.shift();this.packet(e)}},r.prototype.cleanup=function(){c("cleanup");for(var e=this.subs.length,t=0;t=this._reconnectionAttempts)c("reconnect failed"),this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var t=this.backoff.duration();c("will wait %dms before reconnect attempt",t),this.reconnecting=!0;var n=setTimeout(function(){e.skipReconnect||(c("attempting reconnect"),e.emitAll("reconnect_attempt",e.backoff.attempts),e.emitAll("reconnecting",e.backoff.attempts),e.skipReconnect||e.open(function(t){t?(c("reconnect attempt error"),e.reconnecting=!1,e.reconnect(),e.emitAll("reconnect_error",t.data)):(c("reconnect success"),e.onreconnect())}))},t);this.subs.push({destroy:function(){clearTimeout(n)}})}},r.prototype.onreconnect=function(){var e=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",e)}},function(e,t){function n(e,t,n){return e.on(t,n),{destroy:function(){e.removeListener(t,n)}}}e.exports=n},function(e,t,n){function r(e,t,n){this.io=e,this.nsp=t,this.json=this,this.ids=0,this.acks={},this.receiveBuffer=[],this.sendBuffer=[],this.connected=!1,this.disconnected=!0,n&&n.query&&(this.query=n.query),this.io.autoConnect&&this.open()}var o=n(209),i=n(78),a=n(939),s=n(356),l=n(230),u=n(131)("socket.io-client:socket"),c=n(260);e.exports=t=r;var d={connect:1,connect_error:1,connect_timeout:1,connecting:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1,ping:1,pong:1},f=i.prototype.emit;i(r.prototype),r.prototype.subEvents=function(){if(!this.subs){var e=this.io;this.subs=[s(e,"open",l(this,"onopen")),s(e,"packet",l(this,"onpacket")),s(e,"close",l(this,"onclose"))]}},r.prototype.open=r.prototype.connect=function(){return this.connected?this:(this.subEvents(),this.io.open(),"open"===this.io.readyState&&this.onopen(),this.emit("connecting"),this)},r.prototype.send=function(){var e=a(arguments);return e.unshift("message"),this.emit.apply(this,e),this},r.prototype.emit=function(e){if(d.hasOwnProperty(e))return f.apply(this,arguments),this;var t=a(arguments),n=o.EVENT;c(t)&&(n=o.BINARY_EVENT);var r={type:n,data:t};return r.options={},r.options.compress=!this.flags||!1!==this.flags.compress,"function"==typeof t[t.length-1]&&(u("emitting packet with ack id %d",this.ids),this.acks[this.ids]=t.pop(),r.id=this.ids++),this.connected?this.packet(r):this.sendBuffer.push(r),delete this.flags,this},r.prototype.packet=function(e){e.nsp=this.nsp,this.io.packet(e)},r.prototype.onopen=function(){u("transport is open - connecting"),"/"!==this.nsp&&(this.query?this.packet({type:o.CONNECT,query:this.query}):this.packet({type:o.CONNECT}))},r.prototype.onclose=function(e){u("close (%s)",e),this.connected=!1,this.disconnected=!0,delete this.id,this.emit("disconnect",e)},r.prototype.onpacket=function(e){if(e.nsp===this.nsp)switch(e.type){case o.CONNECT:this.onconnect();break;case o.EVENT:this.onevent(e);break;case o.BINARY_EVENT:this.onevent(e);break;case o.ACK:this.onack(e);break;case o.BINARY_ACK:this.onack(e);break;case o.DISCONNECT:this.ondisconnect();break;case o.ERROR:this.emit("error",e.data)}},r.prototype.onevent=function(e){var t=e.data||[];u("emitting event %j",t),null!=e.id&&(u("attaching ack callback to event"),t.push(this.ack(e.id))),this.connected?f.apply(this,t):this.receiveBuffer.push(t)},r.prototype.ack=function(e){var t=this,n=!1;return function(){if(!n){n=!0;var r=a(arguments);u("sending ack %j",r);var i=c(r)?o.BINARY_ACK:o.ACK;t.packet({type:i,id:e,data:r})}}},r.prototype.onack=function(e){var t=this.acks[e.id];"function"==typeof t?(u("calling ack %s with %j",e.id,e.data),t.apply(this,e.data),delete this.acks[e.id]):u("bad ack %s",e.id)},r.prototype.onconnect=function(){this.connected=!0,this.disconnected=!1,this.emit("connect"),this.emitBuffered()},r.prototype.emitBuffered=function(){var e;for(e=0;e0);return t}function r(e){var t=0;for(c=0;cr&&(n=r),t>=r||t>=n||0===r)return new ArrayBuffer(0);for(var o=new Uint8Array(e),i=new Uint8Array(n-t),a=t,s=0;a>8-s%1*8)){if(r=i.charCodeAt(s+=.75),r>255)throw new n;t=t<<8|r}return a}var o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";n.prototype=new Error,n.prototype.code=5,n.prototype.name="InvalidCharacterError",e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(30);e.exports=function(e,t,n){if(!t)return e;var i;if(n)i=n(t);else if(o.isURLSearchParams(t))i=t.toString();else{var a=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)&&(t+="[]"),o.isArray(e)||(e=[e]),o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),a.push(r(t)+"="+r(e))}))}),i=a.join("&")}return i&&(e+=(e.indexOf("?")===-1?"?":"&")+i),e}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(30);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,i,a){var s=[];s.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&s.push("expires="+new Date(n).toGMTString()),r.isString(o)&&s.push("path="+o),r.isString(i)&&s.push("domain="+i),a===!0&&s.push("secure"),document.cookie=s.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,n){"use strict";var r=n(30);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(30);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(30);e.exports=function(e){var t,n,o,i={};return e?(r.forEach(e.split("\n"),function(e){o=e.indexOf(":"),t=r.trim(e.substr(0,o)).toLowerCase(),n=r.trim(e.substr(o+1)),t&&(i[t]=i[t]?i[t]+", "+n:n)}),i):i}},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var l=Object.assign||function(e){for(var t=1;t=24&&(t-=24),t<0&&(t+=12),t+="",1==t.length&&(t="0"+t);var n=e.getMinutes();n+="",1==n.length&&(n="0"+n);var r=e.getUTCSeconds(),o=e.getMonth()+1;o+="",1==o.length&&(o="0"+o);var i=e.getDate();i+="",1==i.length&&(i="0"+i),this.setState({hours:t,minutes:n,seconds:r,month:o,day:i,year:e.getFullYear()})}},{key:"componentWillMount",value:function(){this.setTime()}},{key:"componentDidMount",value:function(){window.setInterval(function(){this.setTime()}.bind(this),1e3)}},{key:"render",value:function(){return u.default.createElement("span",null,this.state.day,".",this.state.month,".",this.state.year," ",this.state.hours,":",this.state.minutes)}}]),t}(l.Component);t.default=c},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n0&&h==v?u.default.createElement(d.Button,{className:"btn btn-sm btn-success",onClick:function(){f(n.id)}},u.default.createElement("i",{className:"fa fa-play"})):void 0,g>0?u.default.createElement(d.Button,{className:"btn btn-sm btn-warning",onClick:function(){f(n.id)}},u.default.createElement("i",{className:"fa fa-forward"})):void 0,u.default.createElement(d.Button,{className:"btn btn-sm btn-success",onClick:function(){e.refs.step_modal_new.wrappedInstance.show({fermenter_id:n.id,name:"",temp:"",days:"",minutes:"",hours:""})}},u.default.createElement("i",{ className:"fa fa-plus"}))),u.default.createElement("div",{className:"clearfix"})),u.default.createElement("div",{className:"list-group"},n.steps?n.steps.map(this.render_step.bind(this)):void 0,u.default.createElement(O.default,{ref:"step_modal_new",add:!0}))))}}]),t}(l.Component),M=function(e,t){var n=e.fermenter.list[t.id];return{data:n,cooler:n.cooler?e.actor.actors[n.cooler]||{}:{},heater:n.heater?e.actor.actors[n.heater]||{}:{},sensor:n.sensor?e.sensor.sensors[n.sensor]||{}:{},sensor2:n.sensor2?e.sensor.sensors[n.sensor2]||{}:{},sensor3:n.sensor3?e.sensor.sensors[n.sensor3]||{}:{},inactive_count:n.steps?n.steps.filter(function(e){return"I"===e.state}).length:0,active_count:n.steps?n.steps.filter(function(e){return"A"===e.state}).length:0,count:n.steps?n.steps.length:0,unit:(0,N.get_parameter)(e,"unit","C")}},A=function(e,t,n){return{start:function(t){e((0,p.start)(t))},stop:function(t){e((0,p.stop)(t))},toggle:function(t){e((0,f.toggle)(t))},toggle_automatic:function(t){e((0,p.toggle_automatic)(t))}}};S.contextTypes={router:u.default.PropTypes.shape({history:u.default.PropTypes.object.isRequired})},t.default=(0,c.connect)(M,A,null,{withRef:!0})(S)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){for(var n=0;n1?t-1:0),r=1;r1?t-1:0),r=1;r0?c.default.createElement(h.Button,{bsSize:"small",className:"btn-success",onClick:function(){e.props.start()}},c.default.createElement("i",{className:"fa fa-play"})):void 0,n>0&&r0&&e.jitter<=1?e.jitter:0,this.attempts=0}e.exports=n,n.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),n=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-n:e+n}return 0|Math.min(e,this.max)},n.prototype.reset=function(){this.attempts=0},n.prototype.setMin=function(e){this.ms=e},n.prototype.setMax=function(e){this.max=e},n.prototype.setJitter=function(e){this.jitter=e}},function(e,t){!function(){"use strict";for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(256),r=0;r>2],i+=e[(3&r[n])<<4|r[n+1]>>4],i+=e[(15&r[n+1])<<2|r[n+2]>>6],i+=e[63&r[n+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},t.decode=function(e){var t,r,o,i,a,s=.75*e.length,l=e.length,u=0;"="===e[e.length-1]&&(s--,"="===e[e.length-2]&&s--);var c=new ArrayBuffer(s),d=new Uint8Array(c);for(t=0;t>4,d[u++]=(15&o)<<4|i>>2,d[u++]=(3&i)<<6|63&a;return c}}()},function(e,t){(function(t){function n(e){for(var t=0;t1?t-1:0),r=1;r1?t-1:0),r=1;r0?c.default.createElement(h.Button,{bsSize:"small",className:"btn-success",onClick:function(){e.props.start()}},c.default.createElement("i",{className:"fa fa-play"})):void 0,n>0&&r0&&e.jitter<=1?e.jitter:0,this.attempts=0}e.exports=n,n.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),n=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-n:e+n}return 0|Math.min(e,this.max)},n.prototype.reset=function(){this.attempts=0},n.prototype.setMin=function(e){this.ms=e},n.prototype.setMax=function(e){this.max=e},n.prototype.setJitter=function(e){this.jitter=e}},function(e,t){!function(){"use strict";for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(256),r=0;r>2],i+=e[(3&r[n])<<4|r[n+1]>>4],i+=e[(15&r[n+1])<<2|r[n+2]>>6],i+=e[63&r[n+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},t.decode=function(e){var t,r,o,i,a,s=.75*e.length,l=e.length,u=0;"="===e[e.length-1]&&(s--,"="===e[e.length-2]&&s--);var c=new ArrayBuffer(s),d=new Uint8Array(c);for(t=0;t>4,d[u++]=(15&o)<<4|i>>2,d[u++]=(3&i)<<6|63&a;return c}}()},function(e,t){(function(t){function n(e){for(var t=0;tc;)if(s=l[c++],s!=s)return!0}else for(;u>c;c++)if((e||c in l)&&l[c]===n)return e||c||0;return!e&&-1}}},function(e,t,n){var r=n(139),o=n(37)("toStringTag"),i="Arguments"==r(function(){return arguments}()),a=function(e,t){try{return e[t]}catch(e){}};e.exports=function(e){var t,n,s;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=a(t=Object(e),o))?n:i?r(t):"Object"==(s=r(t))&&"function"==typeof t.callee?"Arguments":s}},function(e,t,n){"use strict";var r=n(47),o=n(82);e.exports=function(e,t,n){t in e?r.f(e,t,o(0,n)):e[t]=n}},function(e,t,n){var r=n(63),o=n(146),i=n(81);e.exports=function(e){var t=r(e),n=o.f;if(n)for(var a,s=n(e),l=i.f,u=0;s.length>u;)l.call(e,a=s[u++])&&t.push(a);return t}},function(e,t,n){e.exports=n(46).document&&document.documentElement},function(e,t,n){var r=n(80),o=n(37)("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(r.Array===e||i[o]===e)}},function(e,t,n){var r=n(139);e.exports=Array.isArray||function(e){return"Array"==r(e)}},function(e,t,n){var r=n(60);e.exports=function(e,t,n,o){try{return o?t(r(n)[0],n[1]):t(n)}catch(t){var i=e.return;throw void 0!==i&&r(i.call(e)),t}}},function(e,t,n){"use strict";var r=n(144),o=n(82),i=n(147),a={};n(62)(a,n(37)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:o(1,n)}),i(e,t+" Iterator")}},function(e,t,n){var r=n(37)("iterator"),o=!1;try{var i=[7][r]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var n=!1;try{var i=[7],a=i[r]();a.next=function(){return{done:n=!0}},i[r]=function(){return a},e(i)}catch(e){}return n}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){var r=n(63),o=n(41);e.exports=function(e,t){for(var n,i=o(e),a=r(i),s=a.length,l=0;s>l;)if(i[n=a[l++]]===t)return n}},function(e,t,n){var r=n(102)("meta"),o=n(79),i=n(51),a=n(47).f,s=0,l=Object.isExtensible||function(){return!0},u=!n(61)(function(){return l(Object.preventExtensions({}))}),c=function(e){a(e,r,{value:{i:"O"+ ++s,w:{}}})},d=function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!i(e,r)){if(!l(e))return"F";if(!t)return"E";c(e)}return e[r].i},f=function(e,t){if(!i(e,r)){if(!l(e))return!0;if(!t)return!1;c(e)}return e[r].w},p=function(e){return u&&h.NEED&&l(e)&&!i(e,r)&&c(e),e},h=e.exports={KEY:r,NEED:!1,fastKey:d,getWeak:f,onFreeze:p}},function(e,t,n){"use strict";var r=n(63),o=n(146),i=n(81),a=n(101),s=n(234),l=Object.assign;e.exports=!l||n(61)(function(){var e={},t={},n=Symbol(),r="abcdefghijklmnopqrst";return e[n]=7,r.split("").forEach(function(e){t[e]=e}),7!=l({},e)[n]||Object.keys(l({},t)).join("")!=r})?function(e,t){for(var n=a(e),l=arguments.length,u=1,c=o.f,d=i.f;l>u;)for(var f,p=s(arguments[u++]),h=c?r(p).concat(c(p)):r(p),m=h.length,v=0;m>v;)d.call(p,f=h[v++])&&(n[f]=p[f]);return n}:l},function(e,t,n){var r=n(47),o=n(60),i=n(63);e.exports=n(50)?Object.defineProperties:function(e,t){o(e);for(var n,a=i(t),s=a.length,l=0;s>l;)r.f(e,n=a[l++],t[n]);return e}},function(e,t,n){var r=n(41),o=n(236).f,i={}.toString,a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(e){try{return o(e)}catch(e){return a.slice()}};e.exports.f=function(e){return a&&"[object Window]"==i.call(e)?s(e):o(r(e))}},function(e,t,n){var r=n(79),o=n(60),i=function(e,t){if(o(e),!r(t)&&null!==t)throw TypeError(t+": can't set as prototype!")};e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,t,r){try{r=n(140)(Function.call,n(145).f(Object.prototype,"__proto__").set,2),r(e,[]),t=!(e instanceof Array)}catch(e){t=!0}return function(e,n){return i(e,n),t?e.__proto__=n:r(e,n),e}}({},!1):void 0),check:i}},function(e,t,n){var r=n(150),o=n(141);e.exports=function(e){return function(t,n){var i,a,s=String(o(t)),l=r(n),u=s.length;return l<0||l>=u?e?"":void 0:(i=s.charCodeAt(l),i<55296||i>56319||l+1===u||(a=s.charCodeAt(l+1))<56320||a>57343?e?s.charAt(l):i:e?s.slice(l,l+2):(i-55296<<10)+(a-56320)+65536)}}},function(e,t,n){var r=n(150),o=Math.max,i=Math.min;e.exports=function(e,t){return e=r(e),e<0?o(e+t,0):i(e,t)}},function(e,t,n){var r=n(453),o=n(37)("iterator"),i=n(80);e.exports=n(25).getIteratorMethod=function(e){if(void 0!=e)return e[o]||e["@@iterator"]||i[r(e)]}},function(e,t,n){"use strict";var r=n(140),o=n(36),i=n(101),a=n(459),s=n(457),l=n(242),u=n(454),c=n(471);o(o.S+o.F*!n(461)(function(e){Array.from(e)}),"Array",{from:function(e){var t,n,o,d,f=i(e),p="function"==typeof this?this:Array,h=arguments.length,m=h>1?arguments[1]:void 0,v=void 0!==m,y=0,g=c(f);if(v&&(m=r(m,h>2?arguments[2]:void 0,2)),void 0==g||p==Array&&s(g))for(t=l(f.length),n=new p(t);t>y;y++)u(n,y,v?m(f[y],y):f[y]);else for(d=g.call(f),n=new p;!(o=d.next()).done;y++)u(n,y,v?a(d,m,[o.value,y],!0):o.value);return n.length=y,n}})},function(e,t,n){"use strict";var r=n(451),o=n(462),i=n(80),a=n(41);e.exports=n(235)(Array,"Array",function(e,t){this._t=a(e),this._i=0,this._k=t},function(){var e=this._t,t=this._k,n=this._i++;return!e||n>=e.length?(this._t=void 0,o(1)):"keys"==t?o(0,n):"values"==t?o(0,e[n]):o(0,[n,e[n]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(e,t,n){var r=n(36);r(r.S+r.F,"Object",{assign:n(465)})},function(e,t,n){var r=n(36);r(r.S,"Object",{create:n(144)})},function(e,t,n){var r=n(36);r(r.S+r.F*!n(50),"Object",{defineProperty:n(47).f})},function(e,t,n){var r=n(41),o=n(145).f;n(239)("getOwnPropertyDescriptor",function(){return function(e,t){return o(r(e),t)}})},function(e,t,n){var r=n(101),o=n(237);n(239)("getPrototypeOf",function(){return function(e){return o(r(e))}})},function(e,t,n){var r=n(36);r(r.S,"Object",{setPrototypeOf:n(468).set})},function(e,t){},function(e,t,n){"use strict";var r=n(46),o=n(51),i=n(50),a=n(36),s=n(241),l=n(464).KEY,u=n(61),c=n(149),d=n(147),f=n(102),p=n(37),h=n(153),m=n(152),v=n(463),y=n(455),g=n(458),b=n(60),x=n(41),E=n(151),_=n(82),w=n(144),C=n(467),O=n(145),k=n(47),T=n(63),N=O.f,P=k.f,S=C.f,M=r.Symbol,A=r.JSON,D=A&&A.stringify,R="prototype",j=p("_hidden"),I=p("toPrimitive"),L={}.propertyIsEnumerable,B=c("symbol-registry"),V=c("symbols"),F=c("op-symbols"),U=Object[R],H="function"==typeof M,z=r.QObject,W=!z||!z[R]||!z[R].findChild,G=i&&u(function(){return 7!=w(P({},"a",{get:function(){return P(this,"a",{value:7}).a}})).a})?function(e,t,n){var r=N(U,t);r&&delete U[t],P(e,t,n),r&&e!==U&&P(U,t,r)}:P,Y=function(e){var t=V[e]=w(M[R]);return t._k=e,t},q=H&&"symbol"==typeof M.iterator?function(e){return"symbol"==typeof e}:function(e){return e instanceof M},X=function(e,t,n){return e===U&&X(F,t,n),b(e),t=E(t,!0),b(n),o(V,t)?(n.enumerable?(o(e,j)&&e[j][t]&&(e[j][t]=!1),n=w(n,{enumerable:_(0,!1)})):(o(e,j)||P(e,j,_(1,{})),e[j][t]=!0),G(e,t,n)):P(e,t,n)},K=function(e,t){b(e);for(var n,r=y(t=x(t)),o=0,i=r.length;i>o;)X(e,n=r[o++],t[n]);return e},$=function(e,t){return void 0===t?w(e):K(w(e),t)},Z=function(e){var t=L.call(this,e=E(e,!0));return!(this===U&&o(V,e)&&!o(F,e))&&(!(t||!o(this,e)||!o(V,e)||o(this,j)&&this[j][e])||t)},J=function(e,t){if(e=x(e),t=E(t,!0),e!==U||!o(V,t)||o(F,t)){var n=N(e,t);return!n||!o(V,t)||o(e,j)&&e[j][t]||(n.enumerable=!0),n}},Q=function(e){for(var t,n=S(x(e)),r=[],i=0;n.length>i;)o(V,t=n[i++])||t==j||t==l||r.push(t);return r},ee=function(e){for(var t,n=e===U,r=S(n?F:x(e)),i=[],a=0;r.length>a;)!o(V,t=r[a++])||n&&!o(U,t)||i.push(V[t]);return i};H||(M=function(){if(this instanceof M)throw TypeError("Symbol is not a constructor!");var e=f(arguments.length>0?arguments[0]:void 0),t=function(n){this===U&&t.call(F,n),o(this,j)&&o(this[j],e)&&(this[j][e]=!1),G(this,e,_(1,n))};return i&&W&&G(U,e,{configurable:!0,set:t}),Y(e)},s(M[R],"toString",function(){return this._k}),O.f=J,k.f=X,n(236).f=C.f=Q,n(81).f=Z,n(146).f=ee,i&&!n(143)&&s(U,"propertyIsEnumerable",Z,!0),h.f=function(e){return Y(p(e))}),a(a.G+a.W+a.F*!H,{Symbol:M});for(var te="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),ne=0;te.length>ne;)p(te[ne++]);for(var te=T(p.store),ne=0;te.length>ne;)m(te[ne++]);a(a.S+a.F*!H,"Symbol",{for:function(e){return o(B,e+="")?B[e]:B[e]=M(e)},keyFor:function(e){if(q(e))return v(B,e);throw TypeError(e+" is not a symbol!")},useSetter:function(){W=!0},useSimple:function(){W=!1}}),a(a.S+a.F*!H,"Object",{create:$,defineProperty:X,defineProperties:K,getOwnPropertyDescriptor:J,getOwnPropertyNames:Q,getOwnPropertySymbols:ee}),A&&a(a.S+a.F*(!H||u(function(){var e=M();return"[null]"!=D([e])||"{}"!=D({a:e})||"{}"!=D(Object(e))})),"JSON",{stringify:function(e){if(void 0!==e&&!q(e)){for(var t,n,r=[e],o=1;arguments.length>o;)r.push(arguments[o++]);return t=r[1],"function"==typeof t&&(n=t),!n&&g(t)||(t=function(e,t){if(n&&(t=n.call(this,e,t)),!q(t))return t}),r[1]=t,D.apply(A,r)}}}),M[R][I]||n(62)(M[R],I,M[R].valueOf),d(M,"Symbol"),d(Math,"Math",!0),d(r.JSON,"JSON",!0)},function(e,t,n){var r=n(36),o=n(240)(!0);r(r.S,"Object",{entries:function(e){return o(e)}})},function(e,t,n){var r=n(36),o=n(240)(!1);r(r.S,"Object",{values:function(e){return o(e)}})},function(e,t,n){n(152)("asyncIterator")},function(e,t,n){n(152)("observable")},function(e,t,n){n(473);for(var r=n(46),o=n(62),i=n(80),a=n(37)("toStringTag"),s=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],l=0;l<5;l++){var u=s[l],c=r[u],d=c&&c.prototype;d&&!d[a]&&o(d,a,u),i[u]=i.Array}},function(e,t,n){"use strict";var r=n(1),o=n(244);if("undefined"==typeof r)throw Error("create-react-class could not find the React object. If you are using script tags, make sure that React is being loaded before create-react-class.");var i=(new r.Component).updater;e.exports=o(r.Component,r.isValidElement,i)},function(e,t){"use strict";function n(){var e=document.createElement("div"),t=e.style;"AnimationEvent"in window||delete i.animationend.animation,"TransitionEvent"in window||delete i.transitionend.transition;for(var n in i)if(i.hasOwnProperty(n)){var r=i[n];for(var o in r)if(o in t){a.push(r[o]);break}}}function r(e,t,n){e.addEventListener(t,n,!1)}function o(e,t,n){e.removeEventListener(t,n,!1)}Object.defineProperty(t,"__esModule",{value:!0});var i={transitionend:{transition:"transitionend",WebkitTransition:"webkitTransitionEnd",MozTransition:"mozTransitionEnd",OTransition:"oTransitionEnd",msTransition:"MSTransitionEnd"},animationend:{animation:"animationend",WebkitAnimation:"webkitAnimationEnd",MozAnimation:"mozAnimationEnd",OAnimation:"oAnimationEnd",msAnimation:"MSAnimationEnd"}},a=[];"undefined"!=typeof window&&"undefined"!=typeof document&&n();var s={addEndEventListener:function(e,t){return 0===a.length?void window.setTimeout(t,0):void a.forEach(function(n){r(e,n,t)})},endEvents:a,removeEndEventListener:function(e,t){0!==a.length&&a.forEach(function(n){o(e,n,t)})}};t.default=s,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){for(var n=window.getComputedStyle(e,null),r="",o=0;o Date: Sun, 2 Aug 2020 19:54:44 -0300 Subject: [PATCH 11/34] updated plugins list --- modules/addon/endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/addon/endpoints.py b/modules/addon/endpoints.py index 44c8066..78fda0a 100644 --- a/modules/addon/endpoints.py +++ b/modules/addon/endpoints.py @@ -133,7 +133,7 @@ def plugins(): Read the central plugin yaml to get a list of all official plugins :return: """ - response = requests.get("https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins.yaml") + response = requests.get("https://raw.githubusercontent.com/jpgimenez/craftbeerpi-plugins/master/plugins.yaml") cbpi.cache["plugins"] = merge(yaml.load(response.text), cbpi.cache["plugins"]) for key, value in cbpi.cache["plugins"].items(): value["installed"] = os.path.isdir("./modules/plugins/%s/" % (key)) From 33305839032579cd54c57496c0ab9777e6ad2439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 3 Aug 2020 19:32:43 -0300 Subject: [PATCH 12/34] migrate to Flask 1.1 and Python 3.8 --- Dockerfile | 2 +- modules/base_plugins/gpio_actor/__init__.py | 6 ++-- modules/core/core.py | 4 +-- modules/notification/__init__.py | 2 +- requirements.txt | 37 +++++++++++++-------- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Dockerfile b/Dockerfile index 455b74c..dd18886 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Dockerfile for development on a pc/mac -FROM python:3.5 +FROM python:3.8 EXPOSE 5000 diff --git a/modules/base_plugins/gpio_actor/__init__.py b/modules/base_plugins/gpio_actor/__init__.py index cedf43f..e7726b4 100644 --- a/modules/base_plugins/gpio_actor/__init__.py +++ b/modules/base_plugins/gpio_actor/__init__.py @@ -116,12 +116,12 @@ class DummyPWM(ActorBase): :return: ''' self.power = int(power) if power is not None else 100 - print "DummyPWM ON %s" % self.power + print("DummyPWM ON %s" % self.power) def off(self): self.power = 100 - print "OFF" + print("OFF") def set_power(self, power): self.power = int(power) - print "DummyPWM POWER %s" % self.power + print("DummyPWM POWER %s" % self.power) diff --git a/modules/core/core.py b/modules/core/core.py index 66fb0e0..479c61f 100644 --- a/modules/core/core.py +++ b/modules/core/core.py @@ -378,12 +378,12 @@ class CraftBeerPi(ActorAPI, SensorAPI): # Event Bus - def event(self, name, async=False): + def event(self, name, use_async=False): def real_decorator(function): if self.eventbus.get(name) is None: self.eventbus[name] = [] - self.eventbus[name].append({"function": function, "async": async}) + self.eventbus[name].append({"function": function, "async": use_async}) def wrapper(*args, **kwargs): return function(*args, **kwargs) return wrapper diff --git a/modules/notification/__init__.py b/modules/notification/__init__.py index 2f440cb..97e06ec 100644 --- a/modules/notification/__init__.py +++ b/modules/notification/__init__.py @@ -24,7 +24,7 @@ class NotificationView(FlaskView): cbpi.cache["messages"].pop(idx) return ('', 204) -@cbpi.event("MESSAGE", async=True) +@cbpi.event("MESSAGE", use_async=True) def messageEvent(message, **kwargs): """ React on message event. add the message to the cache and push the message to the clients diff --git a/requirements.txt b/requirements.txt index f28fffb..91b52cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,23 @@ -Flask==1.0 -Flask-SocketIO==2.6.2 -eventlet==0.19.0 -greenlet==0.4.10 -python-dateutil==2.5.3 -python-engineio==3.8.2.post1 -python-mimeparse==1.5.2 -python-socketio==1.4.4 -PyYAML==4.2b1 -requests==2.20.0 -Werkzeug==0.15.3 -httplib2==0.18.0 -flask-classy==0.6.10 -GitPython +attrs==19.3.0 +certifi==2020.6.20 +chardet==3.0.4 +click==7.1.2 +coverage==5.2.1 +Flask==1.1.2 +Flask-Classy==0.6.10 +Flask-SocketIO==2.6.2 +gitdb==4.0.5 +GitPython==3.1.7 +greenlet==0.4.16 +idna==2.10 +itsdangerous==1.1.0 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +python-engineio==3.8.2.post1 +python-socketio==1.4.4 +PyYAML==5.3.1 +requests==2.24.0 +six==1.15.0 +smmap==3.0.4 +urllib3==1.25.10 +Werkzeug==1.0.1 From b5eb6999f7c7d08eb2fcc825ea13fe5305e906f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 3 Aug 2020 22:09:43 -0300 Subject: [PATCH 13/34] lint --- .pre-commit-config.yaml | 16 +++++ modules/base_plugins/gpio_actor/__init__.py | 73 ++++++++++++++------- requirements-dev.txt | 3 + requirements.txt | 20 +++--- 4 files changed, 81 insertions(+), 31 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 requirements-dev.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..67017dc --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/pycqa/pylint + rev: pylint-2.5.3 + hooks: + - id: pylint + stages: [commit] + additional_dependencies: [pylint-flask] diff --git a/modules/base_plugins/gpio_actor/__init__.py b/modules/base_plugins/gpio_actor/__init__.py index e7726b4..efeb0cc 100644 --- a/modules/base_plugins/gpio_actor/__init__.py +++ b/modules/base_plugins/gpio_actor/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +""" base gpio actors """ import time from modules import cbpi @@ -6,19 +7,25 @@ from modules.core.hardware import ActorBase, SensorPassive, SensorActive from modules.core.props import Property try: - import RPi.GPIO as GPIO + import RPi.GPIO as GPIO # pylint: disable=import-error GPIO.setmode(GPIO.BCM) -except Exception as e: - print(e) - pass - +except ModuleNotFoundError as exp: + print(exp) @cbpi.actor 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], description="GPIO to which the actor is connected") + """ + Simple GPIO Actor + """ + 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): GPIO.setup(int(self.gpio), GPIO.OUT) @@ -32,20 +39,28 @@ class GPIOSimple(ActorBase): print("GPIO OFF") GPIO.output(int(self.gpio), 0) + @cbpi.actor 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], description="GPIO to which the actor is connected") + """ + GPIO Actor with PWM support + """ + 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") frequency = Property.Number("Frequency (Hz)", configurable=True) - p = None + gpio_inst = None power = 100 # duty cycle def init(self): GPIO.setup(int(self.gpio), GPIO.OUT) GPIO.output(int(self.gpio), 0) - def on(self, power=None): if power is not None: self.power = int(power) @@ -53,29 +68,37 @@ class GPIOPWM(ActorBase): if self.frequency is None: self.frequency = 0.5 # 2 sec - self.p = GPIO.PWM(int(self.gpio), float(self.frequency)) - self.p.start(int(self.power)) + self.gpio_inst = GPIO.PWM(int(self.gpio), float(self.frequency)) + self.gpio_inst.start(int(self.power)) def set_power(self, power): ''' Optional: Set the power of your actor :param power: int value between 0 - 100 - :return: + :return: ''' - if self.p: + if self.gpio_inst: if power is not None: self.power = int(power) - self.p.ChangeDutyCycle(self.power) + self.gpio_inst.ChangeDutyCycle(self.power) def off(self): print("GPIO OFF") - self.p.stop() + self.gpio_inst.stop() @cbpi.actor 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], description="GPIO to which the actor is connected") + """ + Relay board Actor + """ + 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): GPIO.setup(int(self.gpio), GPIO.OUT) @@ -89,24 +112,30 @@ class RelayBoard(ActorBase): GPIO.output(int(self.gpio), 1) + @cbpi.actor class Dummy(ActorBase): - + """ + Simple Dummy Actor + """ def on(self, power=100): ''' Code to switch on the actor :param power: int value between 0 - 100 - :return: + :return: ''' print("ON") def off(self): print("OFF") + @cbpi.actor class DummyPWM(ActorBase): - + """ + Dummy Actor with PWM support + """ power = 100 def on(self, power=100): diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..a82b166 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +coverage==5.2.1 +pre-commit +pylint diff --git a/requirements.txt b/requirements.txt index 91b52cf..df1e105 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,23 +1,25 @@ -attrs==19.3.0 -certifi==2020.6.20 -chardet==3.0.4 -click==7.1.2 -coverage==5.2.1 Flask==1.1.2 Flask-Classy==0.6.10 Flask-SocketIO==2.6.2 -gitdb==4.0.5 -GitPython==3.1.7 greenlet==0.4.16 + +python-engineio==3.8.2.post1 +python-socketio==1.4.4 + +attrs==19.3.0 +certifi==2020.6.20 +chardet==3.0.4 +click==7.1.2 idna==2.10 itsdangerous==1.1.0 Jinja2==2.11.2 MarkupSafe==1.1.1 -python-engineio==3.8.2.post1 -python-socketio==1.4.4 PyYAML==5.3.1 requests==2.24.0 six==1.15.0 smmap==3.0.4 urllib3==1.25.10 Werkzeug==1.0.1 + +gitdb==4.0.5 +GitPython==3.1.7 From e03ab9c1f13e8d06deb279be7d4143b7a37192ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 3 Aug 2020 22:11:39 -0300 Subject: [PATCH 14/34] ignore .vscode --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f42fbc9..6d0b415 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ modules/ui/package-lock.json .python-version upload/* *.bak +.vscode From f14b3c9cfc13d9f6a3d2ed65017d642bcb997f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 3 Aug 2020 23:49:05 -0300 Subject: [PATCH 15/34] add eventlet dep... --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index df1e105..97d529a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ Flask==1.1.2 Flask-Classy==0.6.10 Flask-SocketIO==2.6.2 greenlet==0.4.16 +eventlet==0.26.1 python-engineio==3.8.2.post1 python-socketio==1.4.4 From 33626e221ec1acbcf21366829d5cbac82e57dd6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 3 Aug 2020 23:50:02 -0300 Subject: [PATCH 16/34] python3 support --- modules/addon/endpoints.py | 4 ++-- modules/system/endpoints.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/addon/endpoints.py b/modules/addon/endpoints.py index 44c8066..10049fe 100644 --- a/modules/addon/endpoints.py +++ b/modules/addon/endpoints.py @@ -133,8 +133,8 @@ def plugins(): Read the central plugin yaml to get a list of all official plugins :return: """ - response = requests.get("https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins.yaml") - cbpi.cache["plugins"] = merge(yaml.load(response.text), cbpi.cache["plugins"]) + response = requests.get("https://raw.githubusercontent.com/jpgimenez/craftbeerpi-plugins/master/plugins.yaml") + cbpi.cache["plugins"] = merge(yaml.safe_load(response.text), cbpi.cache["plugins"]) for key, value in cbpi.cache["plugins"].items(): value["installed"] = os.path.isdir("./modules/plugins/%s/" % (key)) diff --git a/modules/system/endpoints.py b/modules/system/endpoints.py index 1cca9d0..31f90ef 100755 --- a/modules/system/endpoints.py +++ b/modules/system/endpoints.py @@ -130,8 +130,8 @@ class SystemView(FlaskView): endpoints[rule.rule][m] = dict(summary="", description="", consumes=["application/json"],produces=["application/json"]) with open("config/version.yaml", 'r') as stream: + y = yaml.safe_load(stream) - y = yaml.load(stream) pprint.pprint(y) pprint.pprint(re) return Response(yaml.dump(re), mimetype='text/yaml') From 8ded4a1b1d3096bc44db771dc82dad24484ae165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 3 Aug 2020 23:53:07 -0300 Subject: [PATCH 17/34] update python-engineio --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 97d529a..fba8235 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ Flask-SocketIO==2.6.2 greenlet==0.4.16 eventlet==0.26.1 -python-engineio==3.8.2.post1 +python-engineio==3.13.1 python-socketio==1.4.4 attrs==19.3.0 From a05c6e67351ea8ac2ad1f6e0d7afebb61f7a2bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Wed, 5 Aug 2020 20:15:25 -0300 Subject: [PATCH 18/34] use ImportError that is compatible with python 3.5 --- modules/base_plugins/gpio_actor/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/base_plugins/gpio_actor/__init__.py b/modules/base_plugins/gpio_actor/__init__.py index efeb0cc..0cbbc2f 100644 --- a/modules/base_plugins/gpio_actor/__init__.py +++ b/modules/base_plugins/gpio_actor/__init__.py @@ -10,7 +10,7 @@ try: import RPi.GPIO as GPIO # pylint: disable=import-error GPIO.setmode(GPIO.BCM) -except ModuleNotFoundError as exp: +except ImportError as exp: print(exp) From 0a1b8245a6c0f19deea6fd63044c8c9d84497094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Thu, 6 Aug 2020 00:22:41 -0300 Subject: [PATCH 19/34] change update_addon to delete/download if the repo_url was changed --- modules/addon/endpoints.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/addon/endpoints.py b/modules/addon/endpoints.py index 10049fe..49ef4b0 100644 --- a/modules/addon/endpoints.py +++ b/modules/addon/endpoints.py @@ -145,9 +145,9 @@ def plugins(): def download_addon(name): plugin = cbpi.cache["plugins"].get(name) - plugin["loading"] = True if plugin is None: return ('', 404) + plugin["loading"] = True try: Repo.clone_from(plugin.get("repo_url"), "./modules/plugins/%s/" % (name)) cbpi.notify("Download successful", "Plugin %s downloaded successfully" % name) @@ -158,9 +158,28 @@ def download_addon(name): @blueprint.route('//update', methods=['POST']) def update_addon(name): + """ + Updates a addon + + :param name: plugin name + :return: HTTP 204 if ok - HTTP 500 if plugin not exists + """ + plugin = cbpi.cache["plugins"].get(name) + if plugin is None: + return ('', 404) + plugin["loading"] = True + repo = Repo("./modules/plugins/%s/" % (name)) - o = repo.remotes.origin - info = o.pull() + if repo.remotes.origin.url == plugin.get('repo_url'): + o = repo.remotes.origin + _info = o.pull() + else: + # url has changed the plugin needs to be re-downloaded + deletePlugin(name) + return download_addon(name) + + reload(name) + plugin["loading"] = False cbpi.notify("Plugin Updated", "Plugin %s updated successfully. Please restart the system" % name) return ('', 204) From 4d0807dc60b0ba6d50badb0a094293cf841d82b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 8 Aug 2020 10:01:04 -0300 Subject: [PATCH 20/34] fix csv reading --- modules/logs/endpoints.py | 6 +++--- requirements.txt | 1 + run.py | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/logs/endpoints.py b/modules/logs/endpoints.py index 397545e..4023494 100644 --- a/modules/logs/endpoints.py +++ b/modules/logs/endpoints.py @@ -1,3 +1,4 @@ +import csv import datetime import os from flask import Blueprint, request, send_from_directory, json @@ -20,9 +21,8 @@ class LogView(FlaskView): filename = "./logs/action.log" if os.path.isfile(filename) == False: return - import csv array = [] - with open(filename, 'rb') as f: + with open(filename, 'rb', encoding='utf-8') as f: reader = csv.reader(f) for row in reader: try: @@ -56,7 +56,7 @@ class LogView(FlaskView): import csv array = [] - with open(filename, 'rb') as f: + with open(filename, 'rb', encoding='utf-8') as f: reader = csv.reader(f) for row in reader: try: diff --git a/requirements.txt b/requirements.txt index fba8235..184e0c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ Flask==1.1.2 Flask-Classy==0.6.10 Flask-SocketIO==2.6.2 +flask-envconfig greenlet==0.4.16 eventlet==0.26.1 diff --git a/run.py b/run.py index 79ea7d5..84419dc 100755 --- a/run.py +++ b/run.py @@ -8,4 +8,3 @@ except ValueError: port = 5000 socketio.run(app, host='0.0.0.0', port=port) - From 93aaa66a42fff181419d0786507141113f6e7d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 8 Aug 2020 12:32:30 -0300 Subject: [PATCH 21/34] lint endpoints --- .pre-commit-config.yaml | 19 +++-- modules/logs/endpoints.py | 154 ++++++++++++++++++++++++++++---------- 2 files changed, 128 insertions(+), 45 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 67017dc..19be268 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,9 +8,18 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - - repo: https://github.com/pycqa/pylint - rev: pylint-2.5.3 + +# - repo: https://github.com/pycqa/pylint +# rev: pylint-2.5.3 +# hooks: +# - id: pylint +# stages: [commit] +# additional_dependencies: [pylint-flask] + + - repo: local hooks: - - id: pylint - stages: [commit] - additional_dependencies: [pylint-flask] + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] diff --git a/modules/logs/endpoints.py b/modules/logs/endpoints.py index 4023494..a740974 100644 --- a/modules/logs/endpoints.py +++ b/modules/logs/endpoints.py @@ -1,15 +1,29 @@ +# -*- coding: utf-8 -*- +""" +enpoints for logging +""" import csv import datetime import os -from flask import Blueprint, request, send_from_directory, json +import re + +from flask import send_from_directory, json from flask_classy import FlaskView, route from modules import cbpi class LogView(FlaskView): + """ + View for logging + """ @route('/', methods=['GET']) - def get_all_logfiles(self): + def get_all_logfiles(self): # pylint: disable=no-self-use + """ + get all log files + + :return: json for all logs + """ result = [] for filename in os.listdir("./logs"): if filename.endswith(".log"): @@ -17,90 +31,150 @@ class LogView(FlaskView): return json.dumps(result) @route('/actions') - def actions(self): + def actions(self): # pylint: disable=no-self-use + """ + get actions log + + :return: json for actions log + """ filename = "./logs/action.log" - if os.path.isfile(filename) == False: - return + if not os.path.isfile(filename): + return ('File not found', 404) array = [] - with open(filename, 'rb', encoding='utf-8') as f: - reader = csv.reader(f) + with open(filename, 'rb', encoding='utf-8') as csv_file: + reader = csv.reader(csv_file) for row in reader: try: - array.append([int((datetime.datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S") - datetime.datetime(1970, 1, 1)).total_seconds()) * 1000, row[1]]) - except: + array.append([ + int((datetime.datetime.strptime( + row[0], "%Y-%m-%d %H:%M:%S") - + datetime.datetime(1970, 1, 1)).total_seconds()) * + 1000, row[1] + ]) + except IndexError: pass return json.dumps(array) @route('/', methods=["DELETE"]) def clearlog(self, file): """ - Overload delete method to shutdown sensor before delete - :param id: sensor id + log delete + :param file: log file name :return: HTTP 204 """ if not self.check_filename(file): return ('File Not Found', 404) filename = "./logs/%s" % file - if os.path.isfile(filename) == True: + if os.path.isfile(filename): os.remove(filename) cbpi.notify("log deleted succesfully", "") else: cbpi.notify("Failed to delete log", "", type="danger") return ('', 204) - def read_log_as_json(self, type, id): - filename = "./logs/%s_%s.log" % (type, id) - if os.path.isfile(filename) == False: - return + def read_log_as_json(self, log_type, log_id): # pylint: disable=no-self-use + """ + :param log_type: log type + :param log_id: log id + + :return: log as array + """ + filename = "./logs/%s_%s.log" % (log_type, log_id) + if not os.path.isfile(filename): + return ('File not found', 404) - import csv array = [] - with open(filename, 'rb', encoding='utf-8') as f: - reader = csv.reader(f) + with open(filename, 'rb', encoding='utf-8') as csv_file: + reader = csv.reader(csv_file) for row in reader: try: - array.append([int((datetime.datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S") - datetime.datetime(1970, 1, 1)).total_seconds()) * 1000, float(row[1])]) - except: + array.append([ + int((datetime.datetime.strptime( + row[0], "%Y-%m-%d %H:%M:%S") - + datetime.datetime(1970, 1, 1)).total_seconds()) * + 1000, + float(row[1]) + ]) + except IndexError: pass 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"])} + """ + :param chart_data: data for a chart - @route('//', methods=["POST"]) - def get_logs_as_json(self, t, id): - data = request.json - result = [] - if t == "s": - name = cbpi.cache.get("sensors").get(id).name - result.append({"name": name, "data": self.read_log_as_json("sensor", id)}) + :return: json for chart data + """ + return { + "name": + chart_data["name"], + "data": + self.read_log_as_json(chart_data["data_type"], + chart_data["data_id"]) + } - if t == "k": - kettle = cbpi.cache.get("kettle").get(id) - result = list(map(self.convert_chart_data_to_json, cbpi.get_controller(kettle.logic).get("class").chart(kettle))) + @route('//', methods=["POST"]) + def get_logs_as_json(self, log_type, log_id): + """ + :param log_type: log type + :param log_id: log id - if t == "f": - fermenter = cbpi.cache.get("fermenter").get(id) - result = list(map(self.convert_chart_data_to_json, cbpi.get_fermentation_controller(fermenter.logic).get("class").chart(fermenter))) + :return: log as array + """ + result = [] + if log_type == "s": + name = cbpi.cache.get("sensors").get(log_id).name + result.append({ + "name": name, + "data": self.read_log_as_json("sensor", log_id) + }) + + if log_type == "k": + kettle = cbpi.cache.get("kettle").get(log_id) + result = list( + map( + self.convert_chart_data_to_json, + cbpi.get_controller( + kettle.logic).get("class").chart(kettle))) + + if log_type == "f": + fermenter = cbpi.cache.get("fermenter").get(log_id) + result = list( + map( + self.convert_chart_data_to_json, + cbpi.get_fermentation_controller( + fermenter.logic).get("class").chart(fermenter))) return json.dumps(result) @route('/download/') @cbpi.nocache def download(self, file): + """ + :param file: log file name + + :return: log data + """ if not self.check_filename(file): return ('File Not Found', 404) - return send_from_directory('../logs', file, as_attachment=True, attachment_filename=file) + return send_from_directory('../logs', + file, + as_attachment=True, + attachment_filename=file) - def check_filename(self, name): - import re + def check_filename(self, name): # pylint: disable=no-self-use + """ + :param name: log file name + :return: bool + """ pattern = re.compile('^([A-Za-z0-9-_])+.log$') - return True if pattern.match(name) else False + return bool(pattern.match(name)) + @cbpi.initalizer() -def init(app): +def init(app): # pylint: disable=unused-argument """ Initializer for the message module :param app: the flask app From a6ed3f7c9e46b2876a52a943afb447aa3152a9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 8 Aug 2020 17:25:29 -0300 Subject: [PATCH 22/34] add settings to simulate a real sensor... --- modules/base_plugins/dummy_temp/__init__.py | 39 ++++++++++----------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/modules/base_plugins/dummy_temp/__init__.py b/modules/base_plugins/dummy_temp/__init__.py index 9ef3a94..3c89cfb 100644 --- a/modules/base_plugins/dummy_temp/__init__.py +++ b/modules/base_plugins/dummy_temp/__init__.py @@ -12,17 +12,18 @@ from modules.core.props import Property class DummyTempSensor(SensorActive): temp = Property.Number("Temperature", configurable=True, default_value=5, description="Dummy Temperature as decimal value") + inc = Property.Number("Auto increase", configurable=True, default_value=0.5, description="Dummy Temperature increase as decimal value") + max_temp = Property.Number("Max temperature", configurable=True, default_value='100', description="Dummy Max. Temperature as decimal value") + min_temp = Property.Number("Min temperature", configurable=True, default_value='0', description="Dummy Min. Temperature as decimal value") + current_temp = None - @cbpi.action("My Custom Action") - def my_action(self): - print("HELLO WORLD") - pass + @cbpi.action("Reset") + def reset(self): + self.current_temp = None - 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" + @cbpi.action("Toogle Up/Down") + def toogle(self): + self.inc *= -1 def stop(self): SensorActive.stop(self) @@ -30,24 +31,20 @@ class DummyTempSensor(SensorActive): def execute(self): ''' Active sensor has to handle his own loop - :return: + :return: ''' while self.is_running() is True: - self.data_received(self.temp) + if not self.current_temp: + self.current_temp = self.temp + self.data_received(self.current_temp) + new_temp = float(self.current_temp) + float(self.inc) + if float(self.min_temp) <= new_temp <= float(self.max_temp): + self.current_temp = '%.2f' % new_temp self.sleep(5) @classmethod def init_global(cls): ''' Called one at the startup for all sensors - :return: + :return: ''' - - - - - - - - - From eb1c5af1a51dec4b8c63653616ebfb022938c29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 8 Aug 2020 17:30:38 -0300 Subject: [PATCH 23/34] lint --- modules/base_plugins/dummy_temp/__init__.py | 42 +++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/modules/base_plugins/dummy_temp/__init__.py b/modules/base_plugins/dummy_temp/__init__.py index 3c89cfb..c5f0478 100644 --- a/modules/base_plugins/dummy_temp/__init__.py +++ b/modules/base_plugins/dummy_temp/__init__.py @@ -1,31 +1,59 @@ # -*- coding: utf-8 -*- +""" +Dummy sensors +""" import subprocess import time from modules import cbpi, socketio -from modules.core.hardware import SensorActive -from modules import cbpi +from modules.core.hardware import SensorActive from modules.core.props import Property @cbpi.sensor class DummyTempSensor(SensorActive): - - temp = Property.Number("Temperature", configurable=True, default_value=5, description="Dummy Temperature as decimal value") - inc = Property.Number("Auto increase", configurable=True, default_value=0.5, description="Dummy Temperature increase as decimal value") - max_temp = Property.Number("Max temperature", configurable=True, default_value='100', description="Dummy Max. Temperature as decimal value") - min_temp = Property.Number("Min temperature", configurable=True, default_value='0', description="Dummy Min. Temperature as decimal value") + """ + Dummy temperature sensor + """ + temp = Property.Number("Temperature", + configurable=True, + default_value=5, + description="Dummy Temperature as decimal value") + inc = Property.Number( + "Auto increase", + configurable=True, + default_value=0.5, + description="Dummy Temperature increase as decimal value") + max_temp = Property.Number( + "Max temperature", + configurable=True, + default_value='100', + description="Dummy Max. Temperature as decimal value") + min_temp = Property.Number( + "Min temperature", + configurable=True, + default_value='0', + description="Dummy Min. Temperature as decimal value") current_temp = None @cbpi.action("Reset") def reset(self): + """ + reset to default temp + """ self.current_temp = None @cbpi.action("Toogle Up/Down") def toogle(self): + """ + toogle inc from up/down + """ self.inc *= -1 def stop(self): + """ + stop sensor + """ SensorActive.stop(self) def execute(self): From 387e50843f60eb318eff02e41686c9cd0baaa5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 8 Aug 2020 17:38:18 -0300 Subject: [PATCH 24/34] add dev deps --- requirements-dev.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index a82b166..3c37c08 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,5 @@ coverage==5.2.1 pre-commit pylint +pylint-flask +black From df8c6a7084b30f501c2132e5dc92303cfe17d864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sat, 8 Aug 2020 19:17:24 -0300 Subject: [PATCH 25/34] fix charts --- modules/logs/endpoints.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/logs/endpoints.py b/modules/logs/endpoints.py index a740974..d787ea9 100644 --- a/modules/logs/endpoints.py +++ b/modules/logs/endpoints.py @@ -41,7 +41,7 @@ class LogView(FlaskView): if not os.path.isfile(filename): return ('File not found', 404) array = [] - with open(filename, 'rb', encoding='utf-8') as csv_file: + with open(filename, 'r', encoding='utf-8') as csv_file: reader = csv.reader(csv_file) for row in reader: try: @@ -85,7 +85,7 @@ class LogView(FlaskView): return ('File not found', 404) array = [] - with open(filename, 'rb', encoding='utf-8') as csv_file: + with open(filename, 'r', encoding='utf-8') as csv_file: reader = csv.reader(csv_file) for row in reader: try: @@ -114,7 +114,7 @@ class LogView(FlaskView): chart_data["data_id"]) } - @route('//', methods=["POST"]) + @route('//', methods=["POST"]) def get_logs_as_json(self, log_type, log_id): """ :param log_type: log type From a497c01ebc4ce4553d18e41a6aa6e81f074a0b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sun, 9 Aug 2020 15:59:07 -0300 Subject: [PATCH 26/34] fix up/down toogle --- modules/base_plugins/dummy_temp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/base_plugins/dummy_temp/__init__.py b/modules/base_plugins/dummy_temp/__init__.py index c5f0478..bc81d85 100644 --- a/modules/base_plugins/dummy_temp/__init__.py +++ b/modules/base_plugins/dummy_temp/__init__.py @@ -48,7 +48,7 @@ class DummyTempSensor(SensorActive): """ toogle inc from up/down """ - self.inc *= -1 + self.inc = float(self.inc) * -1 def stop(self): """ From d4823372b7802c3fa93e50ebdae5f882343ddfe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sun, 9 Aug 2020 16:01:50 -0300 Subject: [PATCH 27/34] * migrate install to python3 * add Vagrantfile to test installation --- Vagrantfile | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 10 ++++---- run.py | 12 ++++++---- 3 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 Vagrantfile diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..8633ad4 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,69 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://vagrantcloud.com/search. + config.vm.box = "debian/buster64" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # NOTE: This will enable public access to the opened port + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine and only allow access + # via 127.0.0.1 to disable public access + config.vm.network "forwarded_port", guest: 5000, host: 5000, host_ip: "127.0.0.1" + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Enable provisioning with a shell script. Additional provisioners such as + # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the + # documentation for more information about their specific syntax and use. + config.vm.provision "shell", inline: <<-SHELL + sudo /vagrant/install.sh + SHELL +end diff --git a/install.sh b/install.sh index 1d46095..1139002 100755 --- a/install.sh +++ b/install.sh @@ -35,11 +35,11 @@ show_menu () { apt-get -y update; apt-get -y upgrade; fi - apt-get -y install python-setuptools - easy_install pip - apt-get -y install python-dev - apt-get -y install libpcre3-dev - pip install -r requirements.txt +# apt-get -y install python-setuptools +# easy_install pip + apt-get -y install python3-pip python3-dev + apt-get -y install libpcre3-dev git + pip3 install -r requirements.txt confirmAnswer "Would you like to add active 1-wire support at your Raspberry PI now? IMPORTANT: The 1-wire thermometer must be conneted to GPIO 4!" if [ $? = 0 ]; then diff --git a/run.py b/run.py index 84419dc..8616882 100755 --- a/run.py +++ b/run.py @@ -1,10 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +""" +cbpi runner +""" + from modules import socketio, app, cbpi try: - port = int(cbpi.get_config_parameter('port', '5000')) + PORT = int(cbpi.get_config_parameter('port', '5000')) except ValueError: - port = 5000 + PORT = 5000 -socketio.run(app, host='0.0.0.0', port=port) +socketio.run(app, host='0.0.0.0', port=PORT) From 79520bda7ae6120a21baf382b9cf7f741d084dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Sun, 9 Aug 2020 20:49:23 -0300 Subject: [PATCH 28/34] typo --- modules/core/core.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/core/core.py b/modules/core/core.py index 479c61f..f6910ee 100644 --- a/modules/core/core.py +++ b/modules/core/core.py @@ -83,7 +83,7 @@ class SensorAPI(object): def init_sensors(self): ''' Initialize all sensors - :return: + :return: ''' self.app.logger.info("Init Sensors") @@ -108,15 +108,15 @@ class SensorAPI(object): def init_sensor(self, id): ''' initialize sensor by id - :param id: - :return: + :param id: + :return: ''' def start_active_sensor(instance): ''' start active sensors as background job - :param instance: - :return: + :param instance: + :return: ''' instance.execute() @@ -447,12 +447,12 @@ class CraftBeerPi(ActorAPI, SensorAPI): def run_init(self): ''' call all initialziers after startup - :return: + :return: ''' self.app.logger.info("Invoke Init") self.cache["init"] = sorted(self.cache["init"], key=lambda k: k['order']) for i in self.cache.get("init"): - self.app.logger.info("INITIALIZER - METHOD %s PAHT %s: " % (i.get("function").__name__, str(inspect.getmodule(i.get("function")).__file__) )) + self.app.logger.info("INITIALIZER - METHOD %s PATH %s: " % (i.get("function").__name__, str(inspect.getmodule(i.get("function")).__file__) )) i.get("function")(self) @@ -461,10 +461,10 @@ class CraftBeerPi(ActorAPI, SensorAPI): ''' Background Task Decorator - :param key: - :param interval: - :param config_parameter: - :return: + :param key: + :param interval: + :param config_parameter: + :return: ''' def real_decorator(function): self.cache["background"].append({"function": function, "key": key, "interval": interval, "config_parameter": config_parameter}) @@ -476,7 +476,7 @@ class CraftBeerPi(ActorAPI, SensorAPI): def run_background_processes(self): ''' call all background task after startup - :return: + :return: ''' self.app.logger.info("Start Background") From f8ebf830372ad73563f15e653360c91e760329bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 10 Aug 2020 00:47:59 -0300 Subject: [PATCH 29/34] add dep --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 1139002..f658bb5 100755 --- a/install.sh +++ b/install.sh @@ -37,7 +37,7 @@ show_menu () { # apt-get -y install python-setuptools # easy_install pip - apt-get -y install python3-pip python3-dev + apt-get -y install python3-pip python3-dev python3-rpi.gpio apt-get -y install libpcre3-dev git pip3 install -r requirements.txt From aae37b871dec3bc34c3a44c8503190c4955e22b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 10 Aug 2020 00:48:12 -0300 Subject: [PATCH 30/34] add pdbpp to dev req... --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3c37c08..bacd4e8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ pre-commit pylint pylint-flask black +pdbpp From 86e2fcab50cd8c0053b2ae7e65cb31c3698c8277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 10 Aug 2020 00:50:44 -0300 Subject: [PATCH 31/34] force temp to float --- modules/base_plugins/dummy_temp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/base_plugins/dummy_temp/__init__.py b/modules/base_plugins/dummy_temp/__init__.py index bc81d85..2c55b03 100644 --- a/modules/base_plugins/dummy_temp/__init__.py +++ b/modules/base_plugins/dummy_temp/__init__.py @@ -63,7 +63,7 @@ class DummyTempSensor(SensorActive): ''' while self.is_running() is True: if not self.current_temp: - self.current_temp = self.temp + self.current_temp = float(self.temp) self.data_received(self.current_temp) new_temp = float(self.current_temp) + float(self.inc) if float(self.min_temp) <= new_temp <= float(self.max_temp): From ae64ba1ff435fad2a9081eba3081da07d8c57f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Mon, 10 Aug 2020 20:17:30 -0300 Subject: [PATCH 32/34] include PWM fix from https://github.com/PaulKGrimes/craftbeerpi3/tree/issue_242 --- modules/base_plugins/gpio_actor/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/base_plugins/gpio_actor/__init__.py b/modules/base_plugins/gpio_actor/__init__.py index 0cbbc2f..8d91564 100644 --- a/modules/base_plugins/gpio_actor/__init__.py +++ b/modules/base_plugins/gpio_actor/__init__.py @@ -68,7 +68,8 @@ class GPIOPWM(ActorBase): if self.frequency is None: self.frequency = 0.5 # 2 sec - self.gpio_inst = GPIO.PWM(int(self.gpio), float(self.frequency)) + if self.gpio_inst is None: + self.gpio_inst = GPIO.PWM(int(self.gpio), float(self.frequency)) self.gpio_inst.start(int(self.power)) def set_power(self, power): From 2fc012a81511322fcbea5b5b47767f84a002f2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Gim=C3=A9nez?= Date: Tue, 11 Aug 2020 20:44:50 -0300 Subject: [PATCH 33/34] fix for fermenter view --- modules/fermenter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/fermenter/__init__.py b/modules/fermenter/__init__.py index e14154e..c98daea 100755 --- a/modules/fermenter/__init__.py +++ b/modules/fermenter/__init__.py @@ -184,7 +184,7 @@ class FermenterView(BaseView): inactive.state = 'A' inactive.start = time.time() - inactive.direction = "C" if current_temp >= inactive.temp else "H" + inactive.direction = "C" if float(current_temp) >= float(inactive.temp) else "H" FermenterStep.update(**inactive.__dict__) self.postTargetTemp(id, inactive.temp) From 6a72d86952b278e25f275f533cf7949daa5c4a1d Mon Sep 17 00:00:00 2001 From: Eduardo Spremolla Date: Fri, 14 Aug 2020 11:08:04 -0300 Subject: [PATCH 34/34] Update __init__.py Remove reference to w1_bus_master1 since sensors could be no several bus masters like # CraftBeerPi 1-wire support dtoverlay=w1-gpio,gpiopin=4 dtoverlay=w1-gpio,gpiopin=27 dtoverlay=w1-gpio,gpiopin=18 : --- modules/base_plugins/one_wire/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/base_plugins/one_wire/__init__.py b/modules/base_plugins/one_wire/__init__.py index 537f0c6..b421a2a 100644 --- a/modules/base_plugins/one_wire/__init__.py +++ b/modules/base_plugins/one_wire/__init__.py @@ -52,7 +52,7 @@ class myThread (threading.Thread): ## Test Mode if self.sensor_name is None: return - with open('/sys/bus/w1/devices/w1_bus_master1/%s/w1_slave' % self.sensor_name, 'r') as content_file: + with open('/sys/bus/w1/devices/%s/w1_slave' % self.sensor_name, 'r') as content_file: content = content_file.read() if (content.split('\n')[0].split(' ')[11] == "YES"): temp = float(content.split("=")[-1]) / 1000 # temp in Celcius