diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml index a5d569c..d882427 100644 --- a/.idea/dataSources.local.xml +++ b/.idea/dataSources.local.xml @@ -32,5 +32,12 @@ false *:@ + + + + master_key + false + *:@ + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 9b913ae..0cdeb9f 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -78,5 +78,14 @@ + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$USER_HOME$/cbpi4_test/2019-08-08-001/craftbeerpi.db + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1dc20ae..e2ce4ea 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,4 +4,7 @@ + + + + + + + + + + + + + + + @@ -66,12 +113,6 @@ - - - - - - @@ -633,23 +365,19 @@ + + + - - - - - - - @@ -657,6 +385,13 @@ + + + + + + + - + - - - + + + @@ -956,7 +685,11 @@ - + + + + + 1541288846149 @@ -976,39 +709,39 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - @@ -1020,20 +753,20 @@ - + - + - - + + - + - + @@ -1042,11 +775,11 @@ - + - + @@ -1082,13 +815,9 @@ file://$PROJECT_DIR$/cbpi/craftbeerpi.py - 45 + 46 - - file://$PROJECT_DIR$/cbpi/api/sensor.py - @@ -1105,297 +834,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -1404,23 +846,350 @@ - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cbpi/api/__init__.py b/cbpi/api/__init__.py index 9b3a8ed..4f02b33 100644 --- a/cbpi/api/__init__.py +++ b/cbpi/api/__init__.py @@ -2,8 +2,6 @@ __all__ = ["CBPiActor", "CBPiExtension", "Property", "PropertyType", - "on_websocket_message", - "on_mqtt_message", "on_event", "on_startup", "request_mapping", diff --git a/cbpi/api/decorator.py b/cbpi/api/decorator.py index 2913918..40750a2 100644 --- a/cbpi/api/decorator.py +++ b/cbpi/api/decorator.py @@ -2,7 +2,7 @@ from functools import wraps from voluptuous import Schema -__all__ = ["request_mapping", "on_startup", "on_event", "on_mqtt_message", "on_websocket_message", "action", "background_task"] +__all__ = ["request_mapping", "on_startup", "on_event", "action", "background_task"] from aiohttp_auth import auth @@ -55,15 +55,6 @@ def request_mapping(path, name=None, method="GET", auth_required=True, json_sche validate_json_body ) -def on_websocket_message(path, name=None): - def real_decorator(func): - func.ws = True - func.key = path - func.name = name - return func - - return real_decorator - def on_event(topic): def real_decorator(func): func.eventbus = True @@ -76,29 +67,18 @@ def on_event(topic): def action(key, parameters): def real_decorator(func): func.action = True - func.key = key func.parameters = parameters return func return real_decorator -def on_mqtt_message(topic): - def real_decorator(func): - func.mqtt = True - func.topic = topic - return func - - return real_decorator - - def background_task(name, interval): def real_decorator(func): func.background_task = True func.name = name func.interval = interval return func - return real_decorator @@ -108,7 +88,6 @@ def on_startup(name, order=0): func.name = name func.order = order return func - return real_decorator diff --git a/cbpi/api/exceptions.py b/cbpi/api/exceptions.py index 77af2f6..acec4ee 100644 --- a/cbpi/api/exceptions.py +++ b/cbpi/api/exceptions.py @@ -11,4 +11,4 @@ class SensorException(CBPiException): pass class ActorException(CBPiException): - pass \ No newline at end of file + pass diff --git a/cbpi/api/sensor.py b/cbpi/api/sensor.py index 89ef0c3..727c429 100644 --- a/cbpi/api/sensor.py +++ b/cbpi/api/sensor.py @@ -1,11 +1,10 @@ -from logging.handlers import RotatingFileHandler -from time import localtime, strftime +import logging +from abc import ABCMeta from cbpi.api.extension import CBPiExtension -import logging -class CBPiSensor(CBPiExtension): +class CBPiSensor(CBPiExtension, metaclass=ABCMeta): def __init__(self, *args, **kwds): CBPiExtension.__init__(self, *args, **kwds) self.logger = logging.getLogger(__file__) diff --git a/cbpi/api/step.py b/cbpi/api/step.py index 2b4e2dc..d252fb3 100644 --- a/cbpi/api/step.py +++ b/cbpi/api/step.py @@ -2,7 +2,7 @@ import json import time import asyncio import logging -from abc import abstractmethod,ABCMeta +from abc import abstractmethod, ABCMeta class CBPiSimpleStep(metaclass=ABCMeta): diff --git a/cbpi/cli.py b/cbpi/cli.py index 29bed88..6e76cec 100644 --- a/cbpi/cli.py +++ b/cbpi/cli.py @@ -1,7 +1,12 @@ import argparse +import datetime import logging +import subprocess +import sys +import re import requests import yaml +from cbpi.utils.utils import load_config from cbpi.craftbeerpi import CraftBeerPi import os @@ -59,22 +64,87 @@ def list_plugins(): print("***************************************************") print("CraftBeerPi 4.x Plugin List") print("***************************************************") + print("") plugins_yaml = "https://raw.githubusercontent.com/Manuel83/craftbeerpi-plugins/master/plugins_v4.yaml" r = requests.get(plugins_yaml) - data = yaml.load(r.content, Loader=yaml.FullLoader) - - for name, value in data.items(): print(name) + print("") + print("***************************************************") +def add(package_name): + + if package_name is None: + print("Missing Plugin Name: cbpi add --name=") + return + + data = subprocess.check_output([sys.executable, "-m", "pip", "install", package_name]) + data = data.decode('UTF-8') + + patter_already_installed = "Requirement already satisfied: %s" % package_name + pattern = "Successfully installed %s-([-0-9a-zA-Z._]*)" % package_name + + match_already_installed = re.search(patter_already_installed, data) + match_installed = re.search(pattern, data) + + if match_already_installed is not None: + print("Plugin already installed") + return False + + if match_installed is None: + print(data) + print("Faild to install plugin") + return False + + version = match_installed.groups()[0] + plugins = load_config("./config/plugin_list.txt") + if plugins is None: + plugins = {} + now = datetime.datetime.now() + plugins[package_name] = dict(version=version, installation_date=now.strftime("%Y-%m-%d %H:%M:%S")) + + with open('./config/plugin_list.txt', 'w') as outfile: + yaml.dump(plugins, outfile, default_flow_style=False) + + print("Plugin %s added" % package_name) + return True + + +def remove(package_name): + if package_name is None: + print("Missing Plugin Name: cbpi add --name=") + return + data = subprocess.check_output([sys.executable, "-m", "pip", "uninstall", "-y", package_name]) + data = data.decode('UTF-8') + + pattern = "Successfully uninstalled %s-([-0-9a-zA-Z._]*)" % package_name + match_uninstalled = re.search(pattern, data) + + if match_uninstalled is None: + print(data) + print("Faild to uninstall plugin") + return False + + plugins = load_config("./config/plugin_list.txt") + if plugins is None: + plugins = {} + + if package_name not in plugins: + return False + + del plugins[package_name] + with open('./config/plugin_list.txt', 'w') as outfile: + yaml.dump(plugins, outfile, default_flow_style=False) + + print("Plugin %s removed" % package_name) + return True def main(): parser = argparse.ArgumentParser(description='Welcome to CraftBeerPi 4') parser.add_argument("action", type=str, help="start,stop,restart,setup,plugins") - + parser.add_argument("--name", type=str, help="Plugin name") args = parser.parse_args() - #logging.basicConfig(level=logging.INFO, filename='./logs/app.log', filemode='a', format='%(asctime)s - %(levelname)s - %(name)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s') @@ -94,6 +164,16 @@ def main(): list_plugins() return + + if args.action == "add": + + add(args.name) + return + + if args.action == "remove": + remove(args.name) + return + if args.action == "start": if check_for_setup() is False: return diff --git a/cbpi/config/create_database.sql b/cbpi/config/create_database.sql index a341e38..9c4d038 100644 --- a/cbpi/config/create_database.sql +++ b/cbpi/config/create_database.sql @@ -108,4 +108,9 @@ CREATE TABLE IF NOT EXISTS dummy id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(80) -); \ No newline at end of file +); + + +INSERT OR IGNORE INTO config (name, value, type, description, options) VALUES ('TEMP_UNIT', 'F', 'select', 'Temperature Unit', '[{"value": "C", "label": "C"}, {"value": "F", "label": "F"}]'); +INSERT OR IGNORE INTO config (name, value, type, description, options) VALUES ('NAME', 'India Pale Ale1', 'string', 'Brew Name', 'null'); +INSERT OR IGNORE INTO config (name, value, type, description, options) VALUES ('BREWERY_NAME', 'CraftBeerPI', 'string', 'Brewery Name', 'null'); diff --git a/cbpi/config/plugin_list.txt b/cbpi/config/plugin_list.txt index 142c198..e69de29 100644 --- a/cbpi/config/plugin_list.txt +++ b/cbpi/config/plugin_list.txt @@ -1 +0,0 @@ -cbpi-actor \ No newline at end of file diff --git a/cbpi/controller/actor_controller.py b/cbpi/controller/actor_controller.py index 6d7d95d..7cd5278 100644 --- a/cbpi/controller/actor_controller.py +++ b/cbpi/controller/actor_controller.py @@ -149,8 +149,7 @@ class ActorController(CRUDController): pass async def _pre_delete_callback(self, actor_id): - #if int(actor_id) not in self.cache: - # return + if self.cache[int(actor_id)].instance is not None: await self._stop_actor(self.cache[int(actor_id)]) diff --git a/cbpi/controller/dashboard_controller.py b/cbpi/controller/dashboard_controller.py index 7daff7b..ae68214 100644 --- a/cbpi/controller/dashboard_controller.py +++ b/cbpi/controller/dashboard_controller.py @@ -10,6 +10,7 @@ class DashboardController(CRUDController): name = "Dashboard" def __init__(self, cbpi): + self.caching = False super(DashboardController, self).__init__(cbpi) self.cbpi = cbpi self.logger = logging.getLogger(__name__) @@ -29,3 +30,7 @@ class DashboardController(CRUDController): async def move_content(self,content_id, x, y): await DashboardContentModel.update_coordinates(content_id, x, y) + + async def delete_dashboard(self, dashboard_id): + await DashboardContentModel.delete_by_dashboard_id(dashboard_id) + await self.model.delete(dashboard_id) \ No newline at end of file diff --git a/cbpi/craftbeerpi.py b/cbpi/craftbeerpi.py index 0d6f263..c230052 100644 --- a/cbpi/craftbeerpi.py +++ b/cbpi/craftbeerpi.py @@ -1,5 +1,6 @@ import logging from os import urandom +import os from aiohttp import web from aiohttp_auth import auth @@ -231,7 +232,8 @@ class CraftBeerPi(): else: return web.Response(text="Hello from CraftbeerPi!") - self.app.add_routes([web.get('/', http_index)]) + + self.app.add_routes([web.get('/', http_index), web.static('/static', os.path.join(os.path.dirname(__file__),"static"), show_index=True)]) async def init_serivces(self): diff --git a/cbpi/extension/comp/__init__.py b/cbpi/extension/comp/__init__.py index 58ccbeb..e9ae1c2 100644 --- a/cbpi/extension/comp/__init__.py +++ b/cbpi/extension/comp/__init__.py @@ -1,7 +1,4 @@ import os - -from aiohttp import web - from cbpi.api import * from cbpi.controller.crud_controller import CRUDController from cbpi.database.orm_framework import DBModel @@ -22,11 +19,8 @@ class MyComp(CBPiExtension, CRUDController, HttpCrudEndpoints): def __init__(self, cbpi): ''' Initializer - :param cbpi: ''' - - self.cbpi = cbpi # register component for http, events # In addtion the sub folder static is exposed to access static content via http @@ -35,14 +29,14 @@ class MyComp(CBPiExtension, CRUDController, HttpCrudEndpoints): @on_event(topic="actor/#") async def listen(self, **kwargs): + # Listen for all actor events pass - @on_event(topic="kettle/+/automatic") async def listen2(self, **kwargs): + # listen for all kettle events which are switching the automatic logic pass - #await self.cbpi.bus.fire(topic="actor/%s/toggle" % 1, id=1) def setup(cbpi): @@ -54,4 +48,4 @@ def setup(cbpi): ''' # regsiter the component to the core cbpi.plugin.register("MyComp", MyComp) - pass \ No newline at end of file + pass diff --git a/cbpi/extension/ds18b20/__init__.py b/cbpi/extension/ds18b20/__init__.py new file mode 100644 index 0000000..55e9a9a --- /dev/null +++ b/cbpi/extension/ds18b20/__init__.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +import asyncio +import threading +import time + +from aiohttp import web +from cbpi.api import * + +import re +import random + + +def getSensors(): + try: + arr = [] + for dirname in os.listdir('/sys/bus/w1/devices'): + if (dirname.startswith("28") or dirname.startswith("10")): + cbpi.app.logger.info("Device %s Found (Family: 28/10, Thermometer on GPIO4 (w1))" % dirname) + arr.append(dirname) + return arr + except: + return [] + + +class myThread (threading.Thread): + + value = 0 + + + def __init__(self, sensor_name): + threading.Thread.__init__(self) + self.value = 0 + self.sensor_name = sensor_name + self.runnig = True + + def shutdown(self): + pass + + def stop(self): + self.runnig = False + + def run(self): + + while self.runnig: + + try: + app.logger.info("READ TEMP") + ## 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: + content = content_file.read() + if (content.split('\n')[0].split(' ')[11] == "YES"): + temp = float(content.split("=")[-1]) / 1000 # temp in Celcius + self.value = temp + except: + pass + + self.value = random.randint(1,100) + time.sleep(4) + +class DS18B20(CBPiSensor): + + + sensor_name = Property.Select("Sensor", getSensors(), description="The OneWire sensor address.") + offset = Property.Number("Offset", True, 0, description="Offset which is added to the received sensor data. Positive and negative values are both allowed.") + interval = Property.Number(label="interval", configurable=True) + + # Internal runtime variable + value = 0 + + def init(self): + super().init() + self.state = True + self.t = myThread(self.sensor_name) + def shudown(): + shudown.cb.shutdown() + + shudown.cb = self.t + + self.t.start() + + def get_state(self): + return self.state + + def get_value(self): + + return self.value + + def get_unit(self): + return "°%s" % self.get_parameter("TEMP_UNIT", "C") + + def stop(self): + try: + self.t.stop() + except: + pass + + async def run(self, cbpi): + self.value = 0 + while True: + await asyncio.sleep(self.interval) + self.value = random.randint(1,101) + self.log_data(self.value) + await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value) + + + + +def setup(cbpi): + + ''' + This method is called by the server during startup + Here you need to register your plugins at the server + + :param cbpi: the cbpi core + :return: + ''' + + cbpi.plugin.register("DS18B20", DS18B20) diff --git a/cbpi/extension/ds18b20/config.yaml b/cbpi/extension/ds18b20/config.yaml new file mode 100644 index 0000000..7f0aa15 --- /dev/null +++ b/cbpi/extension/ds18b20/config.yaml @@ -0,0 +1,3 @@ +name: DummySensor +version: 4 +active: true \ No newline at end of file diff --git a/cbpi/extension/dummysensor/__init__.py b/cbpi/extension/dummysensor/__init__.py index 971c5a9..ba1e743 100644 --- a/cbpi/extension/dummysensor/__init__.py +++ b/cbpi/extension/dummysensor/__init__.py @@ -4,13 +4,12 @@ from aiohttp import web from cbpi.api import * import re - +import random class CustomSensor(CBPiSensor): # Custom Properties which will can be configured by the user - p1 = Property.Number(label="Test") - p2 = Property.Text(label="Test") + interval = Property.Number(label="interval", configurable=True) # Internal runtime variable @@ -45,104 +44,10 @@ class CustomSensor(CBPiSensor): self.value = 0 while True: await asyncio.sleep(self.interval) - self.value = self.value + 1 + self.value = random.randint(1,101) self.log_data(self.value) await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value) -cache = {} - - -class HTTPSensor(CBPiSensor): - - # Custom Properties which will can be configured by the user - - key = Property.Text(label="Key", configurable=True) - - def init(self): - super().init() - - self.state = True - - def get_state(self): - return self.state - - def get_value(self): - - return self.value - - def stop(self): - pass - - async def run(self, cbpi): - self.value = 0 - while True: - await asyncio.sleep(1) - - try: - value = cache.pop(self.key, None) - - if value is not None: - self.log_data(value) - await cbpi.bus.fire("sensor/%s/data" % self.id, value=value) - except Exception as e: - print(e) - pass - -class HTTPSensorEndpoint(CBPiExtension): - - - def __init__(self, cbpi): - ''' - Initializer - - :param cbpi: - ''' - self.pattern_check = re.compile("^[a-zA-Z0-9,.]{0,10}$") - - self.cbpi = cbpi - # register component for http, events - # In addtion the sub folder static is exposed to access static content via http - self.cbpi.register(self, "/httpsensor") - - - @request_mapping(path="/{key}/{value}", auth_required=False) - async def http_new_value2(self, request): - """ - --- - description: Kettle Heater on - tags: - - HttpSensor - parameters: - - name: "key" - in: "path" - description: "Sensor Key" - required: true - type: "string" - - name: "value" - in: "path" - description: "Value" - required: true - type: "integer" - format: "int64" - responses: - "204": - description: successful operation - """ - - global cache - key = request.match_info['key'] - value = request.match_info['value'] - if self.pattern_check.match(key) is None: - return web.json_response(status=422, data={'error': "Key not matching pattern ^[a-zA-Z0-9,.]{0,10}$"}) - - if self.pattern_check.match(value) is None: - return web.json_response(status=422, data={'error': "Data not matching pattern ^[a-zA-Z0-9,.]{0,10}$"}) - - print("HTTP SENSOR ", key, value) - cache[key] = value - - return web.Response(status=204) - def setup(cbpi): @@ -153,6 +58,5 @@ def setup(cbpi): :param cbpi: the cbpi core :return: ''' - cbpi.plugin.register("HTTPSensor", HTTPSensor) - cbpi.plugin.register("HTTPSensorEndpoint", HTTPSensorEndpoint) + cbpi.plugin.register("CustomSensor", CustomSensor) diff --git a/cbpi/extension/httpsensor/__init__.py b/cbpi/extension/httpsensor/__init__.py new file mode 100644 index 0000000..19c65ba --- /dev/null +++ b/cbpi/extension/httpsensor/__init__.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +import asyncio +from aiohttp import web +from cbpi.api import * + +import re +import random + + +cache = {} + + +class HTTPSensor(CBPiSensor): + + # Custom Properties which will can be configured by the user + + key = Property.Text(label="Key", configurable=True) + + def init(self): + super().init() + + self.state = True + + def get_state(self): + return self.state + + def get_value(self): + + return self.value + + def stop(self): + pass + + async def run(self, cbpi): + self.value = 0 + while True: + await asyncio.sleep(1) + + try: + value = cache.pop(self.key, None) + + if value is not None: + self.log_data(value) + await cbpi.bus.fire("sensor/%s/data" % self.id, value=value) + except Exception as e: + print(e) + pass + +class HTTPSensorEndpoint(CBPiExtension): + + + def __init__(self, cbpi): + ''' + Initializer + + :param cbpi: + ''' + self.pattern_check = re.compile("^[a-zA-Z0-9,.]{0,10}$") + + self.cbpi = cbpi + # register component for http, events + # In addtion the sub folder static is exposed to access static content via http + self.cbpi.register(self, "/httpsensor") + + + @request_mapping(path="/{key}/{value}", auth_required=False) + async def http_new_value2(self, request): + """ + --- + description: Kettle Heater on + tags: + - HttpSensor + parameters: + - name: "key" + in: "path" + description: "Sensor Key" + required: true + type: "string" + - name: "value" + in: "path" + description: "Value" + required: true + type: "integer" + format: "int64" + responses: + "204": + description: successful operation + """ + + global cache + key = request.match_info['key'] + value = request.match_info['value'] + if self.pattern_check.match(key) is None: + return web.json_response(status=422, data={'error': "Key not matching pattern ^[a-zA-Z0-9,.]{0,10}$"}) + + if self.pattern_check.match(value) is None: + return web.json_response(status=422, data={'error': "Data not matching pattern ^[a-zA-Z0-9,.]{0,10}$"}) + + print("HTTP SENSOR ", key, value) + cache[key] = value + + return web.Response(status=204) + + +def setup(cbpi): + + ''' + This method is called by the server during startup + Here you need to register your plugins at the server + + :param cbpi: the cbpi core + :return: + ''' + cbpi.plugin.register("HTTPSensor", HTTPSensor) + cbpi.plugin.register("HTTPSensorEndpoint", HTTPSensorEndpoint) + diff --git a/cbpi/extension/httpsensor/config.yaml b/cbpi/extension/httpsensor/config.yaml new file mode 100644 index 0000000..7f0aa15 --- /dev/null +++ b/cbpi/extension/httpsensor/config.yaml @@ -0,0 +1,3 @@ +name: DummySensor +version: 4 +active: true \ No newline at end of file diff --git a/cbpi/http_endpoints/http_dashboard.py b/cbpi/http_endpoints/http_dashboard.py index 03a2aca..26ad9ed 100644 --- a/cbpi/http_endpoints/http_dashboard.py +++ b/cbpi/http_endpoints/http_dashboard.py @@ -118,7 +118,9 @@ class DashBoardHttpEndpoints(HttpCrudEndpoints): "204": description: successful operation """ - return await super().http_delete_one(request) + id = request.match_info['id'] + await self.cbpi.dashboard.delete_dashboard(id) + return web.Response(status=204) @request_mapping(path="/{id:\d+}/content", auth_required=False) async def get_content(self, request): diff --git a/cbpi/http_endpoints/http_sensor.py b/cbpi/http_endpoints/http_sensor.py index c1b319b..76705f1 100644 --- a/cbpi/http_endpoints/http_sensor.py +++ b/cbpi/http_endpoints/http_sensor.py @@ -180,9 +180,9 @@ class SensorHttpEndpoints(HttpCrudEndpoints): """ --- - description: Toogle an actor on or off + description: Execute action on sensor tags: - - Actor + - Sensor parameters: - name: "id" in: "path" diff --git a/cbpi/static/splash.png b/cbpi/static/splash.png new file mode 100644 index 0000000..68086b5 Binary files /dev/null and b/cbpi/static/splash.png differ diff --git a/cbpi/static/test.html b/cbpi/static/test.html new file mode 100644 index 0000000..94782b0 --- /dev/null +++ b/cbpi/static/test.html @@ -0,0 +1,17 @@ + + + + + CraftBeerPi 4.0 + + + + + + + \ No newline at end of file diff --git a/cbpi/utils/utils.py b/cbpi/utils/utils.py index e4c34a4..8673080 100644 --- a/cbpi/utils/utils.py +++ b/cbpi/utils/utils.py @@ -7,11 +7,13 @@ import yaml def load_config(fname): + try: with open(fname, 'rt') as f: - data = yaml.load(f) + data = yaml.load(f, Loader=yaml.FullLoader) return data except Exception as e: + print(e) pass def json_dumps(obj): diff --git a/cbpi_cloud/default_broker.yaml b/cbpi_cloud/default_broker.yaml deleted file mode 100644 index c7d9dc7..0000000 --- a/cbpi_cloud/default_broker.yaml +++ /dev/null @@ -1,14 +0,0 @@ -listeners: - default: - type: tcp - bind: 0.0.0.0:1883 -sys_interval: 20 -auth: - allow-anonymous: true -plugins: - - auth_file - - auth_anonymous -topic-check: - enabled': True - plugins': - - topic_taboo \ No newline at end of file diff --git a/cbpi_cloud/repo/plugins.yaml b/cbpi_cloud/repo/plugins.yaml deleted file mode 100644 index 17a2d68..0000000 --- a/cbpi_cloud/repo/plugins.yaml +++ /dev/null @@ -1,13 +0,0 @@ -SampleActor: - description: A sample Actor for CraftBeerPi - api: 4.0 - author: CraftBeerPi11 - pip: requests - repo_url: https://github.com/craftbeerpi/sample_actor - -SampleActor2: - description: A sample Actor2 for CraftBeerPi - api: 4.0 - author: CraftBeerPi - pip: requests - repo_url: https://github.com/craftbeerpi/sample_actor diff --git a/cbpi_cloud/run.py b/cbpi_cloud/run.py deleted file mode 100644 index 294ea2f..0000000 --- a/cbpi_cloud/run.py +++ /dev/null @@ -1,62 +0,0 @@ -import yaml -from aiohttp import web - - -def load_yaml(): - try: - with open('./repo/plugins.yaml', 'rt') as f: - data = yaml.load(f) - return data - except Exception as e: - print(e) - pass - -data = load_yaml() - -for k, v in data.items(): - del v["pip"] -data2 = load_yaml() - - -async def check(request): - peername = request.transport.get_extra_info('peername') - if peername is not None: - host, port = peername - print(host, port) - data = await request.json() - print(data) - return web.json_response(data=dict(latestversion="4.0.0.3")) - -async def reload_yaml(request): - global data, data2 - - file = load_yaml() - for k, v in file.items(): - del v["pip"] - data = file - data2 = load_yaml() - - return web.json_response(data=data2) - -async def get_list(request): - print("Request List") - return web.json_response(data=data) - -async def get_package_name(request): - print("Request Package") - name = request.match_info.get('plugin_name', None) - if name in data2: - package_name = data2[name]["pip"] - else: - package_name = None - return web.json_response(data=dict(package_name=package_name)) - - -app = web.Application() -app.add_routes([ - web.get('/list', get_list), - web.post('/check', check), - web.get('/reload', reload_yaml), - web.get('/get/{plugin_name}', get_package_name)]) - -web.run_app(app, port=2202) diff --git a/config/plugin_list.txt b/config/plugin_list.txt index 92f3cdc..0967ef4 100644 --- a/config/plugin_list.txt +++ b/config/plugin_list.txt @@ -1,11 +1 @@ -cbpi-actor: - api: 4.0 - author: CraftBeerPi11 - description: A sample Actor for CraftBeerPi - repo_url: https://github.com/craftbeerpi/sample_actor -cbpi-ui: - api: 4.0 - author: CraftBeerPi - description: A sample Actor2 for CraftBeerPi - repo_url: https://github.com/craftbeerpi/sample_actor - +{} diff --git a/craftbeerpi.db b/craftbeerpi.db index 9c91416..600db5b 100644 Binary files a/craftbeerpi.db and b/craftbeerpi.db differ diff --git a/sample.py b/sample.py index c32859e..249930b 100644 --- a/sample.py +++ b/sample.py @@ -1,72 +1,19 @@ import datetime -import glob -import json -import logging -from logging.handlers import RotatingFileHandler -from time import strftime, localtime -import pandas as pd -import matplotlib.pyplot as plt -sid = 2 +import yaml +from cbpi.utils.utils import load_config + +package_name = "test222" + +with open("./config/plugin_list.txt", 'rt') as f: + print(f) + plugins = yaml.load(f) + if plugins is None: + plugins = {} -data_logger = logging.getLogger('cbpi.sensor.%s' % sid) -data_logger.propagate = False -data_logger.setLevel(logging.DEBUG) -handler = RotatingFileHandler('./logs/sensor_%s.log' % sid, maxBytes=100_000, backupCount=10) -data_logger.addHandler(handler) -import random +now = datetime.datetime.now() -start = datetime.datetime.now() -''' -v = random.randint(50,60) -for i in range(5760): - d = start + datetime.timedelta(seconds=6*i) - formatted_time = d.strftime("%Y-%m-%d %H:%M:%S") - if i % 750 == 0: - v = random.randint(50,60) - data_logger.info("%s,%s" % (formatted_time, v)) - - -''' -def dateparse (time_in_secs): - return datetime.datetime.strptime(time_in_secs, '%Y-%m-%d %H:%M:%S') - -all_filenames = glob.glob('./logs/sensor_1.log*') -all_filenames.sort() - - -all_filenames2 = glob.glob('./logs/sensor_2.log*') -all_filenames2.sort() - -combined_csv = pd.concat([pd.read_csv(f, parse_dates=True, date_parser=dateparse, index_col='DateTime', names=['DateTime', 'Sensor1'], header=None) for f in all_filenames]) -combined_csv2 = pd.concat([pd.read_csv(f, parse_dates=True, date_parser=dateparse, index_col='DateTime', names=['DateTime', 'Sensor2'], header=None) for f in all_filenames2]) - - -print(combined_csv) -print(combined_csv2) - - -m2 = pd.merge(combined_csv, combined_csv2, how='inner', left_index=True, right_index=True) - -print(m2) - -m2.plot() - -m2.plot(y=['Sensor1','Sensor2']) - -ts = combined_csv.Sensor1.resample('5000s').max() - -#ts.plot(y='Sensor1') - -i = 0 -def myconverter(o): - if isinstance(o, datetime.datetime): - return o.__str__() - - - -data = {"time": ts.index.tolist(), "data": ts.tolist()} -s1 = json.dumps(data, default = myconverter) - -plt.show() +plugins[package_name] = dict(version="1.0", installation_date=now.strftime("%Y-%m-%d %H:%M:%S")) +with open('./config/plugin_list.txt', 'w') as outfile: + yaml.dump(plugins, outfile, default_flow_style=False) diff --git a/setup.py b/setup.py index b443fae..2499821 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup(name='cbpi', - version='4.0.0.4', + version='4.0.0.5', description='CraftBeerPi', author='Manuel Fritsch', author_email='manuel@craftbeerpi.com', diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..1e41833 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,20 @@ +import logging +import unittest + +from cli import add, remove, list_plugins + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s') + + +class CLITest(unittest.TestCase): + + def test_install(self): + assert add("cbpi4-ui-plugin") == True + assert add("cbpi4-ui-plugin") == False + assert remove("cbpi4-ui-plugin") == True + + def test_list(self): + list_plugins() + +if __name__ == '__main__': + unittest.main() \ No newline at end of file