| @@ -17,3 +17,8 @@ yarn.lock | |||
| modules/ui/package-lock.json | |||
| .python-version | |||
| upload/* | |||
| *.bak | |||
| .vscode | |||
| @@ -0,0 +1,25 @@ | |||
| # 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] | |||
| - repo: local | |||
| hooks: | |||
| - id: pylint | |||
| name: pylint | |||
| entry: pylint | |||
| language: system | |||
| types: [python] | |||
| @@ -1,5 +1,5 @@ | |||
| # Dockerfile for development on a pc/mac | |||
| FROM python:2 | |||
| FROM python:3.8 | |||
| EXPOSE 5000 | |||
| @@ -11,4 +11,4 @@ RUN pip install --no-cache-dir -r requirements.txt | |||
| COPY . . | |||
| CMD ["python", "run.py"] | |||
| CMD ["python", "run.py"] | |||
| @@ -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 | |||
| @@ -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 python3-rpi.gpio | |||
| 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 | |||
| @@ -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("##########################################") | |||
| @@ -1 +1 @@ | |||
| import endpoints | |||
| import modules.addon.endpoints | |||
| @@ -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: | |||
| @@ -132,9 +133,9 @@ 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"]) | |||
| for key, value in cbpi.cache["plugins"].iteritems(): | |||
| 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)) | |||
| return json.dumps(cbpi.cache["plugins"]) | |||
| @@ -144,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) | |||
| @@ -157,9 +158,28 @@ def download_addon(name): | |||
| @blueprint.route('/<name>/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) | |||
| @@ -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): | |||
| @@ -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) | |||
| @@ -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) | |||
| @@ -1,53 +1,78 @@ | |||
| # -*- 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") | |||
| @cbpi.action("My Custom Action") | |||
| def my_action(self): | |||
| print "HELLO WORLD" | |||
| pass | |||
| 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" | |||
| """ | |||
| 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 = float(self.inc) * -1 | |||
| def stop(self): | |||
| """ | |||
| stop sensor | |||
| """ | |||
| SensorActive.stop(self) | |||
| 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 = 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): | |||
| self.current_temp = '%.2f' % new_temp | |||
| self.sleep(5) | |||
| @classmethod | |||
| def init_global(cls): | |||
| ''' | |||
| Called one at the startup for all sensors | |||
| :return: | |||
| :return: | |||
| ''' | |||
| @@ -1,4 +1,5 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ base gpio actors """ | |||
| import time | |||
| from modules import cbpi | |||
| @@ -6,46 +7,60 @@ 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 ImportError 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) | |||
| 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 | |||
| 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,28 +68,38 @@ 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)) | |||
| 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): | |||
| ''' | |||
| Optional: Set the power of your actor | |||
| :param power: int value between 0 - 100 | |||
| :return: | |||
| :return: | |||
| ''' | |||
| if power is not None: | |||
| self.power = int(power) | |||
| self.p.ChangeDutyCycle(self.power) | |||
| if self.gpio_inst: | |||
| if power is not None: | |||
| self.power = int(power) | |||
| self.gpio_inst.ChangeDutyCycle(self.power) | |||
| def off(self): | |||
| print "GPIO OFF" | |||
| self.p.stop() | |||
| print("GPIO OFF") | |||
| 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) | |||
| @@ -88,20 +113,45 @@ 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" | |||
| print("ON") | |||
| def off(self): | |||
| print "OFF" | |||
| print("OFF") | |||
| @cbpi.actor | |||
| class DummyPWM(ActorBase): | |||
| """ | |||
| Dummy Actor with PWM support | |||
| """ | |||
| 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) | |||
| @@ -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 | |||
| @@ -1,5 +1,5 @@ | |||
| import time | |||
| from thread import start_new_thread | |||
| from _thread import start_new_thread | |||
| from modules import cbpi | |||
| try: | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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) | |||
| @@ -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() | |||
| @@ -83,13 +83,13 @@ class SensorAPI(object): | |||
| def init_sensors(self): | |||
| ''' | |||
| Initialize all sensors | |||
| :return: | |||
| :return: | |||
| ''' | |||
| 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"): | |||
| @@ -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() | |||
| @@ -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}) | |||
| @@ -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 | |||
| @@ -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") | |||
| @@ -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) | |||
| @@ -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__, | |||
| @@ -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) | |||
| @@ -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): | |||
| @@ -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): | |||
| @@ -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) | |||
| @@ -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") | |||
| @@ -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() | |||
| @@ -1 +1 @@ | |||
| import endpoints | |||
| import modules.logs.endpoints | |||
| @@ -1,14 +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"): | |||
| @@ -16,91 +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 | |||
| import csv | |||
| if not os.path.isfile(filename): | |||
| return ('File not found', 404) | |||
| array = [] | |||
| with open(filename, 'rb') as f: | |||
| reader = csv.reader(f) | |||
| with open(filename, 'r', 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('/<file>', 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') as f: | |||
| reader = csv.reader(f) | |||
| with open(filename, 'r', 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"])} | |||
| @route('/<t>/<int:id>', 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)}) | |||
| """ | |||
| :param chart_data: data for a chart | |||
| 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)) | |||
| :return: json for chart data | |||
| """ | |||
| return { | |||
| "name": | |||
| chart_data["name"], | |||
| "data": | |||
| self.read_log_as_json(chart_data["data_type"], | |||
| chart_data["data_id"]) | |||
| } | |||
| @route('/<log_type>/<int:log_id>', 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 = 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/<file>') | |||
| @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 | |||
| @@ -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 | |||
| @@ -1,3 +1,3 @@ | |||
| import beerxml | |||
| import kbh | |||
| import restapi | |||
| from . import beerxml | |||
| from . import kbh | |||
| from . import restapi | |||
| @@ -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: | |||
| @@ -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() | |||
| @@ -1 +1 @@ | |||
| import endpoints | |||
| import modules.system.endpoints | |||
| @@ -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/<name>', 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/<name>', 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.safe_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') | |||
| @@ -1 +1 @@ | |||
| import endpoints | |||
| import modules.ui.endpoints | |||
| @@ -0,0 +1,6 @@ | |||
| coverage==5.2.1 | |||
| pre-commit | |||
| pylint | |||
| pylint-flask | |||
| black | |||
| pdbpp | |||
| @@ -1,14 +1,27 @@ | |||
| Flask==0.12.4 | |||
| Flask-SocketIO==2.6.2 | |||
| eventlet==0.19.0 | |||
| greenlet==0.4.10 | |||
| python-dateutil==2.5.3 | |||
| python-engineio==0.9.2 | |||
| python-mimeparse==1.5.2 | |||
| python-socketio==1.4.4 | |||
| PyYAML==4.2b1 | |||
| requests==2.20.0 | |||
| Werkzeug==0.11.10 | |||
| httplib2==0.9.2 | |||
| flask-classy==0.6.10 | |||
| GitPython==2.1.3 | |||
| Flask==1.1.2 | |||
| Flask-Classy==0.6.10 | |||
| Flask-SocketIO==2.6.2 | |||
| flask-envconfig | |||
| greenlet==0.4.16 | |||
| eventlet==0.26.1 | |||
| python-engineio==3.13.1 | |||
| 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 | |||
| 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 | |||
| @@ -1,11 +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 | |||
| socketio.run(app, host='0.0.0.0', port=port) | |||
| PORT = 5000 | |||
| socketio.run(app, host='0.0.0.0', port=PORT) | |||