From 05e08d0dc67ea8a423356cbe70d6c489872fef9c Mon Sep 17 00:00:00 2001 From: Manuel Fritsch Date: Sun, 17 Jan 2021 22:49:18 +0100 Subject: [PATCH] new step controller --- .vscode/.ropeproject/config.py | 114 +++++++ .vscode/.ropeproject/objectdb | Bin 0 -> 6 bytes cbpi/api/__init__.py | 4 +- cbpi/api/decorator.py | 9 +- cbpi/api/property.py | 2 - cbpi/api/step.py | 54 ++++ cbpi/controller/dashboard_controller.py | 20 +- cbpi/controller/plugin_controller.py | 85 ++++- cbpi/controller/sensor_controller.py | 6 +- cbpi/controller/step_controller_ng.py | 251 +++++++++++++++ cbpi/craftbeerpi.py | 10 +- cbpi/eventbus.py | 2 +- cbpi/extension/dummystep/__init__.py | 20 +- cbpi/http_endpoints/http_config.py | 11 +- cbpi/http_endpoints/http_dashboard.py | 23 ++ cbpi/http_endpoints/http_step2.py | 200 ++++++++++++ cbpi/http_endpoints/http_system.py | 2 +- cbpi/job/aiohttp.py | 3 +- config/dashboard/cbpi_dashboard_1.json | 403 +++++++++++++++--------- config/step_data.json | 35 ++ craftbeerpi.db | Bin 53248 -> 53248 bytes data.txt | 1 + sample.py | 19 -- sampletest.py | 0 step_data.json | 6 + tests/test_step_ng.py | 46 +++ 26 files changed, 1125 insertions(+), 201 deletions(-) create mode 100644 .vscode/.ropeproject/config.py create mode 100644 .vscode/.ropeproject/objectdb create mode 100644 cbpi/controller/step_controller_ng.py create mode 100644 cbpi/http_endpoints/http_step2.py create mode 100644 config/step_data.json create mode 100644 data.txt delete mode 100644 sample.py create mode 100644 sampletest.py create mode 100644 step_data.json create mode 100644 tests/test_step_ng.py diff --git a/.vscode/.ropeproject/config.py b/.vscode/.ropeproject/config.py new file mode 100644 index 0000000..dee2d1a --- /dev/null +++ b/.vscode/.ropeproject/config.py @@ -0,0 +1,114 @@ +# The default ``config.py`` +# flake8: noqa + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', + '.hg', '.svn', '_svn', '.git', '.tox'] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + # prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + # prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + # prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs['save_objectdb'] = True + prefs['compress_objectdb'] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs['automatic_soa'] = True + # The depth of calls to follow in static object analysis + prefs['soa_followed_calls'] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs['perform_doa'] = True + + # Rope can check the validity of its object DB when running. + prefs['validate_objectdb'] = True + + # How many undos to hold? + prefs['max_history_items'] = 32 + + # Shows whether to save history across sessions. + prefs['save_history'] = True + prefs['compress_history'] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs['indent_size'] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs['extension_modules'] = [] + + # Add all standard c-extensions to extension_modules list. + prefs['import_dynload_stdmods'] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs['ignore_syntax_errors'] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs['ignore_bad_imports'] = False + + # If `True`, rope will insert new module imports as + # `from import ` by default. + prefs['prefer_module_from_imports'] = False + + # If `True`, rope will transform a comma list of imports into + # multiple separate import statements when organizing + # imports. + prefs['split_imports'] = False + + # If `True`, rope will remove all top-level import statements and + # reinsert them at the top of the module when making changes. + prefs['pull_imports_to_top'] = True + + # If `True`, rope will sort imports alphabetically by module name instead + # of alphabetically by import statement, with from imports after normal + # imports. + prefs['sort_imports_alphabetically'] = False + + # Location of implementation of + # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general + # case, you don't have to change this value, unless you're an rope expert. + # Change this value to inject you own implementations of interfaces + # listed in module rope.base.oi.type_hinting.providers.interfaces + # For example, you can add you own providers for Django Models, or disable + # the search type-hinting in a class hierarchy, etc. + prefs['type_hinting_factory'] = ( + 'rope.base.oi.type_hinting.factory.default_type_hinting_factory') + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff --git a/.vscode/.ropeproject/objectdb b/.vscode/.ropeproject/objectdb new file mode 100644 index 0000000000000000000000000000000000000000..0a47446c0ad231c193bdd44ff327ba2ab28bf3d8 GIT binary patch literal 6 NcmZo*sx4&D0{{kv0iOT> literal 0 HcmV?d00001 diff --git a/cbpi/api/__init__.py b/cbpi/api/__init__.py index 4f02b33..d629a95 100644 --- a/cbpi/api/__init__.py +++ b/cbpi/api/__init__.py @@ -6,6 +6,7 @@ __all__ = ["CBPiActor", "on_startup", "request_mapping", "action", + "parameters", "background_task", "CBPiKettleLogic", "CBPiSimpleStep", @@ -13,7 +14,8 @@ __all__ = ["CBPiActor", "KettleException", "SensorException", "ActorException", - "CBPiSensor"] + "CBPiSensor", + "CBPiStep"] from cbpi.api.actor import * from cbpi.api.sensor import * diff --git a/cbpi/api/decorator.py b/cbpi/api/decorator.py index 40750a2..2c28d1d 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", "action", "background_task"] +__all__ = ["request_mapping", "on_startup", "on_event", "action", "background_task", "parameters"] from aiohttp_auth import auth @@ -73,6 +73,13 @@ def action(key, parameters): return real_decorator +def parameters(parameter): + def real_decorator(func): + func.cbpi_p = True + func.cbpi_parameters = parameter + return func + return real_decorator + def background_task(name, interval): def real_decorator(func): func.background_task = True diff --git a/cbpi/api/property.py b/cbpi/api/property.py index 98f44ca..048c989 100644 --- a/cbpi/api/property.py +++ b/cbpi/api/property.py @@ -29,8 +29,6 @@ class Property(object): ''' def __init__(self, label, configurable=False, default_value=None, unit="", description=""): ''' - Test - :param label: :param configurable: diff --git a/cbpi/api/step.py b/cbpi/api/step.py index d252fb3..be515a0 100644 --- a/cbpi/api/step.py +++ b/cbpi/api/step.py @@ -3,7 +3,61 @@ import time import asyncio import logging from abc import abstractmethod, ABCMeta +import logging +class CBPiStep(metaclass=ABCMeta): + def __init__(self, cbpi, id, name, props) : + self.cbpi = cbpi + self.props = {"wohoo": 0, "count": 5, **props} + self.id = id + self.name = name + self.status = 0 + self.running = False + self.stop_reason = None + self.pause = False + self.task = None + self._exception_count = 0 + self._max_exceptions = 2 + self.state_msg = "No state" + + def get_state(self): + return self.state_msg + + def stop(self): + self.stop_reason = "STOP" + self.running = False + + def start(self): + self.running = True + self.stop_reason = None + + def next(self): + self.stop_reason = "NEXT" + self.running = False + + async def reset(self): + pass + + async def update(self, props): + await self.cbpi.step2.update_props(self.id, props) + + async def run(self): + while self.running: + try: + await self.execute() + except: + self._exception_count += 1 + logging.error("Step has thrown exception") + if self._exception_count >= self._max_exceptions: + self.stop_reason = "MAX_EXCEPTIONS" + return (self.id, self.stop_reason) + await asyncio.sleep(1) + + return (self.id, self.stop_reason) + + @abstractmethod + async def execute(self): + pass class CBPiSimpleStep(metaclass=ABCMeta): diff --git a/cbpi/controller/dashboard_controller.py b/cbpi/controller/dashboard_controller.py index 221db71..7848351 100644 --- a/cbpi/controller/dashboard_controller.py +++ b/cbpi/controller/dashboard_controller.py @@ -2,7 +2,7 @@ import logging import json from cbpi.controller.crud_controller import CRUDController from cbpi.database.model import DashboardModel, DashboardContentModel - +import os class DashboardController(CRUDController): @@ -20,20 +20,24 @@ class DashboardController(CRUDController): return dict(items=self.cache) async def get_content(self, dashboard_id): - with open('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id) as json_file: - data = json.load(json_file) - return data + try: + with open('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id) as json_file: + data = json.load(json_file) + return data + except: + return {} + async def add_content(self, dashboard_id, data): with open('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id, 'w') as outfile: json.dump(data, outfile, indent=4, sort_keys=True) - print(data) + return {"status": "OK"} - async def delete_content(self, content_id): - await DashboardContentModel.delete(content_id) + async def delete_content(self, dashboard_id): + if os.path.exists('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id): + os.remove('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id) - async def delete_dashboard(self, dashboard_id): await DashboardContentModel.delete_by_dashboard_id(dashboard_id) diff --git a/cbpi/controller/plugin_controller.py b/cbpi/controller/plugin_controller.py index 953e258..c4f98f0 100644 --- a/cbpi/controller/plugin_controller.py +++ b/cbpi/controller/plugin_controller.py @@ -11,6 +11,7 @@ from cbpi.utils.utils import load_config logger = logging.getLogger(__name__) + class PluginController(): modules = {} types = {} @@ -34,18 +35,22 @@ class PluginController(): async def install(self, package_name): async def install(cbpi, plugins, package_name): - data = subprocess.check_output([sys.executable, "-m", "pip", "install", package_name]) + data = subprocess.check_output( + [sys.executable, "-m", "pip", "install", package_name]) data = data.decode('UTF-8') if package_name not in self.plugins: now = datetime.datetime.now() - self.plugins[package_name] = dict(version="1.0", installation_date=now.strftime("%Y-%m-%d %H:%M:%S")) + self.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(self.plugins, outfile, default_flow_style=False) if data.startswith('Requirement already satisfied'): - self.cbpi.notify(key="p", message="Plugin already installed ", type="warning") + self.cbpi.notify( + key="p", message="Plugin already installed ", type="warning") else: - self.cbpi.notify(key="p", message="Plugin installed ", type="success") + self.cbpi.notify( + key="p", message="Plugin installed ", type="success") async with aiohttp.ClientSession() as session: async with session.get('http://localhost:2202/get/%s' % package_name) as resp: @@ -55,17 +60,20 @@ class PluginController(): await self.cbpi.job.start_job(install(self.cbpi, self.plugins, data["package_name"]), data["package_name"], "plugins_install") return True else: - self.cbpi.notify(key="p", message="Failed to install Plugin %s " % package_name, type="danger") + self.cbpi.notify( + key="p", message="Failed to install Plugin %s " % package_name, type="danger") return False async def uninstall(self, package_name): async def uninstall(cbpi, plugins, package_name): print("try to uninstall", package_name) try: - data = subprocess.check_output([sys.executable, "-m", "pip", "uninstall", "-y", package_name]) + data = subprocess.check_output( + [sys.executable, "-m", "pip", "uninstall", "-y", package_name]) data = data.decode('UTF-8') if data.startswith("Successfully uninstalled"): - cbpi.notify(key="p", message="Plugin %s Uninstalled" % package_name, type="success") + cbpi.notify(key="p", message="Plugin %s Uninstalled" % + package_name, type="success") else: cbpi.notify(key="p", message=data, type="success") except Exception as e: @@ -83,15 +91,17 @@ class PluginController(): continue try: logger.info("Trying to load plugin %s" % filename) - data = load_config(os.path.join(this_directory, "../extension/%s/config.yaml" % filename)) - + data = load_config(os.path.join( + this_directory, "../extension/%s/config.yaml" % filename)) if (data.get("active") is True and data.get("version") == 4): - self.modules[filename] = import_module("cbpi.extension.%s" % (filename)) + self.modules[filename] = import_module( + "cbpi.extension.%s" % (filename)) self.modules[filename].setup(self.cbpi) logger.info("Plugin %s loaded successful" % filename) else: - logger.warning("Plugin %s is not supporting version 4" % filename) + logger.warning( + "Plugin %s is not supporting version 4" % filename) except Exception as e: print(e) @@ -132,9 +142,47 @@ class PluginController(): if issubclass(clazz, CBPiSimpleStep): self.cbpi.step.types[name] = self._parse_props(clazz) + if issubclass(clazz, CBPiStep): + self.cbpi.step2.types[name] = self._parse_step_props(clazz,name) + if issubclass(clazz, CBPiExtension): self.c = clazz(self.cbpi) + + def _parse_property_object(self, p): + if isinstance(p, Property.Number): + return {"label": p.label, "type": "number", "configurable": p.configurable, "description": p.description, "default_value": p.default_value} + elif isinstance(p, Property.Text): + return {"label": p.label, "type": "text", "configurable": p.configurable, "default_value": p.default_value, "description": p.description} + elif isinstance(p, Property.Select): + return {"label": p.label, "type": "select", "configurable": True, "options": p.options, "description": p.description} + elif isinstance(p, Property.Actor): + return {"label": p.label, "type": "actor", "configurable": p.configurable, "description": p.description} + elif isinstance(p, Property.Sensor): + return {"label": p.label, "type": "sensor", "configurable": p.configurable, "description": p.description} + elif isinstance(p, Property.Kettle): + return {"label": p.label, "type": "kettle", "configurable": p.configurable, "description": p.description} + + def _parse_step_props(self, cls, name): + + result = {"name": name, "class": cls, + "properties": [], "actions": []} + + if hasattr(cls, "cbpi_parameters"): + parameters = [] + for p in cls.cbpi_parameters: + parameters.append(self._parse_property_object(p)) + result["properties"] = parameters + for method_name, method in cls.__dict__.items(): + if hasattr(method, "action"): + key = method.__getattribute__("key") + parameters = [] + for p in method.__getattribute__("parameters"): + parameters.append(self._parse_property_object(p)) + result["actions"].append({"method": method_name, "label": key, "parameters": parameters}) + + return result + def _parse_props(self, cls): name = cls.__name__ @@ -142,7 +190,8 @@ class PluginController(): result = {"name": name, "class": cls, "properties": [], "actions": []} tmpObj = cls(cbpi=None, managed_fields=None) - members = [attr for attr in dir(tmpObj) if not callable(getattr(tmpObj, attr)) and not attr.startswith("__")] + members = [attr for attr in dir(tmpObj) if not callable( + getattr(tmpObj, attr)) and not attr.startswith("__")] for m in members: if isinstance(tmpObj.__getattribute__(m), Property.Number): t = tmpObj.__getattribute__(m) @@ -158,18 +207,22 @@ class PluginController(): {"name": m, "label": t.label, "type": "select", "configurable": True, "options": t.options, "description": t.description}) elif isinstance(tmpObj.__getattribute__(m), Property.Actor): t = tmpObj.__getattribute__(m) - result["properties"].append({"name": m, "label": t.label, "type": "actor", "configurable": t.configurable, "description": t.description}) + result["properties"].append( + {"name": m, "label": t.label, "type": "actor", "configurable": t.configurable, "description": t.description}) elif isinstance(tmpObj.__getattribute__(m), Property.Sensor): t = tmpObj.__getattribute__(m) - result["properties"].append({"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable, "description": t.description}) + result["properties"].append( + {"name": m, "label": t.label, "type": "sensor", "configurable": t.configurable, "description": t.description}) elif isinstance(tmpObj.__getattribute__(m), Property.Kettle): t = tmpObj.__getattribute__(m) - result["properties"].append({"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable, "description": t.description}) + result["properties"].append( + {"name": m, "label": t.label, "type": "kettle", "configurable": t.configurable, "description": t.description}) for method_name, method in cls.__dict__.items(): if hasattr(method, "action"): key = method.__getattribute__("key") parameters = method.__getattribute__("parameters") - result["actions"].append({"method": method_name, "label": key, "parameters": parameters}) + result["actions"].append( + {"method": method_name, "label": key, "parameters": parameters}) return result diff --git a/cbpi/controller/sensor_controller.py b/cbpi/controller/sensor_controller.py index bbd42e2..82fcde5 100644 --- a/cbpi/controller/sensor_controller.py +++ b/cbpi/controller/sensor_controller.py @@ -21,11 +21,8 @@ class SensorController(CRUDController): async def init(self): ''' This method initializes all actors during startup. It creates actor instances - :return: ''' - - await super(SensorController, self).init() for id, value in self.cache.items(): await self.init_sensor(value) @@ -34,6 +31,9 @@ class SensorController(CRUDController): return dict(items=self.cache,types=self.types) async def init_sensor(self, sensor): + + + print("INIT SENSOR") if sensor.type in self.types: cfg = sensor.config.copy() cfg.update(dict(cbpi=self.cbpi, id=sensor.id, name=sensor.name)) diff --git a/cbpi/controller/step_controller_ng.py b/cbpi/controller/step_controller_ng.py new file mode 100644 index 0000000..8f6bedd --- /dev/null +++ b/cbpi/controller/step_controller_ng.py @@ -0,0 +1,251 @@ +import asyncio + +from tabulate import tabulate +import json +import copy +import shortuuid +import logging +import os.path + +from ..api.step import CBPiStep + +class Step2(CBPiStep): + + async def execute(self): + + print(self.props) + await self.update(self.props) + print("HALLO") + #raise Exception("RROR") + +class StepControllerNg: + + def __init__(self, cbpi): + self.cbpi = cbpi + self.woohoo = "HALLLO" + self.logger = logging.getLogger(__name__) + self.path = os.path.join(".", 'config', "step_data.json") + self._loop = asyncio.get_event_loop() + self.basic_data = {} + self.step = None + self.types = {} + + self.cbpi.app.on_cleanup.append(self.shutdown) + + async def init(self): + logging.info("INIT STEP Controller") + self.load(startActive=True) + + def load(self, startActive=False): + + # create file if not exists + if os.path.exists(self.path) is False: + with open(self.path, "w") as file: + json.dump(dict(basic={}, profile=[]), file, indent=4, sort_keys=True) + + #load from json file + with open(self.path) as json_file: + data = json.load(json_file) + self.basic_data = data["basic"] + self.profile = data["profile"] + + # Start step after start up + self.profile = list(map(lambda item: {**item, "instance": self.create_step(item.get("id"), item.get("type"), item.get("name"), item.get("props", {}))}, self.profile)) + if startActive is True: + active_step = self.find_by_status("A") + if active_step is not None: + self._loop.create_task(self.start_step(active_step)) + + async def add(self, data): + logging.info("Add step") + print(data) + id = shortuuid.uuid() + item = {**{"status": "I", "props": {}}, **data, "id": id, "instance": self.create_step(id, data.get("type"), data.get("name"), data.get("props", {}))} + self.profile.append(item) + await self.save() + return item + + async def update(self, id, data): + logging.info("update step") + print(id, data) + #if "instance" in data: del data["instance"] + self.profile = list(map(lambda old: {**old, **data} if old["id"] == id else old, self.profile)) + print(tabulate(self.profile)) + await self.save() + return self.find_by_id(id) + + async def save(self): + logging.debug("save profile") + data = dict(basic=self.basic_data, profile=list(map(lambda x: dict(name=x["name"], type=x.get("type"), id=x["id"], status=x["status"],props=x["props"]), self.profile))) + with open(self.path, "w") as file: + json.dump(data, file, indent=4, sort_keys=True) + await self.push_udpate() + + async def start(self): + # already running + if self.find_by_status("A") is not None: + logging.error("Steps already running") + return + # Find next inactive step + step = self.find_by_status("P") + if step is not None: + + logging.info("Resume step") + + await self.start_step(step) + await self.save() + return + + step = self.find_by_status("I") + if step is not None: + logging.info("Start Step") + + await self.start_step(step) + await self.save() + return + + logging.info("BREWING COMPLETE") + + async def next(self): + logging.info("Trigger Next") + step = self.find_by_status("A") + if step is not None: + instance = step.get("instance") + if instance is not None: + logging.info("Next") + instance.next() + await instance.task + else: + logging.info("No Step is running") + + async def resume(self): + step = self.find_by_status("P") + if step is not None: + instance = step.get("instance") + if instance is not None: + await self.start_step(step) + else: + logging.info("Nothing to resume") + + async def stop(self): + logging.info("STOP STEP") + step = self.find_by_status("A") + if step != None and step.get("instance") is not None: + logging.info("CALLING STOP STEP") + instance = step.get("instance") + instance.stop() + # wait for task to be finished + await instance.task + logging.info("STEP STOPPED") + step["status"] = "P" + await self.save() + + async def reset_all(self): + step = self.find_by_status("A") + if step is not None: + logging.error("Please stop before reset") + return + for item in self.profile: + logging.info("Reset %s" % item.get("name")) + item["status"] = "I" + await item["instance"].reset() + await self.push_udpate() + + def create_step(self, id, type, name, props): + + type_cfg = self.types.get(type) + clazz = type_cfg.get("class") + return clazz(self.cbpi, id, name, {**props}) + + def create_dict(self, data): + return dict(name=data["name"], id=data["id"], type=data.get("type"), status=data["status"],props=data["props"], state_text=data["instance"].get_state()) + + def get_types2(self): + result = {} + for key, value in self.types.items(): + print(value) + result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions")) + return result + + def get_state(self): + return {"basic": self.basic_data, "profile": list(map(lambda x: self.create_dict(x), self.profile)), "types":self.get_types2()} + + async def move(self, id, direction): + index = self.get_index_by_id(id) + if direction not in [-1, 1]: + self.logger.error("Cant move. Direction 1 and -1 allowed") + return + self.profile[index], self.profile[index+direction] = self.profile[index+direction], self.profile[index] + self.save() + await self.push_udpate() + + async def delete(self, id): + step = self.find_by_id(id) + if step.get("status") == "A": + logging.error("Cant delete active Step %s", id) + return + + self.profile = list(filter(lambda x: x["id"] != id, self.profile)) + await self.save() + + + async def shutdown(self, app): + logging.info("Mash Profile Shutdonw") + for p in self.profile: + instance = p.get("instance") + # Stopping all running task + if instance.task != None and instance.task.done() is False: + logging.info("Stop Step") + instance.stop() + await instance.task + await self.save() + + def done(self, task): + id, reason = task.result() + print(id, reason) + if reason == "MAX_EXCEPTIONS": + step_current = self.find_by_id(id) + step_current["status"] = "E" + self._loop.create_task(self.save()) + return + + if reason == "NEXT": + step_current = self.find_by_status("A") + if step_current is not None: + step_current["status"] = "D" + async def wrapper(): + await self.save() + await self.start() + self._loop.create_task(wrapper()) + + + def find_by_status(self, status): + return next((item for item in self.profile if item["status"] == status), None) + + def find_by_id(self, id): + return next((item for item in self.profile if item["id"] == id), None) + + def get_index_by_id(self, id): + return next((i for i, item in enumerate(self.profile) if item["id"] == id), None) + + async def push_udpate(self): + print("PUS UPDATE") + await self.cbpi.bus.fire("step/update", data=list(map(lambda x: self.create_dict(x), self.profile))) + + async def start_step(self,step): + logging.info("############# start step") + step.get("instance").start() + step["instance"].task = self._loop.create_task(step["instance"].run(), name=step["name"]) + print(step["instance"].task) + step["instance"].task .add_done_callback(self.done) + step["status"] = "A" + + async def update_props(self, id, props): + logging.info("SAVE PROPS") + print(id, props) + step = self.find_by_id(id) + step["props"] = props + await self.save() + await self.push_udpate() + + \ No newline at end of file diff --git a/cbpi/craftbeerpi.py b/cbpi/craftbeerpi.py index 32d67af..8b0ccc4 100644 --- a/cbpi/craftbeerpi.py +++ b/cbpi/craftbeerpi.py @@ -19,6 +19,7 @@ from cbpi.controller.notification_controller import NotificationController from cbpi.controller.plugin_controller import PluginController from cbpi.controller.sensor_controller import SensorController from cbpi.controller.step_controller import StepController +from cbpi.controller.step_controller_ng import StepControllerNg from cbpi.controller.system_controller import SystemController from cbpi.controller.log_file_controller import LogController from cbpi.database.model import DBModel @@ -32,6 +33,7 @@ from cbpi.http_endpoints.http_dashboard import DashBoardHttpEndpoints from cbpi.http_endpoints.http_kettle import KettleHttpEndpoints from cbpi.http_endpoints.http_sensor import SensorHttpEndpoints from cbpi.http_endpoints.http_step import StepHttpEndpoints +from cbpi.http_endpoints.http_step2 import StepHttpEndpoints2 from cbpi.controller.translation_controller import TranslationController from cbpi.http_endpoints.http_translation import TranslationHttpEndpoint from cbpi.http_endpoints.http_plugin import PluginHttpEndpoints @@ -94,9 +96,11 @@ class CraftBeerPi(): self.kettle = KettleController(self) self.step = StepController(self) + self.step2 = StepControllerNg(self) self.dashboard = DashboardController(self) self.http_step = StepHttpEndpoints(self) + self.http_step2 = StepHttpEndpoints2(self) self.http_sensor = SensorHttpEndpoints(self) self.http_config = ConfigHttpEndpoints(self) self.http_actor = ActorHttpEndpoints(self) @@ -199,8 +203,8 @@ class CraftBeerPi(): print("SWAGGER.......") setup_swagger(self.app, description=long_description, - title=self.static_config.get("name", "CraftBeerPi"), - api_version=self.static_config.get("version", ""), + title=self.static_config.get("name", "CraftBeerPi 4.0"), + api_version=self.static_config.get("version", "4.0"), contact="info@craftbeerpi.com") def notify(self, key: str, message: str, type: str = "info") -> None: @@ -251,7 +255,7 @@ class CraftBeerPi(): self.plugin.load_plugins() self.plugin.load_plugins_from_evn() await self.sensor.init() - await self.step.init() + await self.step2.init() await self.actor.init() await self.kettle.init() await self.call_initializer(self.app) diff --git a/cbpi/eventbus.py b/cbpi/eventbus.py index b31157f..7bcf5a7 100644 --- a/cbpi/eventbus.py +++ b/cbpi/eventbus.py @@ -116,7 +116,7 @@ class CBPiEventBus(object): futures = {} - self.logger.info("FIRE %s %s" % (topic, kwargs)) + #self.logger.info("FIRE %s %s" % (topic, kwargs)) async def wait(futures): if(len(futures) > 0): diff --git a/cbpi/extension/dummystep/__init__.py b/cbpi/extension/dummystep/__init__.py index 1162936..6d9244e 100644 --- a/cbpi/extension/dummystep/__init__.py +++ b/cbpi/extension/dummystep/__init__.py @@ -38,6 +38,24 @@ class CustomStepCBPi(CBPiSimpleStep): #self.cbpi.notify(key="step", message="HELLO FROM STEP") +@parameters([Property.Number(label="Test", configurable=True), Property.Text(label="Test", configurable=True, default_value="HALLO")]) +class Step2(CBPiStep): + + i = 0 + + @action(key="name", parameters=[Property.Number(label="Test", configurable=True)]) + async def action(self, **kwargs): + print("HALLO") + + async def execute(self): + + print(self.props) + self.i += 1 + print(self.i) + self.state_msg = "COUNT %s" % self.i + await self.update(self.props) + print("JETZT GEHTS LO") + #raise Exception("RROR") def setup(cbpi): @@ -48,5 +66,5 @@ def setup(cbpi): :param cbpi: the cbpi core :return: ''' - + cbpi.plugin.register("CustomStep2", Step2) cbpi.plugin.register("CustomStepCBPi", CustomStepCBPi) diff --git a/cbpi/http_endpoints/http_config.py b/cbpi/http_endpoints/http_config.py index acf1fdb..19a2c0a 100644 --- a/cbpi/http_endpoints/http_config.py +++ b/cbpi/http_endpoints/http_config.py @@ -13,7 +13,7 @@ class ConfigHttpEndpoints(HttpCrudEndpoints): self.cbpi.register(self, "/config") @request_mapping(path="/{name}/", method="PUT", auth_required=False) - async def http_post(self, request) -> web.Response: + async def http_put(self, request) -> web.Response: """ --- @@ -26,6 +26,15 @@ class ConfigHttpEndpoints(HttpCrudEndpoints): description: "Parameter name" required: true type: "string" + - name: body + in: body + description: "Parameter Value" + required: true + schema: + type: object + properties: + value: + type: string responses: "204": description: successful operation diff --git a/cbpi/http_endpoints/http_dashboard.py b/cbpi/http_endpoints/http_dashboard.py index 9f01d65..4b39991 100644 --- a/cbpi/http_endpoints/http_dashboard.py +++ b/cbpi/http_endpoints/http_dashboard.py @@ -179,3 +179,26 @@ class DashBoardHttpEndpoints(HttpCrudEndpoints): dashboard_id = int(request.match_info['id']) await self.cbpi.dashboard.add_content(dashboard_id, data) return web.Response(status=204) + + @request_mapping(path="/{id:\d+}/content", method="DELETE", auth_required=False) + async def delete_conent(self, request): + """ + --- + description: Add Dashboard Content + tags: + - Dashboard + parameters: + - name: "id" + in: "path" + description: "Dashboard ID" + required: true + type: "integer" + format: "int64" + responses: + "200": + description: successful operation + """ + + dashboard_id = int(request.match_info['id']) + await self.cbpi.dashboard.delete_content(dashboard_id) + return web.Response(status=204) diff --git a/cbpi/http_endpoints/http_step2.py b/cbpi/http_endpoints/http_step2.py new file mode 100644 index 0000000..78b8b59 --- /dev/null +++ b/cbpi/http_endpoints/http_step2.py @@ -0,0 +1,200 @@ +from aiohttp import web +from cbpi.api import * + + +from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints + + +class StepHttpEndpoints2(): + + def __init__(self, cbpi): + self.cbpi = cbpi + self.controller = cbpi.step2 + self.cbpi.register(self, "/step2") + + def create_dict(self, data): + return dict(name=data["name"], id=data["id"], type=data.get("type"), status=data["status"],props=data["props"], state_text=data["instance"].get_state()) + + + @request_mapping(path="/", auth_required=False) + async def http_get_all(self, request): + + """ + --- + description: Get all steps + tags: + - Step2 + responses: + "200": + description: successful operation + """ + return web.json_response(data=self.controller.get_state()) + + @request_mapping(path="/", method="POST", auth_required=False) + async def http_add(self, request): + + """ + + --- + description: Add + tags: + - Step2 + parameters: + - in: body + name: body + description: Created an step + required: true + schema: + type: object + responses: + "200": + description: successful operation + """ + + data = await request.json() + result = await self.controller.add(data) + print("RESULT", result) + return web.json_response(self.create_dict(result)) + + @request_mapping(path="/{id}", method="PUT", auth_required=False) + async def http_update(self, request): + + """ + --- + description: Update + tags: + - Step2 + parameters: + - in: body + name: body + description: Created an kettle + required: false + schema: + type: object + responses: + "200": + description: successful operation + """ + + data = await request.json() + id = request.match_info['id'] + result = await self.controller.update(id, data) + print("RESULT", result) + return web.json_response(self.create_dict(result)) + + @request_mapping(path="/{id}", method="DELETE", auth_required=False) + async def http_delete(self, request): + """ + + --- + description: Delete + tags: + - Step2 + responses: + "204": + description: successful operation + """ + id = request.match_info['id'] + await self.controller.delete(id) + return web.Response(status=204) + + @request_mapping(path="/next", method="POST", auth_required=False) + async def http_next(self, request): + """ + + --- + description: Next + tags: + - Step2 + responses: + "204": + description: successful operation + """ + + await self.controller.next() + return web.Response(status=204) + + + @request_mapping(path="/move", method="PUT", auth_required=False) + async def http_move(self, request): + + """ + --- + description: Move + tags: + - Step2 + parameters: + - in: body + name: body + description: Created an kettle + required: false + schema: + type: object + properties: + id: + type: string + direction: + type: "integer" + format: "int64" + responses: + "204": + description: successful operation + """ + data = await request.json() + print("MOVE", data) + await self.controller.move(data["id"], data["direction"]) + + return web.Response(status=204) + + @request_mapping(path="/start", method="POST", auth_required=False) + async def http_start(self, request): + + """ + --- + description: Move + tags: + - Step2 + responses: + "204": + description: successful operation + """ + + await self.controller.start() + return web.Response(status=204) + + @request_mapping(path="/stop", method="POST", auth_required=False) + async def http_stop(self, request): + + """ + + --- + description: Stop Step + tags: + - Step2 + responses: + "204": + description: successful operation + """ + + await self.controller.stop() + return web.Response(status=204) + + + @request_mapping(path="/reset", method="POST", auth_required=False) + async def http_reset(self, request): + + """ + + --- + description: Move + tags: + - Step2 + responses: + "204": + description: successful operation + """ + print("RESE HTTP") + await self.controller.reset_all() + + return web.Response(status=204) + \ No newline at end of file diff --git a/cbpi/http_endpoints/http_system.py b/cbpi/http_endpoints/http_system.py index 2723a7b..1bd3180 100644 --- a/cbpi/http_endpoints/http_system.py +++ b/cbpi/http_endpoints/http_system.py @@ -26,7 +26,7 @@ class SystemHttpEndpoints: actor=self.cbpi.actor.get_state(), sensor=self.cbpi.sensor.get_state(), kettle=self.cbpi.kettle.get_state(), - step=await self.cbpi.step.get_state(), + step=self.cbpi.step2.get_state(), dashboard=self.cbpi.dashboard.get_state(), translations=self.cbpi.translation.get_all(), config=self.cbpi.config.get_state()) diff --git a/cbpi/job/aiohttp.py b/cbpi/job/aiohttp.py index e9e8b74..46bf99e 100644 --- a/cbpi/job/aiohttp.py +++ b/cbpi/job/aiohttp.py @@ -4,8 +4,7 @@ from aiohttp.web import View from . import create_scheduler -__all__ = ('setup', 'spawn', 'get_scheduler', 'get_scheduler_from_app', - 'atomic') +__all__ = ('setup', 'spawn', 'get_scheduler', 'get_scheduler_from_app', 'atomic') def get_scheduler(request): diff --git a/config/dashboard/cbpi_dashboard_1.json b/config/dashboard/cbpi_dashboard_1.json index b7b86db..fb8c467 100644 --- a/config/dashboard/cbpi_dashboard_1.json +++ b/config/dashboard/cbpi_dashboard_1.json @@ -1,196 +1,315 @@ { "elements": [ { - "id": "8bf7b72b-76e8-4c7a-86da-23f211d1942b", + "id": "db0c8199-6935-4c77-989a-28528b6743d7", "name": "Kettle", "props": { "heigth": "150", "width": "100" }, "type": "Kettle", - "x": 200, - "y": 115 - }, - { - "id": "78e5fe5a-6db8-4bd6-ba18-e67469cb1a0f", - "name": "Kettle", - "props": { - "heigth": "150", - "width": "100" - }, - "type": "Kettle", - "x": 610, - "y": 115 - }, - { - "id": "0f45b8b5-8ebd-41b2-9bd2-a957f655b84c", - "name": "Kettle", - "props": { - "heigth": "150", - "width": "100" - }, - "type": "Kettle", - "x": 410, - "y": 115 - }, - { - "id": "9d7d8ebf-ffad-4e35-96db-23d5b7f9c11c", - "name": "Sensor Data", - "props": { - "sensor": 1, - "unit": "\u00b0" - }, - "type": "Sensor", - "x": 240, - "y": 230 - }, - { - "id": "97331778-eca2-4e35-b94b-3337763a372c", - "name": "Sensor Data", - "props": { - "sensor": 1, - "unit": "\u00b0" - }, - "type": "Sensor", - "x": 450, - "y": 230 - }, - { - "id": "f20b7ed4-cdc8-46c2-a01a-ac0c0eab3801", - "name": "Sensor Data", - "props": { - "sensor": 1, - "unit": "\u00b0" - }, - "type": "Sensor", - "x": 655, - "y": 230 - }, - { - "id": "03eec665-ab3f-423b-bfb7-6faea856937b", - "name": "MashTun", - "props": { - "color": "#fff", - "size": "10" - }, - "type": "Text", "x": 205, - "y": 120 + "y": 155 }, { - "id": "9912ec53-71c7-4499-a1df-6466a4c02607", - "name": "Hot Liqour Tank", + "id": "35f8c20b-c801-4cf5-946c-29bcf88a989b", + "name": "Kettle", + "props": { + "heigth": "150", + "width": "100" + }, + "type": "Kettle", + "x": 400, + "y": 155 + }, + { + "id": "e62714ea-52c8-4544-af53-e7711fa3a087", + "name": "Kettle", + "props": { + "heigth": "150", + "width": "100" + }, + "type": "Kettle", + "x": 585, + "y": 155 + }, + { + "id": "d7f576b7-7fa7-4be7-8b31-68fef3b65777", + "name": "Mash", "props": { "color": "#fff", "size": "10" }, "type": "Text", - "x": 415, - "y": 120 + "x": 210, + "y": 135 }, { - "id": "4df11ebf-a8c9-4dc1-af8e-b6566bda0137", + "id": "36db0df9-922c-4cf6-8222-63f1eb34e22a", + "name": "HLT", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", + "x": 405, + "y": 135 + }, + { + "id": "7ae6a76b-712f-4d54-a661-7285b8f6d47b", "name": "Boil", "props": { "color": "#fff", "size": "10" }, "type": "Text", + "x": 590, + "y": 140 + }, + { + "id": "dfaad0f6-455c-4da6-9c82-789c6b36e046", + "name": "CraftBeerPi Brewery", + "props": { + "color": "#fff", + "size": "26" + }, + "type": "Text", + "x": 205, + "y": 75 + }, + { + "id": "4d2c8dfe-61a9-433d-83a8-72f74d17e7e5", + "name": "Sensor Data", + "props": { + "sensor": 1, + "unit": "\u00b0" + }, + "type": "Sensor", + "x": 245, + "y": 260 + }, + { + "id": "13a6b89d-50c7-4efb-b940-ec174e522314", + "name": "Sensor Data", + "props": { + "sensor": 1, + "unit": "\u00b0" + }, + "type": "Sensor", + "x": 445, + "y": 260 + }, + { + "id": "8d171952-791d-4f72-bfc9-dac8714b839f", + "name": "Sensor Data", + "props": { + "sensor": 1, + "unit": "\u00b0" + }, + "type": "Sensor", + "x": 630, + "y": 260 + }, + { + "id": "3963a344-8223-471f-aee6-5119e69f007f", + "name": "TargetTemp", + "props": { + "color": "#fff", + "kettle": "1", + "size": "12", + "unit": "\u00b0" + }, + "type": "TargetTemp", + "x": 215, + "y": 175 + }, + { + "id": "50333692-e956-4a8e-830f-934cd1d037c4", + "name": "TargetTemp", + "props": { + "color": "#fff", + "kettle": "1", + "size": "12", + "unit": "\u00b0" + }, + "type": "TargetTemp", + "x": 410, + "y": 175 + }, + { + "id": "28860d2d-f326-4375-a972-4e40a07bcf29", + "name": "Target Temp", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", + "x": 215, + "y": 160 + }, + { + "id": "2f6129ab-61a5-4080-95d3-8832f3f8d57e", + "name": "Target Temp", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", + "x": 410, + "y": 160 + }, + { + "id": "4b3f0ef9-61a8-4be2-9f6f-f954a04a77ce", + "name": "TargetTemp", + "props": { + "color": "#fff", + "kettle": "1", + "size": "12", + "unit": "\u00b0" + }, + "type": "TargetTemp", + "x": 595, + "y": 175 + }, + { + "id": "9fc5f252-7f83-4eb6-89f6-99fd343502b8", + "name": "Target temp", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", + "x": 595, + "y": 160 + }, + { + "id": "3ec3e5d8-f82e-40c1-8c41-cb8286659d3b", + "name": "Led", + "props": { + "actor": 1 + }, + "type": "Led", + "x": 245, + "y": 220 + }, + { + "id": "2e325539-6ed9-4e0d-b1dc-de860c47a1be", + "name": "Heater", + "props": { + "actor": 1 + }, + "type": "ActorButton", + "x": 210, + "y": 380 + }, + { + "id": "78b85989-c1bc-47e3-ad7b-0defeabb9bdc", + "name": "Pump", + "props": { + "actor": 2 + }, + "type": "ActorButton", + "x": 305, + "y": 380 + }, + { + "id": "3c3f81d0-cdfd-4521-a2fe-2f039f17b583", + "name": "Current Temp", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", + "x": 235, + "y": 280 + }, + { + "id": "0ac051db-5550-4dac-a8ba-e3c1f131704b", + "name": "Current Temp", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", + "x": 430, + "y": 280 + }, + { + "id": "e9c833c2-6c87-4849-9ada-479ec95e79da", + "name": "Current Temp", + "props": { + "color": "#fff", + "size": "10" + }, + "type": "Text", "x": 615, - "y": 120 - }, - { - "id": "e51ca9a8-e911-42ea-ba19-ca21cfed72ce", - "name": "Current Temp", - "props": { - "color": "#fff", - "size": "10" - }, - "type": "Text", - "x": 220, - "y": 215 - }, - { - "id": "6eb1d953-c579-4417-89ef-55cb472517a5", - "name": "Current Temp", - "props": { - "color": "#fff", - "size": "10" - }, - "type": "Text", - "x": 435, - "y": 215 - }, - { - "id": "ad3087a4-5b8f-4ade-b665-4b2acbbaecc2", - "name": "Current Temp", - "props": { - "color": "#fff", - "size": "10" - }, - "type": "Text", - "x": 635, - "y": 215 - }, - { - "id": "891eac86-3d81-4438-b9c4-65035c71e52f", - "name": "CraftBeerPi v4.0", - "props": { - "color": "#fff", - "size": "30" - }, - "type": "Text", - "x": 200, - "y": 65 + "y": 280 } ], "pathes": [ { "coordinates": [ [ - 670, - 260 + 305, + 185 ], [ - 670, - 305 - ], - [ - 265, - 305 - ], - [ - 265, - 260 + 405, + 185 ] ], - "id": "74b4bf4d-3bb8-44d8-a50b-f27c915f6218" + "id": "49e7684e-21a3-4e0b-8e94-60f95abee80f" }, { "coordinates": [ + [ + 400, + 275 + ], [ 305, - 230 - ], - [ - 410, - 230 + 275 ] ], - "id": "c967d63c-7ec5-42b9-bbc3-e0d85b420670" + "id": "5ba909c1-49a9-46e5-a6d0-1d0350c37aa4" }, { "coordinates": [ [ - 410, - 145 + 255, + 300 ], [ - 300, - 145 + 255, + 350 + ], + [ + 555, + 350 + ], + [ + 555, + 200 + ], + [ + 585, + 200 ] ], - "id": "e5498af8-3086-4bee-b35b-085a13bbd2b2" + "id": "aed2d4d3-b99e-4af5-b8cf-d92d47721be4" + }, + { + "coordinates": [ + [ + 685, + 275 + ], + [ + 805, + 275 + ] + ], + "id": "176fed29-56c2-4534-9cab-8c328d0e138c" } ] } \ No newline at end of file diff --git a/config/step_data.json b/config/step_data.json new file mode 100644 index 0000000..672898f --- /dev/null +++ b/config/step_data.json @@ -0,0 +1,35 @@ +{ + "basic": {}, + "profile": [ + { + "id": "eopJy6oxGqrNuRNtiAPXvN", + "name": "AMAZING", + "props": { + "count": 5, + "wohoo": 0 + }, + "status": "P", + "type": "CustomStep2" + }, + { + "id": "duxvgLknKLjGYhdm9TKqUE", + "name": "Manuel", + "props": { + "count": 5, + "wohoo": 0 + }, + "status": "I", + "type": "CustomStep2" + }, + { + "id": "hyXYDBUAENgwD7yNwaeLe7", + "name": "HALLO", + "props": { + "count": 5, + "wohoo": 0 + }, + "status": "I", + "type": "CustomStep2" + } + ] +} \ No newline at end of file diff --git a/craftbeerpi.db b/craftbeerpi.db index 23fc9f76527756f9ee9f60cc98b572435803c42e..bae4f665c7056a63c3fa91ceb0bc75638bd45e95 100644 GIT binary patch delta 350 zcmZozz}&Ead4e?KqKPujjEgoVEY#=czr?`Azm0)^8~-!@dHk)L1qDj^C%4)ghDb58 zGRP_mdZZ?nq!uX{I+qrgvnl&lm?H~;r%65!=u#=y+a z#lY{#e~X_B=(4GNKu0M`>Nc{5xcUafhx&PjfSh3HmYSH7HhEpXFRLjto2vR`+k8oZ zOhd!slA_GKbf==!^3Q)qJTD=NJ`3!`Z@Z#`X=U;rsg2jDEKAjrskFA`3f#b#Xq delta 272 zcmZozz}&Ead4e=!=|mZ4#?p-m3-$RK7#JA&Uor5%;-3eEt(ye}O8FzG^u(!dLpgV?c| z%i$8gGLk_uvdU0B!6=q&{_oEu0Mf_IZ^6Jnn?IM|0_ZX=epX{MXBYfMU@JEiMgqHr8zk|Ol+dslJcT{j=rv*c`2ER3IT~ZsS1ucsfGv*lP&V| I78N7_01>52%K!iX diff --git a/data.txt b/data.txt new file mode 100644 index 0000000..e2777c8 --- /dev/null +++ b/data.txt @@ -0,0 +1 @@ +{"people": [{"name": "Scott", "website": "stackabuse.com", "from": "Nebraska"}, {"name": "Larry", "website": "google.com", "from": "Michigan"}, {"name": "Tim", "website": "apple.com", "from": "Alabama"}]} \ No newline at end of file diff --git a/sample.py b/sample.py deleted file mode 100644 index 249930b..0000000 --- a/sample.py +++ /dev/null @@ -1,19 +0,0 @@ -import datetime - -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 = {} - - -now = datetime.datetime.now() - -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/sampletest.py b/sampletest.py new file mode 100644 index 0000000..e69de29 diff --git a/step_data.json b/step_data.json new file mode 100644 index 0000000..f1c1462 --- /dev/null +++ b/step_data.json @@ -0,0 +1,6 @@ +{ + "basic": {}, + "profile": [ + + ] +} \ No newline at end of file diff --git a/tests/test_step_ng.py b/tests/test_step_ng.py new file mode 100644 index 0000000..7bc8383 --- /dev/null +++ b/tests/test_step_ng.py @@ -0,0 +1,46 @@ +import logging +from unittest import mock +import unittest +from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop +from cbpi.craftbeerpi import CraftBeerPi +import pprint +import asyncio +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s') +pp = pprint.PrettyPrinter(indent=4) + +class ActorTestCase(AioHTTPTestCase): + + async def get_application(self): + self.cbpi = CraftBeerPi() + await self.cbpi.init_serivces() + return self.cbpi.app + ''' + @unittest_run_loop + async def test_get_all(self): + resp = await self.client.get(path="/step2") + assert resp.status == 200 + + @unittest_run_loop + async def test_add_step(self): + resp = await self.client.post(path="/step2", json=dict(name="Manuel")) + data = await resp.json() + assert resp.status == 200 + + @unittest_run_loop + async def test_delete(self): + + resp = await self.client.post(path="/step2", json=dict(name="Manuel")) + data = await resp.json() + assert resp.status == 200 + resp = await self.client.delete(path="/step2/%s" % data["id"]) + assert resp.status == 204 + ''' + + @unittest_run_loop + async def test_move(self): + await self.cbpi.step2.resume() + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file