mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-21 14:38:15 +01:00
new step controller
This commit is contained in:
parent
8ced60706b
commit
05e08d0dc6
26 changed files with 1125 additions and 201 deletions
114
.vscode/.ropeproject/config.py
vendored
Normal file
114
.vscode/.ropeproject/config.py
vendored
Normal file
|
@ -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 <package> import <module>` 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!
|
BIN
.vscode/.ropeproject/objectdb
vendored
Normal file
BIN
.vscode/.ropeproject/objectdb
vendored
Normal file
Binary file not shown.
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -29,8 +29,6 @@ class Property(object):
|
|||
'''
|
||||
def __init__(self, label, configurable=False, default_value=None, unit="", description=""):
|
||||
'''
|
||||
Test
|
||||
|
||||
|
||||
:param label:
|
||||
:param configurable:
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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,19 +20,23 @@ 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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
251
cbpi/controller/step_controller_ng.py
Normal file
251
cbpi/controller/step_controller_ng.py
Normal file
|
@ -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()
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
200
cbpi/http_endpoints/http_step2.py
Normal file
200
cbpi/http_endpoints/http_step2.py
Normal file
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
35
config/step_data.json
Normal file
35
config/step_data.json
Normal file
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
BIN
craftbeerpi.db
BIN
craftbeerpi.db
Binary file not shown.
1
data.txt
Normal file
1
data.txt
Normal file
|
@ -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"}]}
|
19
sample.py
19
sample.py
|
@ -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)
|
0
sampletest.py
Normal file
0
sampletest.py
Normal file
6
step_data.json
Normal file
6
step_data.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"basic": {},
|
||||
"profile": [
|
||||
|
||||
]
|
||||
}
|
46
tests/test_step_ng.py
Normal file
46
tests/test_step_ng.py
Normal file
|
@ -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()
|
Loading…
Reference in a new issue