"added dataclassed"
3
.gitignore
vendored
|
@ -12,3 +12,6 @@ node_modules
|
|||
.DS_STORE
|
||||
.vscode
|
||||
.DS_Store
|
||||
.vscode/
|
||||
config/
|
||||
logs/
|
114
.vscode/.ropeproject/config.py
vendored
|
@ -1,114 +0,0 @@
|
|||
# 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
BIN
cbpi/.DS_Store
vendored
|
@ -1 +1 @@
|
|||
__version__ = "4.0.0.20"
|
||||
__version__ = "4.0.0.21"
|
|
@ -14,8 +14,7 @@ __all__ = ["CBPiActor",
|
|||
"SensorException",
|
||||
"ActorException",
|
||||
"CBPiSensor",
|
||||
"CBPiStep",
|
||||
"Stop_Reason"]
|
||||
"CBPiStep"]
|
||||
|
||||
from cbpi.api.actor import *
|
||||
from cbpi.api.sensor import *
|
||||
|
|
|
@ -27,18 +27,35 @@ class CBPiActor(metaclass=ABCMeta):
|
|||
def log_data(self, value):
|
||||
self.cbpi.log.log_data(self.id, value)
|
||||
|
||||
async def run(self):
|
||||
while self.running:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def get_state(self):
|
||||
return dict(state=self.state)
|
||||
|
||||
async def start(self):
|
||||
self.running = True
|
||||
pass
|
||||
|
||||
async def stop(self):
|
||||
self.running = False
|
||||
pass
|
||||
|
||||
async def on_start(self):
|
||||
pass
|
||||
|
||||
async def on_stop(self):
|
||||
pass
|
||||
|
||||
async def run(self):
|
||||
pass
|
||||
|
||||
async def _run(self):
|
||||
|
||||
try:
|
||||
await self.on_start()
|
||||
self.cancel_reason = await self.run()
|
||||
except asyncio.CancelledError as e:
|
||||
pass
|
||||
finally:
|
||||
await self.on_stop()
|
||||
|
||||
|
||||
|
||||
def get_static_config_value(self,name,default):
|
||||
return self.cbpi.static_config.get(name, default)
|
||||
|
|
|
@ -24,7 +24,7 @@ class CBPiBase(metaclass=ABCMeta):
|
|||
return self.cbpi.kettle.find_by_id(id)
|
||||
|
||||
def get_kettle_target_temp(self,id):
|
||||
return self.cbpi.kettle.find_by_id(id).get("target_temp")
|
||||
return self.cbpi.kettle.find_by_id(id).target_temp
|
||||
|
||||
async def set_target_temp(self,id, temp):
|
||||
await self.cbpi.kettle.set_target_temp(id, temp)
|
||||
|
@ -33,6 +33,7 @@ class CBPiBase(metaclass=ABCMeta):
|
|||
return self.cbpi.sensor.find_by_id(id)
|
||||
|
||||
def get_sensor_value(self,id):
|
||||
|
||||
return self.cbpi.sensor.get_sensor_value(id)
|
||||
|
||||
def get_actor(self,id):
|
||||
|
@ -46,22 +47,17 @@ class CBPiBase(metaclass=ABCMeta):
|
|||
logging.error("Faild to read actor state in step - actor {}".format(id))
|
||||
return None
|
||||
|
||||
|
||||
|
||||
async def actor_on(self,id):
|
||||
|
||||
try:
|
||||
print("\n\n ON\n\n\n\n" )
|
||||
await self.cbpi.actor.on(id)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
async def actor_off(self,id):
|
||||
try:
|
||||
print("\n\n OFF\n\n\n\n" )
|
||||
await self.cbpi.actor.off(id)
|
||||
except Exception as e:
|
||||
print("E", e)
|
||||
pass
|
||||
|
||||
|
||||
|
|
144
cbpi/api/dataclasses.py
Normal file
|
@ -0,0 +1,144 @@
|
|||
from cbpi.api.config import ConfigType
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from cbpi.api.step import StepState
|
||||
from dataclasses import dataclass
|
||||
|
||||
class Props:
|
||||
|
||||
def __init__(self, data={}):
|
||||
super(Props, self).__setattr__('__data__', {})
|
||||
for key, value in data.items():
|
||||
self.__setattr__(key, value)
|
||||
def __getattr__(self, name):
|
||||
return self.__data__.get(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
self.__data__[name] = value
|
||||
|
||||
def __str__(self):
|
||||
return self.__data__.__str__()
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.__data__[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__data__[key] = value
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.__data__
|
||||
|
||||
def get(self, key, d=None):
|
||||
if key in self.__data__:
|
||||
return self.__data__[key]
|
||||
else:
|
||||
return d
|
||||
|
||||
def to_dict(self):
|
||||
|
||||
def parse_object(value):
|
||||
if isinstance(value, Props):
|
||||
return value.to_dict()
|
||||
elif isinstance(value, list):
|
||||
return list(map(parse_object, value))
|
||||
else:
|
||||
return value
|
||||
|
||||
return dict((key, parse_object(value)) for (key, value) in self.__data__.items())
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class Actor:
|
||||
id: str = None
|
||||
name: str = None
|
||||
props: Props = Props()
|
||||
state: bool = False
|
||||
type: str = None
|
||||
instance: str = None
|
||||
|
||||
def __str__(self):
|
||||
return "name={} props={}, state={}, type={}".format(self.name, self.props, self.state, self.type)
|
||||
def to_dict(self):
|
||||
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state())
|
||||
|
||||
|
||||
@dataclass
|
||||
class Sensor:
|
||||
id: str = None
|
||||
name: str = None
|
||||
props: Props = Props()
|
||||
state: bool = False
|
||||
type: str = None
|
||||
instance: str = None
|
||||
|
||||
def __str__(self):
|
||||
return "name={} props={}, state={}".format(self.name, self.props, self.state)
|
||||
def to_dict(self):
|
||||
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.state)
|
||||
|
||||
@dataclass
|
||||
class Kettle:
|
||||
id: str = None
|
||||
name: str = None
|
||||
props: Props = Props()
|
||||
instance: str = None
|
||||
agitator: Actor = None
|
||||
heater: Actor = None
|
||||
sensor: Sensor = None
|
||||
type: str = None
|
||||
target_temp: int = 0
|
||||
|
||||
def __str__(self):
|
||||
return "name={} props={} temp={}".format(self.name, self.props, self.target_temp)
|
||||
def to_dict(self):
|
||||
|
||||
if self.instance is not None:
|
||||
|
||||
state = self.instance.state
|
||||
print("READ STATE", state)
|
||||
else:
|
||||
state = False
|
||||
return dict(id=self.id, name=self.name, state=state, target_temp=self.target_temp, heater=self.heater, agitator=self.agitator, sensor=self.sensor, type=self.type, props=self.props.to_dict())
|
||||
|
||||
@dataclass
|
||||
class Step:
|
||||
id: str = None
|
||||
name: str = None
|
||||
props: Props = Props()
|
||||
type: str = None
|
||||
status: StepState = StepState.INITIAL
|
||||
instance: str = None
|
||||
|
||||
def __str__(self):
|
||||
return "name={} props={}, type={}, instance={}".format(self.name, self.props, self.type, self.instance)
|
||||
def to_dict(self):
|
||||
|
||||
msg = self.instance.summary if self.instance is not None else ""
|
||||
|
||||
return dict(id=self.id, name=self.name, state_text=msg, type=self.type, status=self.status.value, props=self.props.to_dict())
|
||||
|
||||
|
||||
|
||||
class ConfigType(Enum):
|
||||
STRING="string"
|
||||
ACTOR="actor"
|
||||
SENSOR="sensor"
|
||||
KETTLE="kettle"
|
||||
NUMBER="number"
|
||||
SELECT="select"
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
|
||||
name: str = None
|
||||
value: Any = None
|
||||
description: str = None
|
||||
type: ConfigType = ConfigType.STRING
|
||||
options: Any = None
|
||||
|
||||
def __str__(self):
|
||||
return "....name={} value={}".format(self.name, self.value)
|
||||
def to_dict(self):
|
||||
return dict(name=self.name, value=self.value, type=self.type.value, description=self.description, options=self.options)
|
|
@ -18,18 +18,34 @@ class CBPiKettleLogic(CBPiBase, metaclass=ABCMeta):
|
|||
def init(self):
|
||||
pass
|
||||
|
||||
async def on_start(self):
|
||||
pass
|
||||
|
||||
async def on_stop(self):
|
||||
pass
|
||||
|
||||
async def run(self):
|
||||
self.state = True
|
||||
while self.running:
|
||||
await asyncio.sleep(1)
|
||||
self.state = False
|
||||
pass
|
||||
|
||||
async def _run(self):
|
||||
|
||||
try:
|
||||
await self.on_start()
|
||||
self.cancel_reason = await self.run()
|
||||
except asyncio.CancelledError as e:
|
||||
pass
|
||||
finally:
|
||||
await self.on_stop()
|
||||
|
||||
def get_state(self):
|
||||
return dict(running=self.running)
|
||||
return dict(running=self.state)
|
||||
|
||||
async def start(self):
|
||||
self.running = True
|
||||
|
||||
self.state = True
|
||||
|
||||
async def stop(self):
|
||||
|
||||
self.task.cancel()
|
||||
await self.task
|
||||
self.state = False
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import asyncio
|
||||
import logging
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from cbpi.api.extension import CBPiExtension
|
||||
|
||||
from cbpi.api.config import ConfigType
|
||||
|
||||
from cbpi.api.base import CBPiBase
|
||||
|
||||
class CBPiSensor(CBPiBase, metaclass=ABCMeta):
|
||||
|
@ -22,10 +23,6 @@ class CBPiSensor(CBPiBase, metaclass=ABCMeta):
|
|||
def log_data(self, value):
|
||||
self.cbpi.log.log_data(self.id, value)
|
||||
|
||||
@abstractmethod
|
||||
async def run(self):
|
||||
self.logger.warning("Sensor Init not implemented")
|
||||
|
||||
def get_state(self):
|
||||
pass
|
||||
|
||||
|
@ -42,7 +39,26 @@ class CBPiSensor(CBPiBase, metaclass=ABCMeta):
|
|||
logging.error("Faild to push sensor update")
|
||||
|
||||
async def start(self):
|
||||
self.running = True
|
||||
pass
|
||||
|
||||
async def stop(self):
|
||||
self.running = False
|
||||
pass
|
||||
|
||||
async def on_start(self):
|
||||
pass
|
||||
|
||||
async def on_stop(self):
|
||||
pass
|
||||
|
||||
async def run(self):
|
||||
pass
|
||||
|
||||
async def _run(self):
|
||||
|
||||
try:
|
||||
await self.on_start()
|
||||
self.cancel_reason = await self.run()
|
||||
except asyncio.CancelledError as e:
|
||||
pass
|
||||
finally:
|
||||
await self.on_stop()
|
||||
|
|
145
cbpi/api/step.py
|
@ -1,84 +1,99 @@
|
|||
import json
|
||||
import time
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from abc import abstractmethod, ABCMeta
|
||||
import logging
|
||||
from cbpi.api.config import ConfigType
|
||||
from cbpi.api.base import CBPiBase
|
||||
import time
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from enum import Enum
|
||||
__all__ = ["Stop_Reason", "CBPiStep"]
|
||||
class Stop_Reason(Enum):
|
||||
STOP = 1
|
||||
NEXT = 2
|
||||
|
||||
from cbpi.api.base import CBPiBase
|
||||
from cbpi.api.config import ConfigType
|
||||
|
||||
class CBPiStep(CBPiBase, metaclass=ABCMeta):
|
||||
def __init__(self, cbpi, id, name, props) :
|
||||
self.cbpi = cbpi
|
||||
self.props = {**props}
|
||||
self.id = id
|
||||
__all__ = ["StepResult", "StepState", "StepMove", "CBPiStep"]
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class StepResult(Enum):
|
||||
STOP=1
|
||||
NEXT=2
|
||||
DONE=3
|
||||
ERROR=4
|
||||
|
||||
class StepState(Enum):
|
||||
INITIAL="I"
|
||||
DONE="D"
|
||||
ACTIVE="A"
|
||||
ERROR="E"
|
||||
STOP="S"
|
||||
|
||||
class StepMove(Enum):
|
||||
UP=-1
|
||||
DONW=1
|
||||
|
||||
class CBPiStep(CBPiBase):
|
||||
|
||||
def __init__(self, cbpi, id, name, props, on_done) -> None:
|
||||
self.name = name
|
||||
self.status = 0
|
||||
self.running = False
|
||||
self.stop_reason = None
|
||||
self.pause = False
|
||||
self.task = None
|
||||
self._task = None
|
||||
self._exception_count = 0
|
||||
self._max_exceptions = 2
|
||||
self.state_msg = ""
|
||||
self.cbpi = cbpi
|
||||
self.id = id
|
||||
self.timer = None
|
||||
self._done_callback = on_done
|
||||
self.props = props
|
||||
self.cancel_reason: StepResult = None
|
||||
self.summary = ""
|
||||
|
||||
def get_state(self):
|
||||
return self.state_msg
|
||||
|
||||
def push_update(self):
|
||||
self.cbpi.step.push_udpate()
|
||||
|
||||
async def stop(self):
|
||||
self.stop_reason = Stop_Reason.STOP
|
||||
self._task.cancel()
|
||||
await self._task
|
||||
def _done(self, task):
|
||||
self._done_callback(self, task.result())
|
||||
|
||||
async def start(self):
|
||||
self.stop_reason = None
|
||||
self._task = asyncio.create_task(self.run())
|
||||
self._task.add_done_callback(self.cbpi.step.done)
|
||||
self.task = asyncio.create_task(self._run())
|
||||
self.task.add_done_callback(self._done)
|
||||
|
||||
async def next(self):
|
||||
self.stop_reason = Stop_Reason.NEXT
|
||||
self._task.cancel()
|
||||
self.cancel_reason = StepResult.NEXT
|
||||
self.task.cancel()
|
||||
await self.task
|
||||
|
||||
async def stop(self):
|
||||
try:
|
||||
self.cancel_reason = StepResult.STOP
|
||||
self.task.cancel()
|
||||
await self.task
|
||||
except:
|
||||
pass
|
||||
|
||||
async def reset(self):
|
||||
pass
|
||||
|
||||
async def on_props_update(self, props):
|
||||
self.props = {**self.props, **props}
|
||||
|
||||
|
||||
def on_props_update(self, props):
|
||||
self.props = props
|
||||
|
||||
async def update(self, props):
|
||||
await self.cbpi.step.update_props(self.id, props)
|
||||
|
||||
async def run(self):
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
await self.execute()
|
||||
except asyncio.CancelledError as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
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)
|
||||
except asyncio.CancelledError as e:
|
||||
return self.id, self.stop_reason
|
||||
|
||||
|
||||
@abstractmethod
|
||||
async def execute(self):
|
||||
async def save_props(self, props):
|
||||
pass
|
||||
|
||||
async def push_update(self):
|
||||
self.cbpi.step.push_udpate()
|
||||
|
||||
async def on_start(self):
|
||||
pass
|
||||
|
||||
async def on_stop(self):
|
||||
pass
|
||||
|
||||
async def _run(self):
|
||||
try:
|
||||
await self.on_start()
|
||||
await self.run()
|
||||
self.cancel_reason = StepResult.DONE
|
||||
except asyncio.CancelledError as e:
|
||||
pass
|
||||
finally:
|
||||
await self.on_stop()
|
||||
|
||||
return self.cancel_reason
|
||||
|
||||
@abstractmethod
|
||||
async def run(self):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return "name={} props={}, type={}".format(self.name, self.props, self.__class__.__name__)
|
||||
|
|
|
@ -5,33 +5,40 @@ import math
|
|||
|
||||
class Timer(object):
|
||||
|
||||
def __init__(self, timeout, callback, update = None) -> None:
|
||||
def __init__(self, timeout, on_done = None, on_update = None) -> None:
|
||||
super().__init__()
|
||||
self.timeout = timeout
|
||||
self._timemout = self.timeout
|
||||
self._task = None
|
||||
self._callback = callback
|
||||
self._update = update
|
||||
self._callback = on_done
|
||||
self._update = on_update
|
||||
self.start_time = None
|
||||
|
||||
def done(self, task):
|
||||
if self._callback is not None:
|
||||
asyncio.create_task(self._callback(self))
|
||||
|
||||
async def _job(self):
|
||||
self.start_time = time.time()
|
||||
self.count = int(round(self._timemout, 0))
|
||||
try:
|
||||
for seconds in range(self.count, -1, -1):
|
||||
for seconds in range(self.count, 0, -1):
|
||||
if self._update is not None:
|
||||
await self._update(seconds, self.format_time(seconds))
|
||||
await self._update(self,seconds)
|
||||
await asyncio.sleep(1)
|
||||
self._callback()
|
||||
|
||||
except asyncio.CancelledError:
|
||||
end = time.time()
|
||||
duration = end - self.start_time
|
||||
self._timemout = self._timemout - duration
|
||||
|
||||
|
||||
def start(self):
|
||||
self._task = asyncio.create_task(self._job())
|
||||
self._task.add_done_callback(self.done)
|
||||
|
||||
async def stop(self):
|
||||
if self._task and self._task.done() is False:
|
||||
self._task.cancel()
|
||||
await self._task
|
||||
|
||||
|
@ -51,7 +58,8 @@ class Timer(object):
|
|||
def get_time(self):
|
||||
return self.format_time(int(round(self._timemout,0)))
|
||||
|
||||
def format_time(self, time):
|
||||
@classmethod
|
||||
def format_time(cls, time):
|
||||
pattern = '{0:02d}:{1:02d}:{2:02d}'
|
||||
seconds = time % 60
|
||||
minutes = math.floor(time / 60) % 60
|
||||
|
|
|
@ -24,7 +24,6 @@ def create_config_file():
|
|||
srcfile = os.path.join(os.path.dirname(__file__), "config", "config.yaml")
|
||||
destfile = os.path.join(".", 'config')
|
||||
shutil.copy(srcfile, destfile)
|
||||
print("Config Folder created")
|
||||
|
||||
if os.path.exists(os.path.join(".", 'config', "actor.json")) is False:
|
||||
srcfile = os.path.join(os.path.dirname(__file__), "config", "actor.json")
|
||||
|
@ -46,10 +45,16 @@ def create_config_file():
|
|||
destfile = os.path.join(".", 'config')
|
||||
shutil.copy(srcfile, destfile)
|
||||
|
||||
if os.path.exists(os.path.join(".", 'config', "config.json")) is False:
|
||||
srcfile = os.path.join(os.path.dirname(__file__), "config", "config.json")
|
||||
destfile = os.path.join(".", 'config')
|
||||
shutil.copy(srcfile, destfile)
|
||||
|
||||
if os.path.exists(os.path.join(".", 'config', "dashboard", "cbpi_dashboard_1.json")) is False:
|
||||
srcfile = os.path.join(os.path.dirname(__file__), "config", "dashboard", "cbpi_dashboard_1.json")
|
||||
destfile = os.path.join(".", "config", "dashboard")
|
||||
shutil.copy(srcfile, destfile)
|
||||
print("Config Folder created")
|
||||
|
||||
|
||||
def create_home_folder_structure():
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
from cbpi.controller.basic_controller import BasicController
|
||||
from cbpi.api.dataclasses import Actor
|
||||
from cbpi.controller.basic_controller2 import BasicController
|
||||
import logging
|
||||
from tabulate import tabulate
|
||||
class ActorController(BasicController):
|
||||
|
||||
def __init__(self, cbpi):
|
||||
super(ActorController, self).__init__(cbpi, "actor.json")
|
||||
super(ActorController, self).__init__(cbpi, Actor,"actor.json")
|
||||
self.update_key = "actorupdate"
|
||||
|
||||
|
||||
async def on(self, id):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
await instance.on()
|
||||
|
||||
if item.instance.state is False:
|
||||
await item.instance.on()
|
||||
await self.push_udpate()
|
||||
await self.cbpi.satellite.publish("cbpi/actor/on", "ACTOR ON")
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e))
|
||||
|
||||
async def off(self, id):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
await instance.off()
|
||||
if item.instance.state is True:
|
||||
await item.instance.off()
|
||||
await self.push_udpate()
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e))
|
||||
|
@ -33,12 +37,3 @@ class ActorController(BasicController):
|
|||
except Exception as e:
|
||||
logging.error("Faild to switch on Actor {} {}".format(id, e))
|
||||
|
||||
|
||||
def create_dict(self, data):
|
||||
try:
|
||||
instance = data.get("instance")
|
||||
state = state=instance.get_state()
|
||||
except Exception as e:
|
||||
logging.error("Faild to create actor dict {} ".format(e))
|
||||
state = dict()
|
||||
return dict(name=data.get("name"), id=data.get("id"), type=data.get("type"), state=state,props=data.get("props", []))
|
|
@ -2,6 +2,7 @@
|
|||
import logging
|
||||
import os.path
|
||||
import json
|
||||
from cbpi.api.dataclasses import Actor, Props
|
||||
import sys, os
|
||||
import shortuuid
|
||||
import asyncio
|
||||
|
@ -10,7 +11,8 @@ from tabulate import tabulate
|
|||
|
||||
class BasicController:
|
||||
|
||||
def __init__(self, cbpi, file):
|
||||
def __init__(self, cbpi, resource, file):
|
||||
self.resource = resource
|
||||
self.update_key = ""
|
||||
self.name = self.__class__.__name__
|
||||
self.cbpi = cbpi
|
||||
|
@ -27,56 +29,56 @@ class BasicController:
|
|||
async def init(self):
|
||||
await self.load()
|
||||
|
||||
def create(self, data):
|
||||
|
||||
return self.resource(data.get("id"), data.get("name"), type=data.get("type"), props=Props(data.get("props", {})) )
|
||||
|
||||
async def load(self):
|
||||
logging.info("{} Load ".format(self.name))
|
||||
with open(self.path) as json_file:
|
||||
data = json.load(json_file)
|
||||
self.data = data["data"]
|
||||
|
||||
for i in data["data"]:
|
||||
self.data.append(self.create(i))
|
||||
|
||||
if self.autostart is True:
|
||||
for d in self.data:
|
||||
for item in self.data:
|
||||
logging.info("{} Starting ".format(self.name))
|
||||
await self.start(d.get("id"))
|
||||
await self.start(item.id)
|
||||
await self.push_udpate()
|
||||
|
||||
async def save(self):
|
||||
logging.info("{} Save ".format(self.name))
|
||||
data = dict(data=list(map(lambda x: self.create_dict(x), self.data)))
|
||||
data = dict(data=list(map(lambda actor: actor.to_dict(), self.data)))
|
||||
with open(self.path, "w") as file:
|
||||
json.dump(data, file, indent=4, sort_keys=True)
|
||||
await self.push_udpate()
|
||||
|
||||
async def push_udpate(self):
|
||||
self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda x: self.create_dict(x), self.data))))
|
||||
|
||||
def create_dict(self, data):
|
||||
return dict(name=data.get("name"), id=data.get("id"), type=data.get("type"), status=data.get("status"),props=data.get("props", []))
|
||||
self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
|
||||
|
||||
def find_by_id(self, id):
|
||||
return next((item for item in self.data if item["id"] == id), None)
|
||||
return next((item for item in self.data if item.id == id), None)
|
||||
|
||||
def get_index_by_id(self, id):
|
||||
return next((i for i, item in enumerate(self.data) if item["id"] == id), None)
|
||||
return next((i for i, item in enumerate(self.data) if item.id == id), None)
|
||||
|
||||
async def shutdown(self, app):
|
||||
logging.info("{} Shutdown ".format(self.name))
|
||||
tasks = []
|
||||
for item in self.data:
|
||||
if item.get("instance") is not None and item.get("instance").running is True:
|
||||
await item.get("instance").stop()
|
||||
tasks.append(item.get("instance").task)
|
||||
if item.instance is not None and item.instance.running is True:
|
||||
item.instance.task.cancel()
|
||||
tasks.append(item.instance.task)
|
||||
await asyncio.gather(*tasks)
|
||||
await self.save()
|
||||
|
||||
async def stop(self, id):
|
||||
logging.info("{} Stop Id {} ".format(self.name, id))
|
||||
|
||||
try:
|
||||
print("STOP NOW")
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
await instance.stop()
|
||||
print("STOP ACTION")
|
||||
await instance.task
|
||||
print("STOP ACTION", instance)
|
||||
await item.instance.stop()
|
||||
await self.push_udpate()
|
||||
except Exception as e:
|
||||
logging.error("{} Cant stop {} - {}".format(self.name, id, e))
|
||||
|
@ -85,18 +87,21 @@ class BasicController:
|
|||
logging.info("{} Start Id {} ".format(self.name, id))
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
if instance is not None and instance.running is True:
|
||||
if item.instance is not None and item.instance.state is True:
|
||||
logging.warning("{} already running {}".format(self.name, id))
|
||||
return
|
||||
if item.type is None:
|
||||
logging.warning("{} No Type {}".format(self.name, id))
|
||||
return
|
||||
clazz = self.types[item.type]["class"]
|
||||
item.instance = clazz(self.cbpi, item.id, item.props)
|
||||
|
||||
await item.instance.start()
|
||||
item.instance.task = self._loop.create_task(item.instance._run())
|
||||
|
||||
type = item["type"]
|
||||
clazz = self.types[type]["class"]
|
||||
item["instance"] = clazz(self.cbpi, item["id"], item["props"])
|
||||
await item["instance"].start()
|
||||
item["instance"].task = self._loop.create_task(item["instance"].run())
|
||||
logging.info("{} started {}".format(self.name, id))
|
||||
|
||||
await self.push_udpate()
|
||||
except Exception as e:
|
||||
logging.error("{} Cant start {} - {}".format(self.name, id, e))
|
||||
|
||||
|
@ -109,40 +114,37 @@ class BasicController:
|
|||
|
||||
def get_state(self):
|
||||
logging.info("{} Get State".format(self.name))
|
||||
return {"data": list(map(lambda x: self.create_dict(x), self.data)), "types":self.get_types()}
|
||||
return {"data": list(map(lambda x: x.to_dict(), self.data)), "types":self.get_types()}
|
||||
|
||||
async def add(self, data):
|
||||
async def add(self, item):
|
||||
logging.info("{} Add".format(self.name))
|
||||
id = shortuuid.uuid()
|
||||
item = {**data, "id": id, "instance": None , "name": data.get("name"), "props": data.get("props", {})}
|
||||
item.id = shortuuid.uuid()
|
||||
self.data.append(item)
|
||||
if self.autostart is True:
|
||||
await self.start(id)
|
||||
await self.save()
|
||||
return item
|
||||
|
||||
async def update(self, id, data) -> dict:
|
||||
async def update(self, item):
|
||||
logging.info("{} Get Update".format(self.name))
|
||||
await self.stop(id)
|
||||
self.data = list(map(lambda old: {**old, **data} if old["id"] == id else old, self.data))
|
||||
await self.stop(item.id)
|
||||
|
||||
self.data = list(map(lambda old_item: item if old_item.id == item.id else old_item, self.data))
|
||||
if self.autostart is True:
|
||||
await self.start(id)
|
||||
await self.start(item.id)
|
||||
await self.save()
|
||||
return self.find_by_id(id)
|
||||
return self.find_by_id(item.id)
|
||||
|
||||
async def delete(self, id) -> None:
|
||||
logging.info("{} Delete".format(self.name))
|
||||
await self.stop(id)
|
||||
self.data = list(filter(lambda x: x["id"] != id, self.data))
|
||||
self.data = list(filter(lambda x: x.id != id, self.data))
|
||||
await self.save()
|
||||
|
||||
async def call_action(self, id, action, parameter) -> None:
|
||||
|
||||
logging.info("{} call all Action {} {}".format(self.name, id, action))
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
|
||||
instance = item.get("instance")
|
||||
await instance.__getattribute__(action)(**parameter)
|
||||
await item.instance.__getattribute__(action)(**parameter)
|
||||
except Exception as e:
|
||||
logging.error("{} Faild to call action on {} {} {}".format(self.name, id, action, e))
|
|
@ -1,50 +1,58 @@
|
|||
from cbpi.api.dataclasses import Config
|
||||
import logging
|
||||
import os
|
||||
|
||||
from cbpi.api.config import ConfigType
|
||||
from cbpi.database.model import ConfigModel
|
||||
from cbpi.utils import load_config
|
||||
|
||||
import json
|
||||
|
||||
class ConfigController:
|
||||
'''
|
||||
The main actor controller
|
||||
'''
|
||||
model = ConfigModel
|
||||
|
||||
|
||||
def __init__(self, cbpi):
|
||||
self.cache = {}
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.cbpi = cbpi
|
||||
self.cbpi.register(self)
|
||||
|
||||
self.path = os.path.join(".", 'config', "config.json")
|
||||
|
||||
def get_state(self):
|
||||
return self.cache
|
||||
|
||||
result = {}
|
||||
for key, value in self.cache.items():
|
||||
result[key] = value.to_dict()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def init(self):
|
||||
this_directory = os.sep.join(os.path.abspath(__file__).split(os.sep)[:-2])
|
||||
|
||||
self.static = load_config("{}/config/config.yaml".format(this_directory))
|
||||
items = await self.model.get_all()
|
||||
for key, value in items.items():
|
||||
self.cache[value.name] = value
|
||||
with open(self.path) as json_file:
|
||||
data = json.load(json_file)
|
||||
for key, value in data.items():
|
||||
self.cache[key] = Config(name=value.get("name"), value=value.get("value"), description=value.get("description"), type=ConfigType(value.get("type", "string")), options=value.get("options", None) )
|
||||
|
||||
def get(self, name, default=None):
|
||||
self.logger.debug("GET CONFIG VALUE %s (default %s)" % (name, default))
|
||||
if name in self.cache and self.cache[name].value is not None:
|
||||
print("name", self.cache[name].value)
|
||||
return self.cache[name].value
|
||||
else:
|
||||
return default
|
||||
|
||||
async def set(self, name, value):
|
||||
self.logger.debug("SET %s = %s" % (name, value))
|
||||
if name in self.cache:
|
||||
|
||||
self.cache[name].value = value
|
||||
await self.model.update(**self.cache[name].__dict__)
|
||||
await self.cbpi.bus.fire(topic="config/%s/update" % name, name=name, value=value)
|
||||
|
||||
data = {}
|
||||
for key, value in self.cache.items():
|
||||
data[key] = value.to_dict()
|
||||
with open(self.path, "w") as file:
|
||||
json.dump(data, file, indent=4, sort_keys=True)
|
||||
|
||||
async def add(self, name, value, type: ConfigType, description, options=None):
|
||||
await self.model.insert(name=name, value=value, type=type.value, description=description, options=options)
|
||||
await self.cbpi.bus.fire(topic="config/%s/add" % name, name=name, value=value)
|
||||
self.cache[name] = Config(name,value,description,type,options)
|
||||
data = {}
|
||||
for key, value in self.cache.items():
|
||||
data[key] = value.to_dict()
|
||||
with open(self.path, "w") as file:
|
||||
json.dump(data, file, indent=4, sort_keys=True)
|
||||
|
|
|
@ -4,7 +4,7 @@ import os
|
|||
from os import listdir
|
||||
from os.path import isfile, join
|
||||
|
||||
class DashboardController():
|
||||
class DashboardController:
|
||||
|
||||
|
||||
def __init__(self, cbpi):
|
||||
|
|
|
@ -1,39 +1,26 @@
|
|||
from cbpi.controller.basic_controller import BasicController
|
||||
from cbpi.api.dataclasses import Kettle, Props
|
||||
from cbpi.controller.basic_controller2 import BasicController
|
||||
import logging
|
||||
from tabulate import tabulate
|
||||
class KettleController(BasicController):
|
||||
|
||||
def __init__(self, cbpi):
|
||||
super(KettleController, self).__init__(cbpi, "kettle.json")
|
||||
super(KettleController, self).__init__(cbpi, Kettle, "kettle.json")
|
||||
self.update_key = "kettleupdate"
|
||||
self.autostart = False
|
||||
|
||||
async def on(self, id):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
await instance.start()
|
||||
await self.push_udpate()
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on KettleLogic {} {}".format(id, e))
|
||||
|
||||
async def off(self, id):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
await instance.stop()
|
||||
await self.push_udpate()
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on KettleLogic {} {}".format(id, e))
|
||||
def create(self, data):
|
||||
return Kettle(data.get("id"), data.get("name"), type=data.get("type"), props=Props(data.get("props", {})), sensor=data.get("sensor"), heater=data.get("heater"), agitator=data.get("agitator"))
|
||||
|
||||
async def toggle(self, id):
|
||||
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
instance = item.get("instance")
|
||||
if instance is None or instance.running == False:
|
||||
|
||||
if item.instance is None or item.instance.state == False:
|
||||
await self.start(id)
|
||||
else:
|
||||
await instance.stop()
|
||||
await item.instance.stop()
|
||||
await self.push_udpate()
|
||||
except Exception as e:
|
||||
logging.error("Faild to switch on KettleLogic {} {}".format(id, e))
|
||||
|
@ -41,16 +28,8 @@ class KettleController(BasicController):
|
|||
async def set_target_temp(self, id, target_temp):
|
||||
try:
|
||||
item = self.find_by_id(id)
|
||||
item["target_temp"] = target_temp
|
||||
item.target_temp = target_temp
|
||||
await self.save()
|
||||
except Exception as e:
|
||||
logging.error("Faild to set Target Temp {} {}".format(id, e))
|
||||
|
||||
def create_dict(self, data):
|
||||
try:
|
||||
instance = data.get("instance")
|
||||
state = instance.get_state()
|
||||
except Exception as e:
|
||||
logging.error("Faild to create KettleLogic dict {} ".format(e))
|
||||
state = dict()
|
||||
return dict(name=data.get("name"), id=data.get("id"), type=data.get("type"), sensor=data.get("sensor"), heater=data.get("heater"), agitator=data.get("agitator"), target_temp=data.get("target_temp"), state=state,props=data.get("props", []))
|
|
@ -51,7 +51,7 @@ class LogController:
|
|||
# remove duplicates
|
||||
names = set(names)
|
||||
|
||||
print(names)
|
||||
|
||||
result = None
|
||||
|
||||
def dateparse(time_in_secs):
|
||||
|
@ -92,7 +92,7 @@ class LogController:
|
|||
else:
|
||||
data[name] = result.interpolate().tolist()
|
||||
|
||||
print(data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
from cbpi.api import *
|
||||
|
||||
|
||||
class NotificationController(object):
|
||||
'''
|
||||
This the notification controller
|
||||
'''
|
||||
|
||||
def __init__(self, cbpi):
|
||||
'''
|
||||
Initializer
|
||||
|
||||
:param cbpi: the cbpi server object
|
||||
'''
|
||||
self.cbpi = cbpi
|
||||
self.cbpi.register(self)
|
||||
|
||||
|
||||
async def notify(self, message, type=None):
|
||||
self.cbpi.ws.send(dict(topic="notifiaction", type=type, message=message))
|
||||
|
|
@ -41,7 +41,7 @@ class PluginController():
|
|||
"Plugin %s is not supporting version 4" % filename)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
logger.error(e)
|
||||
|
||||
def load_plugins_from_evn(self):
|
||||
|
|
74
cbpi/controller/satellite_controller.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
|
||||
|
||||
|
||||
|
||||
import asyncio
|
||||
|
||||
from asyncio_mqtt import Client, MqttError, Will
|
||||
from contextlib import AsyncExitStack, asynccontextmanager
|
||||
|
||||
class SatelliteController:
|
||||
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
self.client = None
|
||||
|
||||
async def init(self):
|
||||
asyncio.create_task(self.init_client(self.cbpi))
|
||||
|
||||
async def publish(self, topic, message):
|
||||
print("MQTT ON")
|
||||
await self.client.publish(topic, message, qos=1)
|
||||
|
||||
async def handle_message(self, messages):
|
||||
async for message in messages:
|
||||
print("FILTERED", message.payload.decode())
|
||||
|
||||
async def handle_unfilterd_message(self, messages):
|
||||
async for message in messages:
|
||||
print("UNFILTERED", message.payload.decode())
|
||||
|
||||
async def init_client(self, cbpi):
|
||||
async def log_messages(messages, template):
|
||||
|
||||
async for message in messages:
|
||||
print(template.format(message.payload.decode()))
|
||||
|
||||
async def cancel_tasks(tasks):
|
||||
for task in tasks:
|
||||
if task.done():
|
||||
continue
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
async with AsyncExitStack() as stack:
|
||||
|
||||
tasks = set()
|
||||
stack.push_async_callback(cancel_tasks, tasks)
|
||||
|
||||
self.client = Client("localhost", will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected"))
|
||||
await stack.enter_async_context(self.client)
|
||||
|
||||
topic_filters = (
|
||||
"cbpi/sensor/#",
|
||||
"cbpi/actor/#"
|
||||
)
|
||||
for topic_filter in topic_filters:
|
||||
# Log all messages that matches the filter
|
||||
manager = self.client.filtered_messages(topic_filter)
|
||||
messages = await stack.enter_async_context(manager)
|
||||
task = asyncio.create_task(self.handle_message(messages))
|
||||
tasks.add(task)
|
||||
|
||||
messages = await stack.enter_async_context(self.client.unfiltered_messages())
|
||||
task = asyncio.create_task(self.handle_unfilterd_message(messages))
|
||||
tasks.add(task)
|
||||
|
||||
await self.client.subscribe("cbpi/#")
|
||||
await asyncio.gather(*tasks)
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from cbpi.controller.basic_controller import BasicController
|
||||
from cbpi.api.dataclasses import Sensor
|
||||
from cbpi.controller.basic_controller2 import BasicController
|
||||
import logging
|
||||
|
||||
class SensorController(BasicController):
|
||||
def __init__(self, cbpi):
|
||||
super(SensorController, self).__init__(cbpi, "sensor.json")
|
||||
super(SensorController, self).__init__(cbpi, Sensor, "sensor.json")
|
||||
self.update_key = "sensorupdate"
|
||||
|
||||
def create_dict(self, data):
|
||||
|
@ -18,7 +19,7 @@ class SensorController(BasicController):
|
|||
|
||||
def get_sensor_value(self, id):
|
||||
try:
|
||||
return self.find_by_id(id).get("instance").get_state()
|
||||
return self.find_by_id(id).instance.get_state()
|
||||
except Exception as e:
|
||||
logging.error("Faild read sensor value {} {} ".format(id, e))
|
||||
return None
|
|
@ -1,21 +1,20 @@
|
|||
import asyncio
|
||||
|
||||
from tabulate import tabulate
|
||||
import json
|
||||
import copy
|
||||
import shortuuid
|
||||
import json
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from ..api.step import CBPiStep, Stop_Reason
|
||||
import shortuuid
|
||||
from cbpi.api.dataclasses import Props, Step
|
||||
from tabulate import tabulate
|
||||
|
||||
from ..api.step import StepMove, StepResult, StepState
|
||||
|
||||
|
||||
class StepController:
|
||||
|
||||
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()
|
||||
|
@ -29,6 +28,27 @@ class StepController:
|
|||
logging.info("INIT STEP Controller")
|
||||
self.load(startActive=True)
|
||||
|
||||
|
||||
def create(self, data):
|
||||
|
||||
id = data.get("id")
|
||||
name = data.get("name")
|
||||
type = data.get("type")
|
||||
status = StepState(data.get("status", "I"))
|
||||
props = data.get("props", {})
|
||||
|
||||
try:
|
||||
type_cfg = self.types.get(type)
|
||||
clazz = type_cfg.get("class")
|
||||
|
||||
instance = clazz(self.cbpi, id, name, Props(props), self.done)
|
||||
except Exception as e:
|
||||
logging.warning("Failed to create step instance %s - %s" % (id, e))
|
||||
instance = None
|
||||
|
||||
return Step(id, name, type=type, status=status, instance=instance, props=Props(props) )
|
||||
|
||||
|
||||
def load(self, startActive=False):
|
||||
|
||||
# create file if not exists
|
||||
|
@ -43,78 +63,86 @@ class StepController:
|
|||
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))
|
||||
self.profile = list(map(lambda item: self.create(item), 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")
|
||||
id = shortuuid.uuid()
|
||||
item = {**{"status": "I", "props": {}}, **data, "id": id, "instance": self.create_step(id, data.get("type"), data.get("name"), data.get("props", {}))}
|
||||
async def add(self, item: Step):
|
||||
logging.debug("Add step")
|
||||
item.id = shortuuid.uuid()
|
||||
item.status = StepState.INITIAL
|
||||
print(item)
|
||||
try:
|
||||
type_cfg = self.types.get(item.type)
|
||||
clazz = type_cfg.get("class")
|
||||
print("CLASS", clazz)
|
||||
item.instance = clazz(self.cbpi, item.id, item.name, item.props, self.done)
|
||||
except Exception as e:
|
||||
logging.warning("Failed to create step instance %s - %s " % (id, e))
|
||||
item.instance = None
|
||||
self.profile.append(item)
|
||||
await self.save()
|
||||
return item
|
||||
|
||||
async def update(self, id, data):
|
||||
logging.info("update step")
|
||||
def merge_data(id, old, data):
|
||||
step = {**old, **data}
|
||||
try:
|
||||
step["instance"] = self.create_step(id,data["type"], data["name"], data["props"])
|
||||
except Exception as e:
|
||||
logging.error("Faild create step instance during update props")
|
||||
return step
|
||||
async def update(self, item: Step):
|
||||
|
||||
self.profile = list(map(lambda old: {**merge_data(id, old, data)} if old["id"] == id else old, self.profile))
|
||||
logging.info("update step")
|
||||
try:
|
||||
type_cfg = self.types.get(item.type)
|
||||
clazz = type_cfg.get("class")
|
||||
|
||||
item.instance = clazz(self.cbpi, item.id, item.name, item.props, self.done)
|
||||
except Exception as e:
|
||||
logging.warning("Failed to create step instance %s - %s " % (item.id, e))
|
||||
item.instance = None
|
||||
|
||||
self.profile = list(map(lambda old: item if old.id == item.id else old, self.profile))
|
||||
await self.save()
|
||||
return self.find_by_id(id)
|
||||
return item
|
||||
|
||||
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)))
|
||||
data = dict(basic=self.basic_data, profile=list(map(lambda item: item.to_dict(), self.profile)))
|
||||
with open(self.path, "w") as file:
|
||||
json.dump(data, file, indent=4, sort_keys=True)
|
||||
self.push_udpate()
|
||||
|
||||
async def start(self):
|
||||
# already running
|
||||
if self.find_by_status("A") is not None:
|
||||
|
||||
if self.find_by_status(StepState.ACTIVE) is not None:
|
||||
logging.error("Steps already running")
|
||||
return
|
||||
# Find next inactive step
|
||||
step = self.find_by_status("P")
|
||||
if step is not None:
|
||||
|
||||
step = self.find_by_status(StepState.STOP)
|
||||
if step is not None:
|
||||
logging.info("Resume step")
|
||||
|
||||
await self.start_step(step)
|
||||
await self.save()
|
||||
return
|
||||
|
||||
step = self.find_by_status("I")
|
||||
step = self.find_by_status(StepState.INITIAL)
|
||||
if step is not None:
|
||||
logging.info("####### Start Step")
|
||||
|
||||
logging.info("Start Step")
|
||||
await self.start_step(step)
|
||||
await self.save()
|
||||
return
|
||||
await self.cbpi.notification.notify(message="HALLO")
|
||||
|
||||
self.cbpi.notify(message="BREWING COMPLETE")
|
||||
logging.info("BREWING COMPLETE")
|
||||
|
||||
async def next(self):
|
||||
logging.info("Trigger Next")
|
||||
step = self.find_by_status("A")
|
||||
step = self.find_by_status(StepState.ACTIVE)
|
||||
if step is not None:
|
||||
instance = step.get("instance")
|
||||
if instance is not None:
|
||||
await instance.next()
|
||||
step = self.find_by_status("P")
|
||||
if step.instance is not None:
|
||||
await step.instance.next()
|
||||
|
||||
step = self.find_by_status(StepState.STOP)
|
||||
if step is not None:
|
||||
instance = step.get("instance")
|
||||
if instance is not None:
|
||||
step["status"] = "D"
|
||||
if step.instance is not None:
|
||||
step.status = StepState.DONE
|
||||
await self.save()
|
||||
await self.start()
|
||||
else:
|
||||
|
@ -130,49 +158,41 @@ class StepController:
|
|||
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:
|
||||
step = self.find_by_status(StepState.ACTIVE)
|
||||
if step != None:
|
||||
logging.info("CALLING STOP STEP")
|
||||
instance = step.get("instance")
|
||||
await instance.stop()
|
||||
logging.info("STEP STOPPED")
|
||||
step["status"] = "P"
|
||||
try:
|
||||
await step.instance.stop()
|
||||
step.status = StepState.STOP
|
||||
await self.save()
|
||||
except Exception as e:
|
||||
logging.error("Failed to stop step - Id: %s" % step.id)
|
||||
|
||||
async def reset_all(self):
|
||||
step = self.find_by_status("A")
|
||||
if step is not None:
|
||||
if self.find_by_status(StepState.ACTIVE) 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()
|
||||
logging.info("Reset %s" % item)
|
||||
item.status = StepState.INITIAL
|
||||
try:
|
||||
await item.instance.reset()
|
||||
except:
|
||||
logging.warning("No Step Instance - Id: %s", item.id)
|
||||
await self.save()
|
||||
self.push_udpate()
|
||||
|
||||
def create_step(self, id, type, name, props):
|
||||
print(id, type, name, props)
|
||||
try:
|
||||
type_cfg = self.types.get(type)
|
||||
clazz = type_cfg.get("class")
|
||||
return clazz(self.cbpi, id, name, {**props})
|
||||
except:
|
||||
pass
|
||||
|
||||
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):
|
||||
def get_types(self):
|
||||
result = {}
|
||||
for key, value in self.types.items():
|
||||
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()}
|
||||
return {"basic": self.basic_data, "profile": list(map(lambda item: item.to_dict(), self.profile)), "types":self.get_types()}
|
||||
|
||||
async def move(self, id, direction):
|
||||
async def move(self, id, direction: StepMove):
|
||||
index = self.get_index_by_id(id)
|
||||
if direction not in [-1, 1]:
|
||||
self.logger.error("Cant move. Direction 1 and -1 allowed")
|
||||
|
@ -183,18 +203,22 @@ class StepController:
|
|||
|
||||
async def delete(self, id):
|
||||
step = self.find_by_id(id)
|
||||
if step.get("status") == "A":
|
||||
|
||||
if step is None:
|
||||
logging.error("Cant find step - Nothing deleted - Id: %s", id)
|
||||
return
|
||||
|
||||
if step.status == StepState.ACTIVE:
|
||||
logging.error("Cant delete active Step %s", id)
|
||||
return
|
||||
|
||||
self.profile = list(filter(lambda x: x["id"] != id, self.profile))
|
||||
self.profile = list(filter(lambda item: item.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")
|
||||
instance = p.instance
|
||||
# Stopping all running task
|
||||
if instance.task != None and instance.task.done() is False:
|
||||
logging.info("Stop Step")
|
||||
|
@ -202,54 +226,35 @@ class StepController:
|
|||
await instance.task
|
||||
await self.save()
|
||||
|
||||
def done(self, task):
|
||||
|
||||
id, reason = task.result()
|
||||
print("DONE", 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 == Stop_Reason.NEXT:
|
||||
step_current = self.find_by_status("A")
|
||||
if step_current is not None:
|
||||
step_current["status"] = "D"
|
||||
def done(self, step, result):
|
||||
if result == StepResult.NEXT:
|
||||
step_current = self.find_by_id(step.id)
|
||||
step_current.status = StepState.DONE
|
||||
async def wrapper():
|
||||
## TODO DONT CALL SAVE
|
||||
await self.save()
|
||||
await self.start()
|
||||
self._loop.create_task(wrapper())
|
||||
asyncio.create_task(wrapper())
|
||||
|
||||
|
||||
def find_by_status(self, status):
|
||||
return next((item for item in self.profile if item["status"] == status), None)
|
||||
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)
|
||||
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)
|
||||
return next((i for i, item in enumerate(self.profile) if item.id == id), None)
|
||||
|
||||
def push_udpate(self):
|
||||
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda x: self.create_dict(x), self.profile))))
|
||||
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda item: item.to_dict(), self.profile))))
|
||||
|
||||
async def start_step(self,step):
|
||||
logging.info("Start Step")
|
||||
try:
|
||||
await step["instance"].start()
|
||||
logging.info("Try to start step %s" % step)
|
||||
await step.instance.start()
|
||||
step.status = StepState.ACTIVE
|
||||
except Exception as e:
|
||||
print(".........",e)
|
||||
step["status"] = "A"
|
||||
print("STARTED",step)
|
||||
|
||||
async def update_props(self, id, props):
|
||||
logging.info("SAVE PROPS")
|
||||
step = self.find_by_id(id)
|
||||
step["props"] = props
|
||||
await self.save()
|
||||
self.push_udpate()
|
||||
logging.error("Faild to start step %s" % step)
|
||||
|
||||
async def save_basic(self, data):
|
||||
logging.info("SAVE Basic Data")
|
||||
|
|
|
@ -15,18 +15,7 @@ class SystemController:
|
|||
|
||||
|
||||
async def check_for_update(self, app):
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=1)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post('http://localhost:2202/check', json=dict(version=app["cbpi"].version)) as resp:
|
||||
if (resp.status == 200):
|
||||
data = await resp.json()
|
||||
if data.get("version") != self.cbpi.version:
|
||||
self.logger.info("Version Check: Newer Version exists")
|
||||
else:
|
||||
self.logger.info("Version Check: You are up to date")
|
||||
except:
|
||||
self.logger.warning("Version Check: Can't check for update")
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
import logging
|
||||
from os import urandom
|
||||
import os
|
||||
|
@ -15,7 +16,6 @@ from cbpi.controller.job_controller import JobController
|
|||
from cbpi.controller.actor_controller import ActorController
|
||||
from cbpi.controller.config_controller import ConfigController
|
||||
from cbpi.controller.kettle_controller import KettleController
|
||||
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
|
||||
|
@ -23,7 +23,7 @@ from cbpi.controller.step_controller import StepController
|
|||
from cbpi.controller.system_controller import SystemController
|
||||
|
||||
from cbpi.controller.log_file_controller import LogController
|
||||
from cbpi.database.model import DBModel
|
||||
|
||||
from cbpi.eventbus import CBPiEventBus
|
||||
from cbpi.http_endpoints.http_login import Login
|
||||
from cbpi.utils import *
|
||||
|
@ -73,7 +73,7 @@ class CraftBeerPi:
|
|||
self.version = __version__
|
||||
|
||||
self.static_config = load_config(os.path.join(".", 'config', "config.yaml"))
|
||||
print(self.path, self.static_config)
|
||||
|
||||
self.database_file = os.path.join(".", 'config', "craftbeerpi.db")
|
||||
logger.info("Init CraftBeerPI")
|
||||
|
||||
|
@ -96,8 +96,8 @@ class CraftBeerPi:
|
|||
self.log = LogController(self)
|
||||
self.system = SystemController(self)
|
||||
self.kettle = KettleController(self)
|
||||
self.step = StepController(self)
|
||||
|
||||
self.step : StepController = StepController(self)
|
||||
#self.satellite: SatelliteController = SatelliteController(self)
|
||||
self.dashboard = DashboardController(self)
|
||||
|
||||
self.http_step = StepHttpEndpoints(self)
|
||||
|
@ -108,7 +108,6 @@ class CraftBeerPi:
|
|||
self.http_dashboard = DashBoardHttpEndpoints(self)
|
||||
self.http_plugin = PluginHttpEndpoints(self)
|
||||
self.http_system = SystemHttpEndpoints(self)
|
||||
self.notification = NotificationController(self)
|
||||
self.http_log = LogHttpEndpoints(self)
|
||||
self.login = Login(self)
|
||||
|
||||
|
@ -205,7 +204,10 @@ class CraftBeerPi:
|
|||
api_version=self.version,
|
||||
contact="info@craftbeerpi.com")
|
||||
|
||||
def notify(self, key: str, message: str, type: str = "info") -> None:
|
||||
|
||||
|
||||
|
||||
def notify(self, message: str, type: str = "info") -> None:
|
||||
'''
|
||||
This is a convinience method to send notification to the client
|
||||
|
||||
|
@ -214,7 +216,7 @@ class CraftBeerPi:
|
|||
:param type: notification type (info,warning,danger,successs)
|
||||
:return:
|
||||
'''
|
||||
self.bus.sync_fire(topic="notification/%s" % key, key=key, message=message, type=type)
|
||||
self.ws.send(dict(topic="notifiaction", type=type, message=message))
|
||||
|
||||
async def call_initializer(self, app):
|
||||
self.initializer = sorted(self.initializer, key=lambda k: k['order'])
|
||||
|
@ -247,17 +249,19 @@ class CraftBeerPi:
|
|||
self._print_logo()
|
||||
|
||||
await self.job.init()
|
||||
await DBModel.setup()
|
||||
|
||||
await self.config.init()
|
||||
self._setup_http_index()
|
||||
self.plugin.load_plugins()
|
||||
self.plugin.load_plugins_from_evn()
|
||||
await self.sensor.init()
|
||||
await self.step.init()
|
||||
|
||||
await self.actor.init()
|
||||
await self.kettle.init()
|
||||
await self.call_initializer(self.app)
|
||||
await self.dashboard.init()
|
||||
#await self.satellite.init()
|
||||
self._swagger_setup()
|
||||
|
||||
return self.app
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
import json
|
||||
|
||||
import aiosqlite
|
||||
|
||||
|
||||
from cbpi.database.orm_framework import DBModel
|
||||
|
||||
DATABASE_FILE = "./craftbeerpi.db"
|
||||
|
||||
|
||||
class ActorModel(DBModel):
|
||||
__fields__ = ["name", "type", "config"]
|
||||
__table_name__ = "actor"
|
||||
__json_fields__ = ["config"]
|
||||
|
||||
__validation_schema__ = {
|
||||
'id': int,
|
||||
'name': str,
|
||||
'type': str,
|
||||
'config': dict
|
||||
}
|
||||
|
||||
def to_json(self):
|
||||
data = dict(**self.__dict__)
|
||||
if hasattr(self,"instance"):
|
||||
data["state"] = self.instance.get_state()
|
||||
del data["instance"]
|
||||
return data
|
||||
|
||||
|
||||
class SensorModel(DBModel):
|
||||
__fields__ = ["name", "type", "config"]
|
||||
__table_name__ = "sensor"
|
||||
__json_fields__ = ["config"]
|
||||
__validation_schema__ = {
|
||||
'id': int,
|
||||
'name': str,
|
||||
'type': str,
|
||||
'config': dict
|
||||
}
|
||||
|
||||
def to_json(self):
|
||||
data = dict(**self.__dict__)
|
||||
if hasattr(self,"instance"):
|
||||
data["value"] = self.instance.get_value()
|
||||
data["unit"] = self.instance.get_unit()
|
||||
data["state"] = self.instance.get_state()
|
||||
del data["instance"]
|
||||
return data
|
||||
|
||||
|
||||
class ConfigModel(DBModel):
|
||||
__fields__ = ["type", "value", "description", "options"]
|
||||
__table_name__ = "config"
|
||||
__json_fields__ = ["options"]
|
||||
__priamry_key__ = "name"
|
||||
__order_by__ = "name"
|
||||
|
||||
|
||||
class KettleModel(DBModel):
|
||||
__fields__ = ["name", "sensor", "heater", "automatic", "logic", "config", "agitator", "target_temp"]
|
||||
__table_name__ = "kettle"
|
||||
__json_fields__ = ["config"]
|
||||
|
||||
|
||||
class StepModel(DBModel):
|
||||
__fields__ = ["order", "name", "type", "stepstate", "state", "start", "end", "config", "kettleid"]
|
||||
__table_name__ = "step"
|
||||
__json_fields__ = ["config", "stepstate"]
|
||||
|
||||
@classmethod
|
||||
async def update_step_state(cls, step_id, state):
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
cursor = await db.execute("UPDATE %s SET stepstate = ? WHERE id = ?" % cls.__table_name__, (json.dumps(state), step_id))
|
||||
await db.commit()
|
||||
|
||||
@classmethod
|
||||
async def update_state(cls, step_id, state, end=None):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
if end is not None:
|
||||
await db.execute("UPDATE %s SET state = ?, end = ? WHERE id = ?" % cls.__table_name__, (state, end, step_id))
|
||||
else:
|
||||
await db.execute("UPDATE %s SET state = ? WHERE id = ?" % cls.__table_name__, (state, step_id))
|
||||
await db.commit()
|
||||
|
||||
@classmethod
|
||||
async def get_by_state(cls, state, order=True):
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
db.row_factory = DBModel.dict_factory
|
||||
async with db.execute("SELECT * FROM %s WHERE state = ? ORDER BY %s.'order'" % (cls.__table_name__, cls.__table_name__,), state) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row is not None:
|
||||
return cls(row)
|
||||
else:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def reset_all_steps(cls):
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
await db.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__)
|
||||
await db.commit()
|
||||
|
||||
@classmethod
|
||||
async def sort(cls, new_order):
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
for key, value in new_order.items():
|
||||
|
||||
await db.execute("UPDATE %s SET '%s' = ? WHERE id = ?" % (cls.__table_name__, "order"), (value, key))
|
||||
await db.commit()
|
||||
|
||||
@classmethod
|
||||
async def get_max_order(cls):
|
||||
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
db.row_factory = DBModel.dict_factory
|
||||
async with db.execute("SELECT max(step.'order') as 'order' FROM %s" % cls.__table_name__) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row is not None:
|
||||
return row.get("order")
|
||||
else:
|
||||
return 0
|
||||
|
||||
def to_json(self):
|
||||
data = dict(**self.__dict__)
|
||||
if hasattr(self,"instance"):
|
||||
data["state_msg"] = self.instance.get_status()
|
||||
del data["instance"]
|
||||
return data
|
||||
|
||||
def __str__(self):
|
||||
return "%s, %s, %s, %s, %s" % (self.name, self.start, self.end, self.state, self.order)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "Steps(%s, %s, %s, %s, %s)" % (self.name, self.start, self.end, self.state, self.order)
|
||||
|
||||
|
||||
class DashboardModel(DBModel):
|
||||
__fields__ = ["name"]
|
||||
__table_name__ = "dashboard"
|
||||
__json_fields__ = []
|
||||
|
||||
|
||||
class DashboardContentModel(DBModel):
|
||||
__fields__ = ["dbid", "type", "element_id", "x", "y","config"]
|
||||
__table_name__ = "dashboard_content"
|
||||
__json_fields__ = ["config"]
|
||||
|
||||
__validation_schema__ = {
|
||||
'dbid': int,
|
||||
'element_id': int,
|
||||
'type': str,
|
||||
'x': int,
|
||||
'y': int,
|
||||
'config': dict
|
||||
}
|
||||
|
||||
@classmethod
|
||||
async def get_by_dashboard_id(cls, id, as_array=False):
|
||||
|
||||
result = []
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
db.row_factory = DBModel.dict_factory
|
||||
async with db.execute("SELECT * FROM %s WHERE dbid = ?" % (cls.__table_name__), (id,)) as cursor:
|
||||
async for row in cursor:
|
||||
result.append(cls(row))
|
||||
await cursor.close()
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def update_coordinates(cls, id, x, y):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
await db.execute("UPDATE %s SET x = ?, y = ? WHERE id = ?" % (cls.__table_name__), (x, y, id,))
|
||||
await db.commit()
|
||||
|
||||
|
||||
@classmethod
|
||||
async def delete_by_dashboard_id(cls, id):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
await db.execute("DELETE FROM %s WHERE dbid = ?" % (cls.__table_name__), (id,))
|
||||
await db.commit()
|
||||
|
||||
|
||||
class TranslationModel(DBModel):
|
||||
__fields__ = ["key", "text", "language_code"]
|
||||
__table_name__ = "translation"
|
||||
__json_fields__ = []
|
||||
__priamry_key__ = "key"
|
||||
|
||||
@classmethod
|
||||
async def get_all(cls):
|
||||
|
||||
result = {}
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
sql = "SELECT * FROM %s" % cls.__table_name__
|
||||
db.row_factory = DBModel.dict_factory
|
||||
async with db.execute(sql) as cursor:
|
||||
async for row in cursor:
|
||||
code = row.get("language_code")
|
||||
key = row.get("key")
|
||||
text = row.get("text")
|
||||
if code not in result:
|
||||
result[code] = {}
|
||||
result[code][key] = text
|
||||
await cursor.close()
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def add_key(cls, locale, key):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
await db.execute("INSERT INTO %s (language_code, key, text) VALUES (?,?, ' ')" % (cls.__table_name__), (locale, key))
|
||||
await db.commit()
|
|
@ -1,174 +0,0 @@
|
|||
import json
|
||||
import aiosqlite
|
||||
import os
|
||||
|
||||
from cbpi.api import *
|
||||
from voluptuous import MultipleInvalid, Schema
|
||||
|
||||
DATABASE_FILE = "./craftbeerpi.db"
|
||||
|
||||
|
||||
class DBModel(object):
|
||||
__priamry_key__ = "id"
|
||||
__as_array__ = False
|
||||
__order_by__ = None
|
||||
__json_fields__ = []
|
||||
__validation_schema__ = None
|
||||
|
||||
def __init__(self, args):
|
||||
self.__setattr__(self.__priamry_key__, args[self.__priamry_key__])
|
||||
for f in self.__fields__:
|
||||
|
||||
if f in self.__json_fields__:
|
||||
if args.get(f) is not None:
|
||||
|
||||
if isinstance(args[f], dict) or isinstance(args[f], list):
|
||||
self.__setattr__(f, args.get(f))
|
||||
else:
|
||||
self.__setattr__(f, json.loads(args.get(f, "{}")))
|
||||
else:
|
||||
self.__setattr__(f, None)
|
||||
else:
|
||||
|
||||
self.__setattr__(f, args.get(f))
|
||||
|
||||
@classmethod
|
||||
async def setup(self):
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
assert isinstance(db, aiosqlite.Connection)
|
||||
this_directory = os.path.dirname(__file__)
|
||||
qry = open(os.path.join(this_directory, "../config/create_database.sql"), 'r').read()
|
||||
cursor = await db.executescript(qry)
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data):
|
||||
if cls.__validation_schema__ is not None:
|
||||
try:
|
||||
schema = Schema(cls.__validation_schema__)
|
||||
schema(data)
|
||||
except MultipleInvalid as e:
|
||||
raise CBPiException(str(e))
|
||||
|
||||
@classmethod
|
||||
async def get_all(cls):
|
||||
|
||||
if cls.__as_array__ is True:
|
||||
result = []
|
||||
else:
|
||||
result = {}
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
|
||||
if cls.__order_by__ is not None:
|
||||
sql = "SELECT * FROM %s ORDER BY %s.'%s'" % (cls.__table_name__, cls.__table_name__, cls.__order_by__)
|
||||
else:
|
||||
sql = "SELECT * FROM %s" % cls.__table_name__
|
||||
|
||||
db.row_factory = DBModel.dict_factory
|
||||
async with db.execute(sql) as cursor:
|
||||
async for row in cursor:
|
||||
|
||||
if cls.__as_array__ is True:
|
||||
result.append(cls(row))
|
||||
else:
|
||||
|
||||
result[row.get(cls.__priamry_key__)] = cls(row)
|
||||
await cursor.close()
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def get_one(cls, id):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
db.row_factory = DBModel.dict_factory
|
||||
async with db.execute("SELECT * FROM %s WHERE %s = ?" % (cls.__table_name__, cls.__priamry_key__), (id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row is not None:
|
||||
return cls(row)
|
||||
else:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def delete(cls, id):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
await db.execute("DELETE FROM %s WHERE %s = ? " % (cls.__table_name__, cls.__priamry_key__), (id,))
|
||||
await db.commit()
|
||||
|
||||
@classmethod
|
||||
async def delete_all(cls):
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
await db.execute("DELETE FROM %s" % cls.__table_name__)
|
||||
await db.commit()
|
||||
|
||||
@classmethod
|
||||
async def insert(cls, **kwargs):
|
||||
|
||||
|
||||
cls.validate(kwargs)
|
||||
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
if cls.__priamry_key__ is not None and cls.__priamry_key__ in kwargs:
|
||||
query = "INSERT INTO %s (%s, %s) VALUES (?, %s)" % (
|
||||
cls.__table_name__,
|
||||
cls.__priamry_key__,
|
||||
', '.join("'%s'" % str(x) for x in cls.__fields__),
|
||||
', '.join(['?'] * len(cls.__fields__)))
|
||||
data = ()
|
||||
data = data + (kwargs.get(cls.__priamry_key__),)
|
||||
for f in cls.__fields__:
|
||||
if f in cls.__json_fields__:
|
||||
data = data + (json.dumps(kwargs.get(f)),)
|
||||
else:
|
||||
data = data + (kwargs.get(f),)
|
||||
else:
|
||||
|
||||
query = 'INSERT INTO %s (%s) VALUES (%s)' % (
|
||||
cls.__table_name__,
|
||||
', '.join("'%s'" % str(x) for x in cls.__fields__),
|
||||
', '.join(['?'] * len(cls.__fields__)))
|
||||
|
||||
data = ()
|
||||
for f in cls.__fields__:
|
||||
if f in cls.__json_fields__:
|
||||
data = data + (json.dumps(kwargs.get(f)),)
|
||||
else:
|
||||
data = data + (kwargs.get(f),)
|
||||
|
||||
|
||||
cursor = await db.execute(query, data)
|
||||
await db.commit()
|
||||
|
||||
i = cursor.lastrowid
|
||||
kwargs["id"] = i
|
||||
|
||||
return cls(kwargs)
|
||||
|
||||
@classmethod
|
||||
async def update(cls, **kwargs):
|
||||
print("UPDATE")
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
query = 'UPDATE %s SET %s WHERE %s = ?' % (cls.__table_name__, ', '.join("'%s' = ?" % str(x) for x in cls.__fields__), cls.__priamry_key__)
|
||||
data = ()
|
||||
for f in cls.__fields__:
|
||||
if f in cls.__json_fields__:
|
||||
data = data + (json.dumps(kwargs.get(f)),)
|
||||
else:
|
||||
data = data + (kwargs.get(f),)
|
||||
|
||||
data = data + (kwargs.get(cls.__priamry_key__),)
|
||||
print(query)
|
||||
cursor = await db.execute(query, data)
|
||||
await db.commit()
|
||||
return cls(kwargs)
|
||||
|
||||
@classmethod
|
||||
def dict_factory(cls, cursor, row):
|
||||
d = {}
|
||||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
|
||||
def to_json(self):
|
||||
|
||||
return self.__dict__
|
|
@ -1,51 +0,0 @@
|
|||
import os
|
||||
from cbpi.api import *
|
||||
from cbpi.controller.crud_controller import CRUDController
|
||||
from cbpi.database.orm_framework import DBModel
|
||||
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
|
||||
|
||||
|
||||
class DummyModel(DBModel):
|
||||
'''
|
||||
Cumstom Data Model which will is stored in the database
|
||||
'''
|
||||
__fields__ = ["name"]
|
||||
__table_name__ = "dummy"
|
||||
|
||||
|
||||
class MyComp(CBPiExtension, CRUDController, HttpCrudEndpoints):
|
||||
model = DummyModel
|
||||
|
||||
def __init__(self, cbpi):
|
||||
'''
|
||||
Initializer
|
||||
:param cbpi:
|
||||
'''
|
||||
self.cbpi = cbpi
|
||||
# register component for http, events
|
||||
# In addtion the sub folder static is exposed to access static content via http
|
||||
self.cbpi.register(self, "/dummy", static=os.path.join(os.path.dirname(__file__), "static"))
|
||||
|
||||
|
||||
@on_event(topic="actor/#")
|
||||
async def listen(self, **kwargs):
|
||||
# Listen for all actor events
|
||||
pass
|
||||
|
||||
@on_event(topic="kettle/+/automatic")
|
||||
async def listen2(self, **kwargs):
|
||||
|
||||
# listen for all kettle events which are switching the automatic logic
|
||||
pass
|
||||
|
||||
|
||||
def setup(cbpi):
|
||||
'''
|
||||
Setup method is invoked during startup
|
||||
|
||||
:param cbpi: the cbpi core object
|
||||
:return:
|
||||
'''
|
||||
# regsiter the component to the core
|
||||
cbpi.plugin.register("MyComp", MyComp)
|
||||
pass
|
|
@ -1,2 +0,0 @@
|
|||
name: DummyComponent
|
||||
version: 4
|
|
@ -1 +0,0 @@
|
|||
HALLO WELT INDEX
|
|
@ -1 +0,0 @@
|
|||
HALLO WELT INDEX2
|
|
@ -1,120 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import asyncio
|
||||
import threading
|
||||
import time
|
||||
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
|
||||
import re
|
||||
import random
|
||||
|
||||
|
||||
def getSensors():
|
||||
try:
|
||||
arr = []
|
||||
for dirname in os.listdir('/sys/bus/w1/devices'):
|
||||
if (dirname.startswith("28") or dirname.startswith("10")):
|
||||
cbpi.app.logger.info("Device %s Found (Family: 28/10, Thermometer on GPIO4 (w1))" % dirname)
|
||||
arr.append(dirname)
|
||||
return arr
|
||||
except:
|
||||
return []
|
||||
|
||||
|
||||
class myThread (threading.Thread):
|
||||
|
||||
value = 0
|
||||
|
||||
|
||||
def __init__(self, sensor_name):
|
||||
threading.Thread.__init__(self)
|
||||
self.value = 0
|
||||
self.sensor_name = sensor_name
|
||||
self.runnig = True
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.runnig = False
|
||||
|
||||
def run(self):
|
||||
|
||||
while self.runnig:
|
||||
|
||||
try:
|
||||
app.logger.info("READ TEMP")
|
||||
## Test Mode
|
||||
if self.sensor_name is None:
|
||||
return
|
||||
with open('/sys/bus/w1/devices/w1_bus_master1/%s/w1_slave' % self.sensor_name, 'r') as content_file:
|
||||
content = content_file.read()
|
||||
if (content.split('\n')[0].split(' ')[11] == "YES"):
|
||||
temp = float(content.split("=")[-1]) / 1000 # temp in Celcius
|
||||
self.value = temp
|
||||
except:
|
||||
pass
|
||||
|
||||
self.value = random.randint(1,100)
|
||||
time.sleep(4)
|
||||
|
||||
class DS18B20(CBPiSensor):
|
||||
|
||||
|
||||
sensor_name = Property.Select("Sensor", getSensors(), description="The OneWire sensor address.")
|
||||
offset = Property.Number("Offset", True, 0, description="Offset which is added to the received sensor data. Positive and negative values are both allowed.")
|
||||
interval = Property.Number(label="interval", configurable=True)
|
||||
|
||||
# Internal runtime variable
|
||||
value = 0
|
||||
|
||||
def init(self):
|
||||
super().init()
|
||||
self.state = True
|
||||
self.t = myThread(self.sensor_name)
|
||||
def shudown():
|
||||
shudown.cb.shutdown()
|
||||
|
||||
shudown.cb = self.t
|
||||
|
||||
self.t.start()
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def get_value(self):
|
||||
|
||||
return self.value
|
||||
|
||||
def get_unit(self):
|
||||
return "°%s" % self.get_parameter("TEMP_UNIT", "C")
|
||||
|
||||
def stop(self):
|
||||
try:
|
||||
self.t.stop()
|
||||
except:
|
||||
pass
|
||||
|
||||
async def run(self, cbpi):
|
||||
self.value = 0
|
||||
while True:
|
||||
await asyncio.sleep(self.interval)
|
||||
self.value = random.randint(1,101)
|
||||
self.log_data(self.value)
|
||||
await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value)
|
||||
|
||||
|
||||
|
||||
|
||||
def setup(cbpi):
|
||||
|
||||
'''
|
||||
This method is called by the server during startup
|
||||
Here you need to register your plugins at the server
|
||||
|
||||
:param cbpi: the cbpi core
|
||||
:return:
|
||||
'''
|
||||
|
||||
cbpi.plugin.register("DS18B20", DS18B20)
|
|
@ -1,3 +0,0 @@
|
|||
name: DummySensor
|
||||
version: 4
|
||||
active: true
|
|
@ -12,7 +12,7 @@ class DummyActor(CBPiActor):
|
|||
# Custom property which can be configured by the user
|
||||
@action("test", parameters={})
|
||||
async def action1(self, **kwargs):
|
||||
print("ACTION !", kwargs)
|
||||
|
||||
self.my_name = kwargs.get("name")
|
||||
pass
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import asyncio
|
||||
import logging
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
@ -26,7 +27,6 @@ if (mode == None):
|
|||
@parameters([Property.Select(label="GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]), Property.Select(label="Inverted", options=["Yes", "No"],description="No: Active on high; Yes: Active on low")])
|
||||
class GPIOActor(CBPiActor):
|
||||
|
||||
|
||||
def get_GPIO_state(self, state):
|
||||
# ON
|
||||
if state == 1:
|
||||
|
@ -35,14 +35,12 @@ class GPIOActor(CBPiActor):
|
|||
if state == 0:
|
||||
return 0 if self.inverted == False else 1
|
||||
|
||||
async def start(self):
|
||||
await super().start()
|
||||
self.gpio = self.props.get("GPIO")
|
||||
async def on_start(self):
|
||||
self.gpio = self.props.GPIO
|
||||
self.inverted = True if self.props.get("Inverted", "No") == "Yes" else False
|
||||
GPIO.setup(self.gpio, GPIO.OUT)
|
||||
GPIO.output(self.gpio, self.get_GPIO_state(0))
|
||||
self.state = False
|
||||
pass
|
||||
|
||||
async def on(self, power=0):
|
||||
logger.info("ACTOR %s ON - GPIO %s " % (self.id, self.gpio))
|
||||
|
@ -55,11 +53,12 @@ class GPIOActor(CBPiActor):
|
|||
self.state = False
|
||||
|
||||
def get_state(self):
|
||||
|
||||
return self.state
|
||||
|
||||
async def run(self):
|
||||
pass
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
@parameters([Property.Select(label="GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]), Property.Number("Frequency", configurable=True)])
|
||||
class GPIOPWMActor(CBPiActor):
|
||||
|
@ -69,7 +68,6 @@ class GPIOPWMActor(CBPiActor):
|
|||
async def power(self, **kwargs):
|
||||
self.p.ChangeDutyCycle(1)
|
||||
|
||||
|
||||
async def start(self):
|
||||
await super().start()
|
||||
self.gpio = self.props.get("GPIO")
|
||||
|
@ -97,7 +95,9 @@ class GPIOPWMActor(CBPiActor):
|
|||
return self.state
|
||||
|
||||
async def run(self):
|
||||
pass
|
||||
while True:
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def setup(cbpi):
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class HTTPSensor(CBPiSensor):
|
|||
self.log_data(value)
|
||||
await cbpi.bus.fire("sensor/%s/data" % self.id, value=value)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
pass
|
||||
|
||||
class HTTPSensorEndpoint(CBPiExtension):
|
||||
|
@ -96,7 +96,7 @@ class HTTPSensorEndpoint(CBPiExtension):
|
|||
if self.pattern_check.match(value) is None:
|
||||
return web.json_response(status=422, data={'error': "Data not matching pattern ^[a-zA-Z0-9,.]{0,10}$"})
|
||||
|
||||
print("HTTP SENSOR ", key, value)
|
||||
|
||||
cache[key] = value
|
||||
|
||||
return web.Response(status=204)
|
||||
|
|
|
@ -7,25 +7,24 @@ from cbpi.api import *
|
|||
Property.Number(label="OffsetOff", configurable=True, description="Offset below target temp when heater should switched off")])
|
||||
class Hysteresis(CBPiKettleLogic):
|
||||
|
||||
|
||||
async def stop(self):
|
||||
self.task.cancel()
|
||||
await self.task
|
||||
|
||||
async def run(self):
|
||||
try:
|
||||
self.offset_on = float(self.props.get("OffsetOn", 0))
|
||||
self.offset_off = float(self.props.get("OffsetOff", 0))
|
||||
self.kettle = self.get_kettle(self.id)
|
||||
self.heater = self.kettle.get("heater")
|
||||
self.heater = self.kettle.heater
|
||||
logging.info("CustomLogic {} {} {} {}".format(self.offset_on, self.offset_off, self.id, self.heater))
|
||||
while True:
|
||||
sensor_value = self.get_sensor_value(self.kettle.get("sensor")).get("value")
|
||||
target_temp = self.get_kettle_target_temp("oHxKz3z5RjbsxfSz6KUgov")
|
||||
await self.actor_on(self.heater)
|
||||
'''
|
||||
sensor_value = self.get_sensor_value(self.kettle.sensor).get("value")
|
||||
target_temp = self.get_kettle_target_temp(self.id)
|
||||
|
||||
if sensor_value < target_temp - self.offset_on:
|
||||
await self.actor_on(self.heater)
|
||||
elif sensor_value >= target_temp - self.offset_off:
|
||||
await self.actor_off(self.heater)
|
||||
'''
|
||||
await asyncio.sleep(1)
|
||||
|
||||
except asyncio.CancelledError as e:
|
||||
|
|
|
@ -1,167 +1,108 @@
|
|||
|
||||
import asyncio
|
||||
from cbpi.api.step import CBPiStep, StepResult
|
||||
from cbpi.api.timer import Timer
|
||||
|
||||
from cbpi.api import *
|
||||
import logging
|
||||
|
||||
|
||||
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
|
||||
Property.Number(label="Temp", configurable=True),
|
||||
Property.Sensor(label="Sensor"),
|
||||
Property.Kettle(label="Kettle")])
|
||||
class MashStep(CBPiStep):
|
||||
|
||||
def __init__(self, cbpi, id, name, props):
|
||||
super().__init__(cbpi, id, name, props)
|
||||
self.timer = None
|
||||
async def on_timer_done(self,timer):
|
||||
self.summary = ""
|
||||
await self.next()
|
||||
|
||||
def timer_done(self):
|
||||
self.state_msg = "Done"
|
||||
asyncio.create_task(self.next())
|
||||
async def on_timer_update(self,timer, seconds):
|
||||
self.summary = Timer.format_time(seconds)
|
||||
await self.push_update()
|
||||
|
||||
async def timer_update(self, seconds, time):
|
||||
self.state_msg = "{}".format(time)
|
||||
self.push_update()
|
||||
|
||||
def start_timer(self):
|
||||
async def on_start(self):
|
||||
if self.timer is None:
|
||||
self.time = int(self.props.get("Timer", 0)) * 60
|
||||
self.timer = Timer(self.time, self.timer_done, self.timer_update)
|
||||
self.timer.start()
|
||||
self.timer = Timer(10,on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
|
||||
async def stop_timer(self):
|
||||
if self.timer is not None:
|
||||
self.summary = "Waiting for Target Temp"
|
||||
await self.push_update()
|
||||
|
||||
async def on_stop(self):
|
||||
await self.timer.stop()
|
||||
self.state_msg = "{}".format(self.timer.get_time())
|
||||
|
||||
async def next(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = ""
|
||||
await super().next()
|
||||
|
||||
async def stop(self):
|
||||
await super().stop()
|
||||
await self.stop_timer()
|
||||
self.summary = ""
|
||||
await self.push_update()
|
||||
|
||||
async def reset(self):
|
||||
self.state_msg = ""
|
||||
self.timer = None
|
||||
await super().reset()
|
||||
|
||||
async def execute(self):
|
||||
if self.timer is None:
|
||||
self.state_msg = "Waiting for Target Temp"
|
||||
self.push_update()
|
||||
else:
|
||||
if self.timer is not None and self.timer.is_running() is False:
|
||||
self.start_timer()
|
||||
sensor_value = 0
|
||||
self.timer = Timer(10,on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
|
||||
async def run(self):
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
sensor_value = self.get_sensor_value(self.props.get("Sensor"))
|
||||
if sensor_value.get("value") >= 2 and self.timer == None:
|
||||
sensor_value = self.get_sensor_value(self.props.Sensor)
|
||||
if sensor_value.get("value") >= int(self.props.Temp) and self.timer == None:
|
||||
self.start_timer()
|
||||
return StepResult.DONE
|
||||
|
||||
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True)])
|
||||
class WaitStep(CBPiStep):
|
||||
|
||||
def __init__(self, cbpi, id, name, props):
|
||||
super().__init__(cbpi, id, name, props)
|
||||
self.timer = None
|
||||
async def on_timer_done(self,timer):
|
||||
self.summary = ""
|
||||
await self.next()
|
||||
|
||||
def timer_done(self):
|
||||
self.state_msg = "Done"
|
||||
async def on_timer_update(self,timer, seconds):
|
||||
self.summary = Timer.format_time(seconds)
|
||||
await self.push_update()
|
||||
|
||||
asyncio.create_task(self.next())
|
||||
|
||||
async def timer_update(self, seconds, time):
|
||||
self.state_msg = "{}".format(time)
|
||||
self.push_update()
|
||||
|
||||
def start_timer(self):
|
||||
async def on_start(self):
|
||||
if self.timer is None:
|
||||
self.time = int(self.props.get("Timer", 0)) * 60
|
||||
self.timer = Timer(self.time, self.timer_done, self.timer_update)
|
||||
self.timer = Timer(int(self.props.Timer),on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
self.timer.start()
|
||||
|
||||
async def stop_timer(self):
|
||||
if self.timer is not None:
|
||||
async def on_stop(self):
|
||||
await self.timer.stop()
|
||||
self.state_msg = "{}".format(self.timer.get_time())
|
||||
|
||||
async def next(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = ""
|
||||
await super().next()
|
||||
|
||||
async def stop(self):
|
||||
await super().stop()
|
||||
await self.stop_timer()
|
||||
self.summary = ""
|
||||
await self.push_update()
|
||||
|
||||
async def reset(self):
|
||||
self.state_msg = ""
|
||||
self.timer = None
|
||||
await super().reset()
|
||||
self.timer = Timer(int(self.props.Timer),on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
|
||||
async def execute(self):
|
||||
self.start_timer()
|
||||
async def run(self):
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
return StepResult.DONE
|
||||
|
||||
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
|
||||
Property.Actor(label="Actor")])
|
||||
class ActorStep(CBPiStep):
|
||||
async def on_timer_done(self,timer):
|
||||
self.summary = ""
|
||||
await self.next()
|
||||
|
||||
def __init__(self, cbpi, id, name, props):
|
||||
super().__init__(cbpi, id, name, props)
|
||||
self.timer = None
|
||||
async def on_timer_update(self,timer, seconds):
|
||||
self.summary = Timer.format_time(seconds)
|
||||
await self.push_update()
|
||||
|
||||
def timer_done(self):
|
||||
self.state_msg = "Done"
|
||||
asyncio.create_task(self.actor_off(self.actor_id))
|
||||
asyncio.create_task(self.next())
|
||||
|
||||
async def timer_update(self, seconds, time):
|
||||
self.state_msg = "{}".format(time)
|
||||
self.push_update()
|
||||
|
||||
def start_timer(self):
|
||||
async def on_start(self):
|
||||
if self.timer is None:
|
||||
self.time = int(self.props.get("Timer", 0)) * 60
|
||||
self.timer = Timer(self.time, self.timer_done, self.timer_update)
|
||||
self.timer = Timer(int(self.props.Timer),on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
self.timer.start()
|
||||
await self.actor_on(self.props.Actor)
|
||||
|
||||
async def stop_timer(self):
|
||||
if self.timer is not None:
|
||||
async def on_stop(self):
|
||||
await self.actor_off(self.props.Actor)
|
||||
await self.timer.stop()
|
||||
self.state_msg = "{}".format(self.timer.get_time())
|
||||
|
||||
async def next(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = ""
|
||||
await super().next()
|
||||
|
||||
async def stop(self):
|
||||
await super().stop()
|
||||
await self.actor_off(self.actor_id)
|
||||
await self.stop_timer()
|
||||
self.summary = ""
|
||||
await self.push_update()
|
||||
|
||||
async def reset(self):
|
||||
self.state_msg = ""
|
||||
self.timer = None
|
||||
await super().reset()
|
||||
self.timer = Timer(int(self.props.Timer),on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||
|
||||
async def run(self):
|
||||
|
||||
async def execute(self):
|
||||
self.start_timer()
|
||||
self.actor_id = self.props.get("Actor")
|
||||
await self.actor_on(self.actor_id)
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
return StepResult.DONE
|
||||
|
||||
def setup(cbpi):
|
||||
'''
|
||||
|
@ -172,7 +113,11 @@ def setup(cbpi):
|
|||
:return:
|
||||
'''
|
||||
|
||||
cbpi.plugin.register("ActorStep", ActorStep)
|
||||
cbpi.plugin.register("WaitStep", WaitStep)
|
||||
cbpi.plugin.register("MashStep", MashStep)
|
||||
cbpi.plugin.register("ActorStep", ActorStep)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
176
cbpi/extension/mashstep/__init__.py.old
Normal file
|
@ -0,0 +1,176 @@
|
|||
|
||||
import asyncio
|
||||
from cbpi.api.timer import Timer
|
||||
|
||||
from cbpi.api import *
|
||||
import logging
|
||||
|
||||
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
|
||||
Property.Number(label="Temp", configurable=True),
|
||||
Property.Sensor(label="Sensor"),
|
||||
Property.Kettle(label="Kettle")])
|
||||
class MashStep(CBPiStep):
|
||||
|
||||
def __init__(self, cbpi, id, name, props):
|
||||
super().__init__(cbpi, id, name, props)
|
||||
self.timer = None
|
||||
|
||||
def timer_done(self):
|
||||
self.state_msg = "Done"
|
||||
asyncio.create_task(self.next())
|
||||
|
||||
async def timer_update(self, seconds, time):
|
||||
self.state_msg = "{}".format(time)
|
||||
self.push_update()
|
||||
|
||||
def start_timer(self):
|
||||
if self.timer is None:
|
||||
self.time = int(self.props.get("Timer", 0)) * 60
|
||||
self.timer = Timer(self.time, self.timer_done, self.timer_update)
|
||||
self.timer.start()
|
||||
|
||||
async def stop_timer(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = "{}".format(self.timer.get_time())
|
||||
|
||||
async def next(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = ""
|
||||
await super().next()
|
||||
|
||||
async def stop(self):
|
||||
await super().stop()
|
||||
await self.stop_timer()
|
||||
|
||||
async def reset(self):
|
||||
self.state_msg = ""
|
||||
self.timer = None
|
||||
await super().reset()
|
||||
|
||||
async def execute(self):
|
||||
if self.timer is None:
|
||||
self.state_msg = "Waiting for Target Temp"
|
||||
self.push_update()
|
||||
else:
|
||||
if self.timer is not None and self.timer.is_running() is False:
|
||||
self.start_timer()
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
sensor_value = self.get_sensor_value(self.props.get("Sensor"))
|
||||
if sensor_value.get("value") >= 2 and self.timer == None:
|
||||
self.start_timer()
|
||||
|
||||
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True)])
|
||||
class WaitStep(CBPiStep):
|
||||
|
||||
def __init__(self, cbpi, id, name, props):
|
||||
super().__init__(cbpi, id, name, props)
|
||||
self.timer = None
|
||||
|
||||
def timer_done(self):
|
||||
self.state_msg = "Done"
|
||||
|
||||
asyncio.create_task(self.next())
|
||||
|
||||
async def timer_update(self, seconds, time):
|
||||
self.state_msg = "{}".format(time)
|
||||
self.push_update()
|
||||
|
||||
def start_timer(self):
|
||||
if self.timer is None:
|
||||
self.time = int(self.props.get("Timer", 0)) * 60
|
||||
self.timer = Timer(self.time, self.timer_done, self.timer_update)
|
||||
self.timer.start()
|
||||
|
||||
async def stop_timer(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = "{}".format(self.timer.get_time())
|
||||
|
||||
async def next(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = ""
|
||||
await super().next()
|
||||
|
||||
async def stop(self):
|
||||
await super().stop()
|
||||
await self.stop_timer()
|
||||
|
||||
async def reset(self):
|
||||
self.state_msg = ""
|
||||
self.timer = None
|
||||
await super().reset()
|
||||
|
||||
async def execute(self):
|
||||
self.start_timer()
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
|
||||
Property.Actor(label="Actor")])
|
||||
class ActorStep(CBPiStep):
|
||||
|
||||
def __init__(self, cbpi, id, name, props):
|
||||
super().__init__(cbpi, id, name, props)
|
||||
self.timer = None
|
||||
|
||||
def timer_done(self):
|
||||
self.state_msg = "Done"
|
||||
asyncio.create_task(self.actor_off(self.actor_id))
|
||||
asyncio.create_task(self.next())
|
||||
|
||||
async def timer_update(self, seconds, time):
|
||||
self.state_msg = "{}".format(time)
|
||||
self.push_update()
|
||||
|
||||
def start_timer(self):
|
||||
if self.timer is None:
|
||||
self.time = int(self.props.get("Timer", 0)) * 60
|
||||
self.timer = Timer(self.time, self.timer_done, self.timer_update)
|
||||
self.timer.start()
|
||||
|
||||
async def stop_timer(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = "{}".format(self.timer.get_time())
|
||||
|
||||
async def next(self):
|
||||
if self.timer is not None:
|
||||
await self.timer.stop()
|
||||
self.state_msg = ""
|
||||
await super().next()
|
||||
|
||||
async def stop(self):
|
||||
await super().stop()
|
||||
await self.actor_off(self.actor_id)
|
||||
await self.stop_timer()
|
||||
|
||||
async def reset(self):
|
||||
self.state_msg = ""
|
||||
self.timer = None
|
||||
await super().reset()
|
||||
|
||||
async def execute(self):
|
||||
self.start_timer()
|
||||
self.actor_id = self.props.Actor
|
||||
await self.actor_on(self.actor_id)
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def setup(cbpi):
|
||||
'''
|
||||
This method is called by the server during startup
|
||||
Here you need to register your plugins at the server
|
||||
|
||||
:param cbpi: the cbpi core
|
||||
:return:
|
||||
'''
|
||||
|
||||
cbpi.plugin.register("ActorStep", ActorStep)
|
||||
cbpi.plugin.register("WaitStep", WaitStep)
|
||||
cbpi.plugin.register("MashStep", MashStep)
|
||||
|
|
@ -22,7 +22,7 @@ class CBPiMqttClient:
|
|||
await client.subscribe("cbpi/#")
|
||||
async for message in messages:
|
||||
await self.cbpi.actor.on("YwGzXvWMpmbLb6XobesL8n")
|
||||
print(message.topic, message.payload.decode())
|
||||
|
||||
|
||||
|
||||
async def listen(self, topic, **kwargs):
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name: MQTT
|
||||
version: 4.0
|
||||
version: 4
|
||||
active: false
|
|
@ -48,6 +48,7 @@ class ReadThread (threading.Thread):
|
|||
self.value = temp
|
||||
except:
|
||||
pass
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
@parameters([Property.Select(label="Sensor", options=getSensors()), Property.Select(label="Interval", options=[1,5,10,30,60], description="Interval in Seconds")])
|
||||
|
@ -70,15 +71,15 @@ class OneWire(CBPiSensor):
|
|||
|
||||
async def stop(self):
|
||||
try:
|
||||
print("STOP THE SENSOR")
|
||||
self.t.stop()
|
||||
self.running = False
|
||||
except:
|
||||
pass
|
||||
|
||||
async def run(self):
|
||||
while self.running is True:
|
||||
while True:
|
||||
self.value = self.t.value
|
||||
self.log_data(self.value)
|
||||
self.push_update(self.value)
|
||||
await asyncio.sleep(self.interval)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from cbpi.api.dataclasses import Actor, Props
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
auth = False
|
||||
|
@ -57,9 +58,10 @@ class ActorHttpEndpoints():
|
|||
description: successful operation
|
||||
"""
|
||||
data = await request.json()
|
||||
response_data = await self.controller.add(data)
|
||||
actor = Actor(name=data.get("name"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
response_data = await self.controller.add(actor)
|
||||
|
||||
return web.json_response(data=self.controller.create_dict(response_data))
|
||||
return web.json_response(data=response_data.to_dict())
|
||||
|
||||
|
||||
@request_mapping(path="/{id}", method="PUT", auth_required=False)
|
||||
|
@ -95,7 +97,8 @@ class ActorHttpEndpoints():
|
|||
"""
|
||||
id = request.match_info['id']
|
||||
data = await request.json()
|
||||
return web.json_response(data=self.controller.create_dict(await self.controller.update(id, data)))
|
||||
actor = Actor(id=id, name=data.get("name"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
return web.json_response(data=(await self.controller.update(actor)).to_dict())
|
||||
|
||||
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
|
||||
async def http_delete_one(self, request):
|
||||
|
|
|
@ -2,13 +2,13 @@ from aiohttp import web
|
|||
from cbpi.api import *
|
||||
|
||||
from cbpi.utils import json_dumps
|
||||
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
|
||||
|
||||
|
||||
class ConfigHttpEndpoints(HttpCrudEndpoints):
|
||||
|
||||
class ConfigHttpEndpoints:
|
||||
|
||||
def __init__(self, cbpi):
|
||||
super().__init__(cbpi)
|
||||
self.cbpi = cbpi
|
||||
self.controller = cbpi.config
|
||||
self.cbpi.register(self, "/config")
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
|
||||
from cbpi.utils.utils import json_dumps
|
||||
|
||||
|
||||
class HttpCrudEndpoints():
|
||||
|
||||
def __init__(self, cbpi):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.cbpi = cbpi
|
||||
|
||||
|
||||
@request_mapping(path="/types", auth_required=False)
|
||||
async def get_types(self, request):
|
||||
if self.controller.types is not None:
|
||||
return web.json_response(data=self.controller.types, dumps=json_dumps)
|
||||
else:
|
||||
return web.Response(status=404, text="Types not supported by endpoint")
|
||||
|
||||
@request_mapping(path="/", auth_required=False)
|
||||
async def http_get_all(self, request):
|
||||
return web.json_response(await self.controller.get_all(), dumps=json_dumps)
|
||||
|
||||
@request_mapping(path="/{id:\d+}", auth_required=False)
|
||||
async def http_get_one(self, request):
|
||||
id = int(request.match_info['id'])
|
||||
return web.json_response(await self.controller.get_one(id), dumps=json_dumps)
|
||||
|
||||
@request_mapping(path="/", method="POST", auth_required=False)
|
||||
async def http_add(self, request):
|
||||
data = await request.json()
|
||||
obj = await self.controller.add(**data)
|
||||
return web.json_response(obj, dumps=json_dumps)
|
||||
|
||||
@request_mapping(path="/{id}", method="PUT", auth_required=False)
|
||||
async def http_update(self, request):
|
||||
id = int(request.match_info['id'])
|
||||
data = await request.json()
|
||||
obj = await self.controller.update(id, data)
|
||||
return web.json_response(obj, dumps=json_dumps)
|
||||
|
||||
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
|
||||
async def http_delete_one(self, request):
|
||||
id = request.match_info['id']
|
||||
await self.controller.delete(int(id))
|
||||
return web.Response(status=204)
|
|
@ -1,13 +1,13 @@
|
|||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
from voluptuous import Schema
|
||||
|
||||
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
|
||||
from cbpi.utils import json_dumps
|
||||
import os
|
||||
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
|
||||
class DashBoardHttpEndpoints(HttpCrudEndpoints):
|
||||
from cbpi.utils import json_dumps
|
||||
from voluptuous import Schema
|
||||
|
||||
|
||||
class DashBoardHttpEndpoints:
|
||||
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from cbpi.controller.kettle_controller import KettleController
|
||||
from cbpi.api.dataclasses import Kettle, Props
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
|
||||
|
@ -7,7 +9,7 @@ class KettleHttpEndpoints():
|
|||
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
self.controller = cbpi.kettle
|
||||
self.controller : KettleController = cbpi.kettle
|
||||
self.cbpi.register(self, "/kettle")
|
||||
|
||||
@request_mapping(path="/", auth_required=False)
|
||||
|
@ -70,9 +72,10 @@ class KettleHttpEndpoints():
|
|||
description: successful operation
|
||||
"""
|
||||
data = await request.json()
|
||||
response_data = await self.controller.add(data)
|
||||
|
||||
return web.json_response(data=self.controller.create_dict(response_data))
|
||||
kettle = Kettle(name=data.get("name"), sensor=data.get("sensor"), heater=data.get("heater"), agitator=data.get("agitator"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
response_data = await self.controller.add(kettle)
|
||||
return web.json_response(data=response_data.to_dict())
|
||||
|
||||
|
||||
@request_mapping(path="/{id}", method="PUT", auth_required=False)
|
||||
|
@ -108,7 +111,8 @@ class KettleHttpEndpoints():
|
|||
"""
|
||||
id = request.match_info['id']
|
||||
data = await request.json()
|
||||
return web.json_response(data=self.controller.create_dict(await self.controller.update(id, data)))
|
||||
kettle = Kettle(id=id, name=data.get("name"), sensor=data.get("sensor"), heater=data.get("heater"), agitator=data.get("agitator"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
return web.json_response(data=(await self.controller.update(kettle)).to_dict())
|
||||
|
||||
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
|
||||
async def http_delete_one(self, request):
|
||||
|
|
|
@ -135,7 +135,7 @@ class LogHttpEndpoints:
|
|||
description: successful operation.
|
||||
"""
|
||||
log_name = request.match_info['name']
|
||||
print(log_name)
|
||||
|
||||
data = self.cbpi.log.get_logfile_names(log_name)
|
||||
return web.json_response(data, dumps=json_dumps)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from cbpi.api.dataclasses import Props, Sensor
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
auth = False
|
||||
|
@ -57,9 +58,10 @@ class SensorHttpEndpoints():
|
|||
description: successful operation
|
||||
"""
|
||||
data = await request.json()
|
||||
response_data = await self.controller.add(data)
|
||||
sensor = Sensor(name=data.get("name"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
response_data = await self.controller.add(sensor)
|
||||
|
||||
return web.json_response(data=self.controller.create_dict(response_data))
|
||||
return web.json_response(data=response_data.to_dict())
|
||||
|
||||
|
||||
@request_mapping(path="/{id}", method="PUT", auth_required=False)
|
||||
|
@ -95,7 +97,8 @@ class SensorHttpEndpoints():
|
|||
"""
|
||||
id = request.match_info['id']
|
||||
data = await request.json()
|
||||
return web.json_response(data=self.controller.create_dict(await self.controller.update(id, data)))
|
||||
sensor = Sensor(id=id, name=data.get("name"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
return web.json_response(data=(await self.controller.update(sensor)).to_dict())
|
||||
|
||||
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
|
||||
async def http_delete_one(self, request):
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from cbpi.controller.step_controller import StepController
|
||||
from cbpi.api.dataclasses import Props, Step
|
||||
from aiohttp import web
|
||||
from cbpi.api import *
|
||||
|
||||
|
@ -5,7 +7,7 @@ class StepHttpEndpoints():
|
|||
|
||||
def __init__(self, cbpi):
|
||||
self.cbpi = cbpi
|
||||
self.controller = cbpi.step
|
||||
self.controller : StepController = cbpi.step
|
||||
self.cbpi.register(self, "/step2")
|
||||
|
||||
def create_dict(self, data):
|
||||
|
@ -47,9 +49,14 @@ class StepHttpEndpoints():
|
|||
description: successful operation
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
data = await request.json()
|
||||
result = await self.controller.add(data)
|
||||
return web.json_response(self.create_dict(result))
|
||||
step = Step(name=data.get("name"), props=Props(data.get("props", {})), type=data.get("type"))
|
||||
response_data = await self.controller.add(step)
|
||||
return web.json_response(data=response_data.to_dict())
|
||||
|
||||
|
||||
@request_mapping(path="/{id}", method="PUT", auth_required=False)
|
||||
async def http_update(self, request):
|
||||
|
@ -73,9 +80,8 @@ class StepHttpEndpoints():
|
|||
|
||||
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))
|
||||
step = Step(id, data.get("name"), Props(data.get("props", {})), data.get("type"))
|
||||
return web.json_response((await self.controller.update(step)).to_dict())
|
||||
|
||||
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
|
||||
async def http_delete(self, request):
|
||||
|
|
|
@ -38,21 +38,21 @@ class CBPiSatellite:
|
|||
async def websocket_handler(self, request):
|
||||
|
||||
|
||||
print("HALLO SATELLITE")
|
||||
|
||||
ws = web.WebSocketResponse()
|
||||
await ws.prepare(request)
|
||||
self._clients.add(ws)
|
||||
try:
|
||||
peername = request.transport.get_extra_info('peername')
|
||||
if peername is not None:
|
||||
print(peername)
|
||||
|
||||
host = peername[0]
|
||||
port = peername[1]
|
||||
else:
|
||||
host, port = "Unknowen"
|
||||
self.logger.info("Client Connected - Host: %s Port: %s - client count: %s " % (host, port, len(self._clients)))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
|
|
|
@ -14,6 +14,6 @@ class ComplexEncoder(JSONEncoder):
|
|||
else:
|
||||
raise TypeError()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
pass
|
||||
return None
|
||||
|
|
|
@ -13,7 +13,7 @@ def load_config(fname):
|
|||
data = yaml.load(f, Loader=yaml.FullLoader)
|
||||
return data
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
pass
|
||||
|
||||
def json_dumps(obj):
|
||||
|
|
|
@ -45,14 +45,14 @@ class CBPiWebSocket:
|
|||
try:
|
||||
peername = request.transport.get_extra_info('peername')
|
||||
if peername is not None:
|
||||
print(peername)
|
||||
|
||||
host = peername[0]
|
||||
port = peername[1]
|
||||
else:
|
||||
host, port = "Unknowen"
|
||||
self.logger.info("Client Connected - Host: %s Port: %s - client count: %s " % (host, port, len(self._clients)))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "YwGzXvWMpmbLb6XobesL8n",
|
||||
"name": "Actor 1",
|
||||
"props": {
|
||||
"GPIO": 4,
|
||||
"Inverted": "Yes"
|
||||
},
|
||||
"state": false,
|
||||
"type": "GPIOActor"
|
||||
},
|
||||
{
|
||||
"id": "EsmZwWi9Qp3bzmXqq7N3Ly",
|
||||
"name": "Actor 2",
|
||||
"props": {
|
||||
"Frequency": "20",
|
||||
"GPIO": 5
|
||||
},
|
||||
"state": false,
|
||||
"type": "GPIOPWMActor"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
{
|
||||
"elements": [
|
||||
{
|
||||
"id": "84e6afbd-7ed0-4135-b64e-4ce89568946d",
|
||||
"name": "Kettle",
|
||||
"props": {
|
||||
"heigth": "150",
|
||||
"width": "100"
|
||||
},
|
||||
"type": "Kettle",
|
||||
"x": 105,
|
||||
"y": 55
|
||||
},
|
||||
{
|
||||
"id": "bec11478-8056-4577-a095-a5909282b0ac",
|
||||
"name": "Kettle",
|
||||
"props": {
|
||||
"heigth": "150",
|
||||
"width": "100"
|
||||
},
|
||||
"type": "Kettle",
|
||||
"x": 360,
|
||||
"y": 60
|
||||
},
|
||||
{
|
||||
"id": "d07d7f84-5bc8-42d5-9ef5-9d5be1ff4584",
|
||||
"name": "Left",
|
||||
"props": {
|
||||
"actor": "YwGzXvWMpmbLb6XobesL8n"
|
||||
},
|
||||
"type": "ActorButton",
|
||||
"x": 510,
|
||||
"y": 60
|
||||
},
|
||||
{
|
||||
"id": "310d78c3-b3c0-40dc-b2a1-42488787fd46",
|
||||
"name": "Right",
|
||||
"props": {
|
||||
"actor": "EsmZwWi9Qp3bzmXqq7N3Ly"
|
||||
},
|
||||
"type": "ActorButton",
|
||||
"x": 505,
|
||||
"y": 140
|
||||
},
|
||||
{
|
||||
"id": "3cae292a-f12d-4c9e-8e0e-2fd93a9b253e",
|
||||
"name": "TargetTemp",
|
||||
"props": {
|
||||
"color": "#fff",
|
||||
"kettle": "oHxKz3z5RjbsxfSz6KUgov",
|
||||
"size": "30",
|
||||
"unit": "\u00b0"
|
||||
},
|
||||
"type": "TargetTemp",
|
||||
"x": 135,
|
||||
"y": 75
|
||||
},
|
||||
{
|
||||
"id": "bb90e1ab-7b2d-4623-8df3-3139f91b7087",
|
||||
"name": "Steps",
|
||||
"props": {
|
||||
"width": "200"
|
||||
},
|
||||
"type": "Steps",
|
||||
"x": 595,
|
||||
"y": 50
|
||||
},
|
||||
{
|
||||
"id": "9dfc216e-21d7-44af-b10f-cf4158144134",
|
||||
"name": "KettleControl",
|
||||
"props": {
|
||||
"kettle": "oHxKz3z5RjbsxfSz6KUgov",
|
||||
"orientation": "horizontal"
|
||||
},
|
||||
"type": "KettleControl",
|
||||
"x": 100,
|
||||
"y": 20
|
||||
}
|
||||
],
|
||||
"pathes": [
|
||||
{
|
||||
"condition": {
|
||||
"left": [
|
||||
"YwGzXvWMpmbLb6XobesL8n"
|
||||
],
|
||||
"right": [
|
||||
"EsmZwWi9Qp3bzmXqq7N3Ly"
|
||||
]
|
||||
},
|
||||
"coordinates": [
|
||||
[
|
||||
215,
|
||||
90
|
||||
],
|
||||
[
|
||||
360,
|
||||
90
|
||||
]
|
||||
],
|
||||
"id": "559fb368-bce9-4f9b-a25c-c468ae0cac88"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"left": [
|
||||
"YwGzXvWMpmbLb6XobesL8n"
|
||||
],
|
||||
"right": [
|
||||
"EsmZwWi9Qp3bzmXqq7N3Ly"
|
||||
]
|
||||
},
|
||||
"coordinates": [
|
||||
[
|
||||
365,
|
||||
160
|
||||
],
|
||||
[
|
||||
220,
|
||||
160
|
||||
]
|
||||
],
|
||||
"id": "f0b05e9f-132b-4797-9fb0-1431c0579733"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
name: CraftBeerPi
|
||||
version: 4.0
|
||||
|
||||
index_url: /cbpi_ui/static/index.html
|
||||
plugins:
|
||||
- cbpi4ui
|
||||
|
||||
port: 8080
|
||||
# login data
|
||||
username: cbpi
|
||||
password: 123
|
||||
ws_push_all: true
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
{
|
||||
"elements": [
|
||||
{
|
||||
"id": "db0c8199-6935-4c77-989a-28528b6743d7",
|
||||
"name": "Kettle",
|
||||
"props": {
|
||||
"heigth": "150",
|
||||
"width": "100"
|
||||
},
|
||||
"type": "Kettle",
|
||||
"x": 205,
|
||||
"y": 155
|
||||
},
|
||||
{
|
||||
"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": 210,
|
||||
"y": 135
|
||||
},
|
||||
{
|
||||
"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": "8ohkXvFA9UrkHLsxQL38wu",
|
||||
"unit": "\u00b0"
|
||||
},
|
||||
"type": "Sensor",
|
||||
"x": 245,
|
||||
"y": 260
|
||||
},
|
||||
{
|
||||
"id": "13a6b89d-50c7-4efb-b940-ec174e522314",
|
||||
"name": "Sensor Data",
|
||||
"props": {
|
||||
"sensor": "8ohkXvFA9UrkHLsxQL38wu",
|
||||
"unit": "\u00b0"
|
||||
},
|
||||
"type": "Sensor",
|
||||
"x": 445,
|
||||
"y": 260
|
||||
},
|
||||
{
|
||||
"id": "8d171952-791d-4f72-bfc9-dac8714b839f",
|
||||
"name": "Sensor Data",
|
||||
"props": {
|
||||
"sensor": "8ohkXvFA9UrkHLsxQL38wu",
|
||||
"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": "8BLRqagLicCdEBDdc77Sgr"
|
||||
},
|
||||
"type": "Led",
|
||||
"x": 240,
|
||||
"y": 210
|
||||
},
|
||||
{
|
||||
"id": "2e325539-6ed9-4e0d-b1dc-de860c47a1be",
|
||||
"name": "Heater",
|
||||
"props": {
|
||||
"actor": "8BLRqagLicCdEBDdc77Sgr"
|
||||
},
|
||||
"type": "ActorButton",
|
||||
"x": 120,
|
||||
"y": 255
|
||||
},
|
||||
{
|
||||
"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": 280
|
||||
},
|
||||
{
|
||||
"id": "3be00e94-4e06-4a6b-9b8d-c832be73386a",
|
||||
"name": "Led",
|
||||
"props": {
|
||||
"actor": "Aifjxmw4QdPfU3XbR6iyis"
|
||||
},
|
||||
"type": "Led",
|
||||
"x": 435,
|
||||
"y": 210
|
||||
},
|
||||
{
|
||||
"id": "d896b230-8dab-4c33-b73a-1dd74e6de906",
|
||||
"name": "Led",
|
||||
"props": {
|
||||
"actor": 1
|
||||
},
|
||||
"type": "Led",
|
||||
"x": 625,
|
||||
"y": 215
|
||||
},
|
||||
{
|
||||
"id": "adfe673c-1778-4980-b751-5c613c5c5b76",
|
||||
"name": "Pump1",
|
||||
"props": {
|
||||
"actor": "Aifjxmw4QdPfU3XbR6iyis"
|
||||
},
|
||||
"type": "ActorButton",
|
||||
"x": 360,
|
||||
"y": 360
|
||||
},
|
||||
{
|
||||
"id": "0e1ea214-9ae0-47c2-902d-cae0947ba8a1",
|
||||
"name": "Pump2",
|
||||
"props": {
|
||||
"actor": "HX2bKdobuANehPggYcynnj"
|
||||
},
|
||||
"type": "ActorButton",
|
||||
"x": 810,
|
||||
"y": 320
|
||||
}
|
||||
],
|
||||
"pathes": [
|
||||
{
|
||||
"condition": [
|
||||
"8BLRqagLicCdEBDdc77Sgr",
|
||||
"Aifjxmw4QdPfU3XbR6iyis"
|
||||
],
|
||||
"coordinates": [
|
||||
[
|
||||
305,
|
||||
185
|
||||
],
|
||||
[
|
||||
405,
|
||||
185
|
||||
]
|
||||
],
|
||||
"id": "49e7684e-21a3-4e0b-8e94-60f95abee80f"
|
||||
},
|
||||
{
|
||||
"condition": [
|
||||
"8BLRqagLicCdEBDdc77Sgr"
|
||||
],
|
||||
"coordinates": [
|
||||
[
|
||||
400,
|
||||
275
|
||||
],
|
||||
[
|
||||
305,
|
||||
275
|
||||
]
|
||||
],
|
||||
"id": "5ba909c1-49a9-46e5-a6d0-1d0350c37aa4"
|
||||
},
|
||||
{
|
||||
"condition": [
|
||||
"Aifjxmw4QdPfU3XbR6iyis"
|
||||
],
|
||||
"coordinates": [
|
||||
[
|
||||
255,
|
||||
300
|
||||
],
|
||||
[
|
||||
255,
|
||||
350
|
||||
],
|
||||
[
|
||||
555,
|
||||
350
|
||||
],
|
||||
[
|
||||
555,
|
||||
200
|
||||
],
|
||||
[
|
||||
585,
|
||||
200
|
||||
]
|
||||
],
|
||||
"id": "aed2d4d3-b99e-4af5-b8cf-d92d47721be4"
|
||||
},
|
||||
{
|
||||
"condition": [
|
||||
"HX2bKdobuANehPggYcynnj"
|
||||
],
|
||||
"coordinates": [
|
||||
[
|
||||
685,
|
||||
275
|
||||
],
|
||||
[
|
||||
795,
|
||||
275
|
||||
],
|
||||
[
|
||||
795,
|
||||
375
|
||||
]
|
||||
],
|
||||
"id": "176fed29-56c2-4534-9cab-8c328d0e138c"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 8.4 KiB |
|
@ -1,75 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C1C8D1;}
|
||||
.st1{fill:#758190;}
|
||||
.st2{fill:#A8B0BC;}
|
||||
.st3{fill:#E7E4DD;}
|
||||
.st4{fill:#8892A0;}
|
||||
.st5{fill:#4482C3;}
|
||||
.st6{fill:#95A5A5;}
|
||||
.st7{fill:#BDC3C7;}
|
||||
.st8{fill:#285680;}
|
||||
.st9{fill:none;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M404.7,322.7c-11.4,0-20.7-9.3-20.7-20.7v-32.5h11.8V302c0,4.9,4,8.9,8.9,8.9s8.9-4,8.9-8.9v-38.5
|
||||
c0-3.3-2.7-5.9-5.9-5.9h-11.8v-11.8h11.8c9.8,0,17.7,8,17.7,17.7V302C425.4,313.4,416.1,322.7,404.7,322.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st0" d="M103,322.7c-11.4,0-20.7-9.3-20.7-20.7v-38.5c0-4.7,1.8-9.2,5.2-12.6c3.4-3.3,7.8-5.2,12.6-5.2h11.8v11.8
|
||||
h-11.8c-1.6,0-3.1,0.6-4.2,1.7c-1.1,1.1-1.7,2.6-1.7,4.2V302c0,4.9,4,8.9,8.9,8.9c4.9,0,8.9-4,8.9-8.9v-32.5h11.8V302
|
||||
C123.7,313.4,114.5,322.7,103,322.7z"/>
|
||||
</g>
|
||||
<path class="st1" d="M366.3,234H384c6.5,0,11.8,5.3,11.8,11.8v23.7c0,6.5-5.3,11.8-11.8,11.8h-17.7"/>
|
||||
<path class="st1" d="M141.5,234h-17.7c-6.5,0-11.8,5.3-11.8,11.8v23.7c0,6.5,5.3,11.8,11.8,11.8h17.7"/>
|
||||
<path class="st0" d="M360.4,216.2c7.8,15.6,11.8,32.7,11.8,50.1v198.3c0,26.1-21.2,47.3-47.3,47.3h-142
|
||||
c-26.1,0-47.3-21.2-47.3-47.3V266.3c0-17.4,4.1-34.6,11.8-50.1"/>
|
||||
<path class="st2" d="M372.2,293.1H135.6v-26.8c0-17.4,4.1-34.6,11.8-50.1h213c7.8,15.6,11.8,32.7,11.8,50.1V293.1z"/>
|
||||
<path class="st1" d="M354.5,216.2H153.3c-9.8,0-17.7-7.9-17.7-17.7c0-9.8,7.9-17.7,17.7-17.7h201.1c9.8,0,17.7,7.9,17.7,17.7
|
||||
C372.2,208.3,364.3,216.2,354.5,216.2z"/>
|
||||
<path class="st2" d="M153.3,180.7v-11.8c0-6.5,5.3-11.8,11.8-11.8h177.5c6.5,0,11.8,5.3,11.8,11.8v11.8"/>
|
||||
<circle class="st1" cx="253.9" cy="281.3" r="29.6"/>
|
||||
<circle class="st3" cx="253.9" cy="281.3" r="11.8"/>
|
||||
<path class="st2" d="M372.2,446.9H135.6c-3.3,0-5.9-2.7-5.9-5.9v-23.7c0-3.3,2.7-5.9,5.9-5.9h236.6c3.3,0,5.9,2.7,5.9,5.9V441
|
||||
C378.1,444.3,375.5,446.9,372.2,446.9z"/>
|
||||
<path class="st0" d="M316.3,157.1H165.2c-6.5,0-11.8,5.3-11.8,11.8v11.8h167.2C319.6,172.7,318.2,164.8,316.3,157.1z"/>
|
||||
<path class="st4" d="M135.6,198.5c0,9.8,7.9,17.7,17.7,17.7h168.2c0.2-3.9,0.4-7.9,0.4-11.8c0-8-0.5-15.9-1.4-23.7H153.3
|
||||
C143.5,180.7,135.6,188.7,135.6,198.5L135.6,198.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Page-1">
|
||||
<g id="_x30_23---Valve">
|
||||
<path id="Shape" class="st5" d="M252.6,133l0-9.6c0,2.3-0.8,4.5-2.3,6.2v0c-1.8,2.1-4.5,3.3-7.3,3.3l-9.6-0.1
|
||||
c-2.8,0-5.4-1.3-7.2-3.4c-1-1.1-1.7-2.5-2-4c-0.2-0.7-0.3-1.5-0.3-2.3c0,0.8-0.1,1.5-0.3,2.3c-0.3,1.5-1,2.8-2.1,3.9
|
||||
c-1.8,2.1-4.5,3.3-7.3,3.3l-9.6-0.1c-5.3-0.1-9.6-4.4-9.5-9.7l0.2-43.1c0-2.5,1-5,2.8-6.7c1.8-1.8,4.2-2.8,6.8-2.8l2.4,0l11.3,1
|
||||
c2.6,1.3,4.5,3.6,5.2,6.4c0.2,0.7,0.3,1.5,0.3,2.2c0-2.5,1-5,2.8-6.7c1.8-1.8,4.2-2.8,6.8-2.7L241,71l3.2,0.2
|
||||
c6.1,0.3,10.9,5.4,10.9,11.6l-0.2,47.9C254.9,132,253.8,133,252.6,133z"/>
|
||||
<path id="Shape_1_" class="st6" d="M252.8,82.7c-0.5,0-0.9-0.2-1.3-0.4c-0.6-0.4-0.9-1.1-1-1.8l0-0.1l0-7.3v-2.3
|
||||
c0-0.9-0.4-1.7-1.2-2.2s-1.7-0.5-2.5,0c-0.8,0.4-1.3,1.3-1.3,2.2c0,0.2,0,0.5-0.1,0.7L241,71c0-0.1-0.1-0.3-0.1-0.4
|
||||
c0-3.9,3.2-7.1,7.1-7c3.9,0.1,7.1,3.3,7.1,7.1V73l0,7.3C255.1,81.6,254.1,82.7,252.8,82.7z"/>
|
||||
<path id="Shape_2_" class="st7" d="M257.2,161.7c-1,0-1.9-0.3-2.7-0.9c-1.3-0.9-2-2.4-2.1-4l0.1-23.9c1.3,0,2.3-1,2.3-2.3
|
||||
l0.2-50.3l0-7.3v-2.3c0-3.9-3.2-7.1-7.1-7.1c-3.9,0-7.1,3.1-7.1,7c0,0.1,0,0.3,0.1,0.4l-7.3-0.5c0-4.1,1.8-8,5-10.8
|
||||
c3.1-2.7,7.3-3.9,11.4-3.3c7.2,1.2,12.6,7.5,12.4,14.8L262,157c0,1.3-0.5,2.5-1.4,3.4C259.7,161.3,258.5,161.7,257.2,161.7z"/>
|
||||
<path id="Shape_3_" class="st7" d="M223.7,151.8c-10.6-0.1-19.1-8.7-19.1-19.3l9.6,0.1c0,5.3,4.2,9.6,9.6,9.6
|
||||
c2.5,0,5-0.9,6.8-2.7c1.8-1.8,2.8-4.2,2.8-6.7l9.6,0.1c0,5.1-2.1,9.9-5.7,13.5C233.7,149.9,228.8,151.9,223.7,151.8z"/>
|
||||
<path id="Shape_4_" class="st7" d="M228,59.1l-9.4,12.3l-11.3-1l12.1-15.8l0.1-12.7l9.6,0.1l-0.1,14.3
|
||||
C228.9,57.2,228.6,58.2,228,59.1z"/>
|
||||
<path id="Shape_5_" class="st6" d="M252.6,133c-0.6,0-1.2-0.2-1.6-0.7s-0.7-1-0.7-1.6v-1.1v0l0.2-49.2l0-0.1l0-7.3
|
||||
c0-1.3,1-2.3,2.3-2.3c1.3,0,2.3,1,2.3,2.3l0,7.3l-0.2,50.3C254.9,132,253.8,133,252.6,133z"/>
|
||||
<path id="Shape_6_" class="st8" d="M232.2,41.9l-3.2,0l-9.6-0.1l-3.2,0c-1,0-1.8-0.6-2.2-1.5l-5.1-12c-0.3-0.7-0.3-1.6,0.2-2.2
|
||||
c0.4-0.7,1.2-1.1,2-1l26.3,0.3c0.8,0,1.6,0.4,2,1.1c0.4,0.7,0.5,1.5,0.2,2.2l-5.2,11.9C234,41.3,233.2,41.9,232.2,41.9z"/>
|
||||
<path id="Shape_7_" class="st5" d="M241.2,25.2l-3.7,0L211.2,25l-3.7-0.1c-0.6,0-1.2-0.3-1.6-0.7s-0.7-1-0.7-1.6l0.1-12.3
|
||||
c0-2.5,1-4.8,2.8-6.5s4.1-2.7,6.6-2.6l19.8,0.2c5.1,0.1,9.2,4.2,9.2,9.4L243.5,23C243.5,24.2,242.4,25.2,241.2,25.2z"/>
|
||||
<path id="Shape_8_" class="st8" d="M222.5,125c-0.6-0.4-1-1.1-1-1.9l0.2-43.1c0-0.9,0.4-1.6,1.1-2.1c0.7-0.4,1.6-0.4,2.3,0
|
||||
c0.7,0.4,1.1,1.2,1.1,2.1l-0.2,43.1c0,0.9-0.5,1.6-1.3,2.1C224.1,125.6,223.2,125.5,222.5,125z"/>
|
||||
<path id="Shape_9_" class="st9" d="M244.2,71.3l-3.2-0.2"/>
|
||||
<polyline id="Shape_10_" class="st9" points="245.7,71.5 245.5,71.4 241,71.1 240.4,71 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 5.1 KiB |
|
@ -1 +0,0 @@
|
|||
<svg id="Layer_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><switch><foreignObject height="1" requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/" width="1"/><g><g><g><path d="m256 160v-32h-64v32c0 26.51-21.49 48-48 48-26.51 0-48 21.49-48 48v224c0 8.837 7.163 16 16 16h224c8.837 0 16-7.163 16-16v-224c0-26.51-21.49-48-48-48-26.51 0-48-21.49-48-48z" fill="#c8c8c3"/></g><g><path d="m304 208c-26.51 0-48-21.49-48-48v-32h-32v352c0 8.837-7.163 16-16 16h128c8.837 0 16-7.163 16-16v-224c0-26.51-21.49-48-48-48z" fill="#a0a5aa"/></g><g><path d="m96 288h128v192h-128z" fill="#facc0a"/></g><g><path d="m224 288h128v192h-128z" fill="#faa01e"/></g><g><path d="m96 256h128v32h-128z" fill="#f0d7b9"/></g><g><path d="m224 256h128v32h-128z" fill="#f0be8c"/></g><g><path d="m256 112h-64c-4.418 0-8 3.582-8 8 0 4.418 3.582 8 8 8h64c4.418 0 8-3.582 8-8 0-4.418-3.582-8-8-8z" fill="#c8c8c3"/></g><g><path d="m256 112h-32v16h32c4.418 0 8-3.582 8-8s-3.582-8-8-8z" fill="#a0a5aa"/></g><g><path d="m224 144h-24c-4.418 0-8-3.582-8-8v-24h32z" fill="#f0d7b9"/></g><g><path d="m248 144h-24v-32h32v24c0 4.418-3.582 8-8 8z" fill="#f0be8c"/></g><g><path d="m224 96h-24c-4.418 0-8 3.582-8 8v8h32z" fill="#f0be8c"/></g><g><path d="m248 96h-24v16h32v-8c0-4.418-3.582-8-8-8z" fill="#faa01e"/></g><g><circle cx="162.667" cy="450.667" fill="#faa01e" r="16"/></g><g><circle cx="174.667" cy="329.333" fill="#faa01e" r="12"/></g><g><circle cx="200" cy="410.667" fill="#faa01e" r="8"/></g><g><circle cx="301.333" cy="312" fill="#facc0a" r="16"/></g><g><circle cx="289.333" cy="454.667" fill="#facc0a" r="12"/></g><g><circle cx="264" cy="370.667" fill="#facc0a" r="8"/></g><g><path d="m224 48h-24v-24c0-4.418 3.582-8 8-8h16z" fill="#c8c8c3"/></g><g><path d="m248 48h-24v-32h16c4.418 0 8 3.582 8 8z" fill="#a0a5aa"/></g><g><path d="m224 80h-16c-4.418 0-8-3.582-8-8v-24h24z" fill="#facc0a"/></g><g><path d="m240 80h-16v-32h24v24c0 4.418-3.582 8-8 8z" fill="#faa01e"/></g><g><path d="m216 80h16v16h-16z" fill="#737378"/></g><g><path d="m212 22.667c2.209 0 4 1.791 4 4v41.333c0 2.209-1.791 4-4 4-2.209 0-4-1.791-4-4v-41.333c0-2.209 1.791-4 4-4z" fill="#fff"/></g><g><path d="m112 486c-3.313 0-6-2.687-6-6v-224c0-20.953 17.047-38 38-38 31.981 0 58-26.019 58-58v-32c0-3.313 2.687-6 6-6s6 2.687 6 6v32c0 38.598-31.402 70-70 70-14.336 0-26 11.664-26 26v224c0 3.313-2.687 6-6 6z" fill="#fff"/></g><g><circle cx="384" cy="384" fill="#3c915a" r="96"/></g><g><circle cx="384" cy="384" fill="#73c369" r="72"/></g><g><path d="m384 448c-2.946 0-5.333-2.388-5.333-5.333v-5.333c0-2.945 2.388-5.333 5.333-5.333 2.946 0 5.333 2.388 5.333 5.333v5.333c0 2.945-2.387 5.333-5.333 5.333z" fill="#fff"/></g><g><path d="m320 384c0-2.946 2.388-5.333 5.333-5.333h5.333c2.945 0 5.333 2.388 5.333 5.333 0 2.946-2.388 5.333-5.333 5.333h-5.333c-2.945 0-5.333-2.387-5.333-5.333z" fill="#fff"/></g><g><path d="m432 384c0-2.946 2.388-5.333 5.333-5.333h5.333c2.945 0 5.333 2.388 5.333 5.333 0 2.946-2.388 5.333-5.333 5.333h-5.333c-2.945 0-5.333-2.387-5.333-5.333z" fill="#fff"/></g><g><path d="m417.333 421.333c-1.024 0-2.047-.391-2.829-1.171l-34.504-34.505v-60.323c0-2.209 1.791-4 4-4s4 1.791 4 4v57.01l32.162 32.162c1.562 1.563 1.562 4.095 0 5.657-.781.78-1.805 1.17-2.829 1.17z" fill="#dc6e1e"/></g><g><circle cx="384" cy="384" fill="#fff" r="16"/></g><g><circle cx="384" cy="384" fill="#3c915a" r="8"/></g></g></g></switch></svg>
|
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,45 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 832.8" style="enable-background:new 0 0 512 832.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E7D2CB;}
|
||||
.st1{fill:#C25100;}
|
||||
.st2{fill:#D8B6A9;}
|
||||
.st3{fill:#A04708;}
|
||||
.st4{fill:#7F4216;}
|
||||
.st5{fill:#F6F1EF;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M434.7,12.7H308V44h126.6c25.4,0,46,20.6,46,46v63.1c0,2.2-1.8,4-4.1,4H35.4c-2.2,0-4.1-1.8-4.1-4V90
|
||||
c0-25.4,20.7-46,46-46H204V12.7H77.3C34.7,12.7,0,47.4,0,90v63.1c0,19.5,15.9,35.4,35.4,35.4h441.2c19.5,0,35.4-15.9,35.4-35.4V90
|
||||
C512,47.4,477.3,12.7,434.7,12.7L434.7,12.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M193.6,12.7h124.7V44H193.6V12.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M512,90v63.1c0,19.5-15.9,35.4-35.4,35.4h-31.3c19.5,0,35.4-15.9,35.4-35.4V90c0-0.4,0-0.8,0-1.2
|
||||
c-0.7-42.1-35.1-76.1-77.3-76.1h31.3C477.3,12.7,512,47.4,512,90z"/>
|
||||
</g>
|
||||
<path class="st1" d="M486.4,225.6l-13.7,574.2c-0.9,16.6-14.7,29.6-31.3,29.6H70.5c-16.6,0-30.3-13-31.3-29.6L25.6,225.6
|
||||
c-0.3-6,4.4-11,10.4-11h175l3.6,15.6c2.2,9.5,10.6,16.2,20.3,16.2h42.2c9.7,0,18.2-6.7,20.3-16.2l3.6-15.6h175
|
||||
C482,214.5,486.8,219.6,486.4,225.6L486.4,225.6z"/>
|
||||
<path class="st3" d="M486.4,225.6l-13.7,574.2c-0.9,16.6-14.7,29.6-31.3,29.6h-43.1c16.6,0,30.3-13,31.3-29.6l13.7-574.2
|
||||
c0.3-6-4.4-11-10.4-11H476C482,214.5,486.8,219.6,486.4,225.6L486.4,225.6z"/>
|
||||
<path class="st3" d="M307.7,240.4l6-25.9H198.3l6,25.9c2.2,9.5,10.6,16.2,20.3,16.2h62.8C297.1,256.6,305.6,249.9,307.7,240.4
|
||||
L307.7,240.4z"/>
|
||||
<path class="st4" d="M313.7,214.5l-6,25.9c-2.2,9.5-10.6,16.2-20.3,16.2h-36.3c9.7,0,18.2-6.7,20.3-16.2l6-25.9L313.7,214.5z"/>
|
||||
<path class="st5" d="M34.4,170.4l-6.9,33.7c-1.3,6.5,3.6,12.5,10.2,12.5h436.5c6.6,0,11.6-6.1,10.2-12.5l-6.9-33.7
|
||||
c-3-14.6-15.8-25-30.7-25H65.1C50.3,145.4,37.4,155.8,34.4,170.4L34.4,170.4z"/>
|
||||
<path class="st0" d="M474.3,216.6h-43.1c6.6,0,11.6-6.1,10.2-12.5l-6.9-33.7c-3-14.6-15.8-25-30.7-25h43.1
|
||||
c14.9,0,27.7,10.5,30.7,25l6.9,33.7C485.8,210.5,480.9,216.6,474.3,216.6L474.3,216.6z"/>
|
||||
<g>
|
||||
<path class="st3" d="M357.6,774.4H154.4c-5.8,0-10.4-4.7-10.4-10.4V369.5c0-5.8,4.7-10.4,10.4-10.4h203.1
|
||||
c5.8,0,10.4,4.7,10.4,10.4V764C368,769.7,363.3,774.4,357.6,774.4z"/>
|
||||
<path class="st4" d="M368,369.4v394.5c0,5.8-4.7,10.4-10.4,10.4h-36.3c5.8,0,10.4-4.7,10.4-10.4V369.4c0-5.8-4.7-10.4-10.4-10.4
|
||||
h36.3C363.3,359,368,363.7,368,369.4L368,369.4z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 12 KiB |
|
@ -1,73 +0,0 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#566E7F;" d="M496,248H16c-8.836,0-16,7.163-16,16v48c0,8.836,7.164,16,16,16h480c8.837,0,16-7.164,16-16v-48
|
||||
C512,255.163,504.837,248,496,248z M328,304H184v-32h144V304z"/>
|
||||
<path style="fill:#B1C3D0;" d="M344,106.174V24c0-8.837-7.163-16-16-16H184c-8.837,0-16,7.163-16,16v82.174
|
||||
c0,8.823-3.643,17.255-10.068,23.302L32,248h448L354.069,129.476C347.643,123.429,344,114.997,344,106.174z"/>
|
||||
<g>
|
||||
<path style="fill:#D0D7DA;" d="M196.696,377.524c-3.586-2.602-8.586-1.797-11.172,1.781c-2.594,3.578-1.797,8.578,1.781,11.172
|
||||
C195.492,396.414,200,404.055,200,412s-4.508,15.586-12.695,21.523C174.852,442.547,168,454.789,168,468
|
||||
s6.852,25.453,19.305,34.477c1.422,1.031,3.063,1.523,4.688,1.523c2.477,0,4.922-1.148,6.484-3.305
|
||||
c2.594-3.578,1.797-8.578-1.781-11.172C188.508,483.586,184,475.945,184,468s4.508-15.586,12.695-21.523
|
||||
C209.149,437.453,216,425.211,216,412S209.149,386.547,196.696,377.524z"/>
|
||||
<path style="fill:#D0D7DA;" d="M260.695,377.524c-3.594-2.602-8.586-1.797-11.172,1.781c-2.594,3.578-1.797,8.578,1.781,11.172
|
||||
C259.492,396.414,264,404.055,264,412s-4.508,15.586-12.695,21.523C238.852,442.547,232,454.789,232,468
|
||||
s6.852,25.453,19.305,34.477c1.422,1.031,3.063,1.523,4.688,1.523c2.477,0,4.922-1.148,6.484-3.305
|
||||
c2.594-3.578,1.797-8.578-1.781-11.172C252.508,483.586,248,475.945,248,468s4.508-15.586,12.695-21.523
|
||||
C273.149,437.453,280,425.211,280,412S273.149,386.547,260.695,377.524z"/>
|
||||
<path style="fill:#D0D7DA;" d="M324.695,446.477C337.149,437.453,344,425.211,344,412s-6.852-25.453-19.305-34.477
|
||||
c-3.594-2.602-8.594-1.797-11.172,1.781c-2.594,3.578-1.797,8.578,1.781,11.172C323.492,396.414,328,404.055,328,412
|
||||
s-4.508,15.586-12.695,21.523C302.852,442.547,296,454.789,296,468s6.852,25.453,19.305,34.477
|
||||
c1.422,1.031,3.063,1.523,4.688,1.523c2.477,0,4.922-1.148,6.484-3.305c2.594-3.578,1.797-8.578-1.781-11.172
|
||||
C316.508,483.586,312,475.945,312,468S316.508,452.414,324.695,446.477z"/>
|
||||
</g>
|
||||
<path style="fill:#3F556B;" d="M48,304c-7.447,0-13.651-5.107-15.435-12c-0.332,1.286-0.565,2.61-0.565,4c0,8.844,7.164,16,16,16
|
||||
c8.836,0,16-7.156,16-16c0-1.39-0.233-2.714-0.565-4C61.651,298.893,55.447,304,48,304z"/>
|
||||
<path style="fill:#B1C3D0;" d="M64,288c0,8.844-7.164,16-16,16c-8.836,0-16-7.156-16-16c0-8.843,7.164-16,16-16
|
||||
C56.836,272,64,279.157,64,288z"/>
|
||||
<path style="fill:#3F556B;" d="M96,304c-7.447,0-13.651-5.107-15.435-12c-0.332,1.286-0.565,2.61-0.565,4c0,8.844,7.164,16,16,16
|
||||
c8.836,0,16-7.156,16-16c0-1.39-0.233-2.714-0.565-4C109.651,298.893,103.447,304,96,304z"/>
|
||||
<path style="fill:#B1C3D0;" d="M112,288c0,8.844-7.164,16-16,16c-8.836,0-16-7.156-16-16c0-8.843,7.164-16,16-16
|
||||
C104.836,272,112,279.157,112,288z"/>
|
||||
<rect x="184" y="280" style="fill:#3CB44A;" width="144" height="24"/>
|
||||
<path style="fill:#8EADBE;" d="M344,106.174V24c0-8.837-7.163-16-16-16h-16v104.446c0,4.968,1.157,9.867,3.378,14.311l35.255,70.51
|
||||
c7.979,15.958-3.625,34.733-21.466,34.733H49l-17,16h344h24h80L354.069,129.476C347.643,123.429,344,114.997,344,106.174z"/>
|
||||
<path style="fill:#3F556B;" d="M496,328c8.837,0,16-7.163,16-16v-48c-4.418,0-8,3.582-8,8v32c0,8.837-7.163,16-16,16H24
|
||||
c-4.418,0-8,3.582-8,8H496z"/>
|
||||
<rect x="184" y="272" style="fill:#0E9247;" width="144" height="8"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.8 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="220"><defs><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="3.5" y1="110.5" x2="147.5" y2="110.5"><stop offset="0" stop-color="#323232"/><stop offset=".357" stop-color="#FFF"/><stop offset=".571" stop-color="#919191"/><stop offset="1" stop-color="#4A4A4A"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="73.868" y1="3.277" x2="77.132" y2="217.723"><stop offset="0" stop-color="#5D5D5D"/><stop offset="1" stop-opacity=".959"/></linearGradient><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="3.5" y1="101.083" x2="147.5" y2="101.083"><stop offset="0" stop-color="#323232"/><stop offset=".357" stop-color="#FFF"/><stop offset=".571" stop-color="#919191"/><stop offset="1" stop-color="#4A4A4A"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="2.75" y1="110.5" x2="148.25" y2="110.5"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient><linearGradient id="e" gradientUnits="userSpaceOnUse" x1="219.5" y1="110" x2="223.5" y2="110"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient></defs><path d="M2.25 159.208c0 4.626 32.571 8.375 72.75 8.375s72.75-3.749 72.75-8.375v49.667c0 4.625-32.571 8.375-72.75 8.375s-72.75-3.75-72.75-8.375v-49.667z" fill="#3B2CD5"/><path d="M75 167.333c-40.179 0-72.75-3.749-72.75-8.375 0-4.625 32.571-8.375 72.75-8.375s72.75 3.75 72.75 8.375c0 4.626-32.571 8.375-72.75 8.375z" fill="#2193FF"/><path d="M75.5 20c-40.179 0-72.75-3.75-72.75-8.375S35.321 3.25 75.5 3.25s72.75 3.75 72.75 8.375S115.679 20 75.5 20zM75.5 217.75c-40.179 0-72.75-3.75-72.75-8.375S35.321 201 75.5 201s72.75 3.75 72.75 8.375-32.571 8.375-72.75 8.375zM2.75 208.604V12.396M148.25 209.375V11.625" stroke="#CDCDCD" fill="none"/><g><path d="M2.75 3.25h145.5v214.5H2.75V3.25z" fill="url(#d)"/><path d="M2.75 3.25h145.5v214.5H2.75V3.25z" stroke="#000" fill="none"/><g><path d="M219.5 108.5h4v3h-4v-3z" fill="url(#e)"/><path d="M219.5 108.5h4v3h-4v-3z" stroke="#000" fill="none"/></g></g></svg>
|
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB |
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 58 58" version="1.1"><!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch --><title>016 - Macerator</title><desc>Created with Sketch.</desc><defs/><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="016---Macerator" fill-rule="nonzero"><path d="M49,54 C48.4477153,54 48,53.5522847 48,53 L48,39 C48,37.3431458 49.3431458,36 51,36 L54,36 C54.5522847,36 55,36.4477153 55,37 C55,37.5522847 54.5522847,38 54,38 L51,38 C50.4477153,38 50,38.4477153 50,39 L50,53 C50,53.5522847 49.5522847,54 49,54 Z" id="Shape" fill="#606060"/><path d="M55.757,39.689 L53.757,39.189 C53.3120689,39.0775409 53.0000121,38.6776794 53,38.219 L53,35.781 C53.0000121,35.3223206 53.3120689,34.9224591 53.757,34.811 L55.757,34.311 C56.0557824,34.2361524 56.3723864,34.3031708 56.6152132,34.4926655 C56.8580399,34.6821602 56.9999919,34.9729853 57,35.281 L57,38.719 C56.9999919,39.0270147 56.8580399,39.3178398 56.6152132,39.5073345 C56.3723864,39.6968292 56.0557824,39.7638476 55.757,39.689 Z" id="Shape" fill="#285680"/><path d="M44,51 L50,51 L50,54 C50,54.5522847 49.5522847,55 49,55 L44,55 L44,51 Z" id="Shape" fill="#4482C3"/><path d="M40,50 L45,50 C45.5522847,50 46,50.4477153 46,51 L46,55 C46,55.5522847 45.5522847,56 45,56 L40,56 L40,50 Z" id="Shape" fill="#285680"/><polygon id="Shape" fill="#3B97D3" points="56 4 42 47 28.83 49 27.35 48.77 16 47 2 4 27.5 2.11 29 2"/><path d="M42,47 L42,57 C42,57.5522847 41.5522847,58 41,58 L17,58 C16.4477153,58 16,57.5522847 16,57 L16,47 L42,47 Z" id="Shape" fill="#BDC3C7"/><rect id="Rectangle-path" fill="#95A5A5" x="0" y="0" width="58" height="4" rx="1"/><rect id="Rectangle-path" fill="#BDC3C7" x="0" y="0" width="55" height="4" rx="1"/><path d="M20,44 L19,44 C18.4477153,44 18,43.5522847 18,43 C18,42.4477153 18.4477153,42 19,42 L20,42 C20.5522847,42 21,42.4477153 21,43 C21,43.5522847 20.5522847,44 20,44 Z" id="Shape" fill="#67B9CC"/><path d="M39,44 L24,44 C23.4477153,44 23,43.5522847 23,43 C23,42.4477153 23.4477153,42 24,42 L39,42 C39.5522847,42 40,42.4477153 40,43 C40,43.5522847 39.5522847,44 39,44 Z" id="Shape" fill="#67B9CC"/><path d="M39,52 L38,52 C37.4477153,52 37,51.5522847 37,51 C37,50.4477153 37.4477153,50 38,50 L39,50 C39.5522847,50 40,50.4477153 40,51 C40,51.5522847 39.5522847,52 39,52 Z" id="Shape" fill="#ECF0F1"/><path d="M34,52 L19,52 C18.4477153,52 18,51.5522847 18,51 C18,50.4477153 18.4477153,50 19,50 L34,50 C34.5522847,50 35,50.4477153 35,51 C35,51.5522847 34.5522847,52 34,52 Z" id="Shape" fill="#ECF0F1"/><path d="M10,9 L9,9 C8.44771525,9 8,8.55228475 8,8 C8,7.44771525 8.44771525,7 9,7 L10,7 C10.5522847,7 11,7.44771525 11,8 C11,8.55228475 10.5522847,9 10,9 Z" id="Shape" fill="#67B9CC"/><path d="M49,9 L14,9 C13.4477153,9 13,8.55228475 13,8 C13,7.44771525 13.4477153,7 14,7 L49,7 C49.5522847,7 50,7.44771525 50,8 C50,8.55228475 49.5522847,9 49,9 Z" id="Shape" fill="#67B9CC"/></g></g></svg>
|
Before Width: | Height: | Size: 3 KiB |
|
@ -1 +0,0 @@
|
|||
<svg id="Capa_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m76 15h120v241h-120z" fill="#47568c"/><path d="m338.192 420.751h-67.192-67.192l-36.065 69.247h103.257 103.257z" fill="#47568c"/><path d="m338.192 420.751h-67.192v69.247h103.257z" fill="#29376d"/><circle cx="271" cy="256" fill="#61729b" r="195"/><path d="m466 256c0-107.52-87.48-195-195-195v390c107.52 0 195-87.48 195-195z" fill="#47568c"/><circle cx="271" cy="256" fill="#47568c" r="165"/><path d="m436 256c0-90.981-74.019-165-165-165v330c90.981 0 165-74.019 165-165z" fill="#29376d"/><path d="m211 30h-150c-8.291 0-15-6.709-15-15s6.709-15 15-15h150c8.291 0 15 6.709 15 15s-6.709 15-15 15z" fill="#61729b"/><g><path d="m253.832 390.847c-10.474-1.523-21.548-4.482-31.275-8.789-16.699-8.438-26.118-22.837-26.543-39.844-.41-16.875 8.027-31.948 22.573-40.342l29.912-16.802c9.943-5.727 22.5 1.398 22.5 12.993v77.944c.001 8.948-7.874 16.141-17.167 14.84z" fill="#e6f2ff"/></g><g><path d="m288.168 121.153c10.474 1.523 21.548 4.482 31.275 8.789 16.699 8.438 26.118 22.837 26.543 39.844.41 16.875-8.027 31.948-22.573 40.342l-29.913 16.801c-9.943 5.727-22.5-1.398-22.5-12.993v-77.944c0-8.947 7.875-16.14 17.168-14.839z" fill="#cce6ff"/></g><g><path d="m379.769 338.292c-6.556 8.309-14.656 16.42-23.249 22.69-15.657 10.243-32.836 11.201-47.777 3.065-14.819-8.082-23.654-22.926-23.65-39.72l.405-34.306c.012-11.474 12.461-18.787 22.502-12.989l67.502 38.972c7.748 4.474 10.041 14.89 4.267 22.288z" fill="#cce6ff"/></g><g><path d="m162.231 173.708c6.556-8.309 14.656-16.42 23.249-22.69 15.657-10.243 32.836-11.201 47.777-3.065 14.819 8.082 23.654 22.926 23.65 39.72l-.405 34.306c-.012 11.474-12.461 18.787-22.502 12.989l-67.502-38.972c-7.748-4.474-10.041-14.89-4.267-22.288z" fill="#e6f2ff"/></g><g><path d="m396.365 203.444c3.917 9.832 6.892 20.903 8.026 31.479 1.043 18.681-6.718 34.037-21.234 42.909-14.409 8.793-31.682 9.022-46.224.622l-29.507-17.504c-9.931-5.747-10.039-20.185.002-25.982l67.502-38.972c7.748-4.474 17.915-1.251 21.435 7.448z" fill="#cce6ff"/></g><g><path d="m145.635 308.556c-3.917-9.832-6.892-20.902-8.026-31.479-1.043-18.681 6.718-34.037 21.234-42.909 14.409-8.793 31.682-9.022 46.224-.622l29.507 17.504c9.931 5.747 10.039 20.185-.002 25.982l-67.502 38.972c-7.748 4.474-17.915 1.251-21.435-7.448z" fill="#e6f2ff"/></g><path d="m391 482h-120-120c-8.291 0-15 6.709-15 15s6.709 15 15 15h120 120c8.291 0 15-6.709 15-15s-6.709-15-15-15z" fill="#61729b"/><path d="m406 497c0-8.291-6.709-15-15-15h-120v30h120c8.291 0 15-6.709 15-15z" fill="#47568c"/><circle cx="271" cy="256" fill="#9cf" r="45"/><path d="m316 256c0-24.814-20.186-45-45-45v90c24.814 0 45-20.186 45-45z" fill="#80aaff"/></g></svg>
|
Before Width: | Height: | Size: 2.7 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="220"><defs><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="3.5" y1="110.5" x2="147.5" y2="110.5"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-38.5" y1="-219.554" x2="-135" y2="-189.801" gradientTransform="rotate(180)"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="64" y1="211.25" x2="86.5" y2="211.25"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="73.868" y1="3.277" x2="77.132" y2="217.723"><stop offset="0" stop-color="#5D5D5D"/><stop offset="1" stop-opacity=".959"/></linearGradient><linearGradient id="e" gradientUnits="userSpaceOnUse" x1="3.5" y1="101.083" x2="147.5" y2="101.083"><stop offset="0" stop-color="#323232"/><stop offset=".357" stop-color="#FFF"/><stop offset=".571" stop-color="#919191"/><stop offset="1" stop-color="#4A4A4A"/></linearGradient><linearGradient id="f" gradientUnits="userSpaceOnUse" x1="6.25" y1="110" x2="144.75" y2="110"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient><linearGradient id="g" gradientUnits="userSpaceOnUse" x1="219.5" y1="110" x2="223.5" y2="110"><stop offset="0" stop-color="#232323"/><stop offset=".357" stop-color="#5B5B5B"/><stop offset=".571" stop-color="#474747"/><stop offset="1" stop-color="#282828"/></linearGradient></defs><path d="M135.5 3c6.274.18 10.586 4.113 11.848 10.254h.152v142.873l-36 29.307c-9.065 7.39-18.13 14.78-27.2 22.164V218H66.7v-10.672c-9.028-7.343-18.106-14.627-27.2-21.894l-36-29.307V13.254h.152C4.623 7.127 9.57 3.297 15.5 3h120z" fill="url(#a)"/><path d="M135.5 3c6.274.18 10.586 4.113 11.848 10.254h.152v142.873l-36 29.307c-9.065 7.39-18.13 14.78-27.2 22.164V218H66.7v-10.672c-9.028-7.343-18.106-14.627-27.2-21.894l-36-29.307V13.254h.152C4.623 7.127 9.57 3.297 15.5 3h120z" stroke="#272727" fill="none"/><path d="M147.5 154.579l-36 29.298-36 29.298-36-29.298-36-29.298h72z" fill="url(#b)"/><path d="M147.5 154.579l-36 29.298-36 29.298-36-29.298-36-29.298h72z" stroke="#272727" fill="none"/><g><path d="M64 204.5h22.5V218H64v-13.5z" fill="url(#c)"/><path d="M64 204.5h22.5V218H64v-13.5z" stroke="#272727" fill="none"/></g></svg>
|
Before Width: | Height: | Size: 2.7 KiB |
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"agitator": "YwGzXvWMpmbLb6XobesL8n",
|
||||
"heater": "EsmZwWi9Qp3bzmXqq7N3Ly",
|
||||
"id": "oHxKz3z5RjbsxfSz6KUgov",
|
||||
"name": "MashTun",
|
||||
"props": {
|
||||
"OffsetOff": "23",
|
||||
"OffsetOn": "22"
|
||||
},
|
||||
"sensor": "RedQfuxfy4mYe6PwioY95y",
|
||||
"state": {
|
||||
"running": false
|
||||
},
|
||||
"target_temp": 80,
|
||||
"type": "Hysteresis"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
cbpi4-ui-plugin:
|
||||
installation_date: '2021-01-06 16:03:31'
|
||||
version: '0.0.2'
|
||||
cbpi4-ui:
|
||||
installation_date: '2021-01-06 16:03:31'
|
||||
version: '0.0.1'
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "RedQfuxfy4mYe6PwioY95y",
|
||||
"name": "Test",
|
||||
"props": {
|
||||
"Interval": 5,
|
||||
"Sensor": "DEF"
|
||||
},
|
||||
"state": {
|
||||
"value": 0
|
||||
},
|
||||
"type": "OneWire"
|
||||
},
|
||||
{
|
||||
"id": "JUGteK9KrSVPDxboWjBS4N",
|
||||
"name": "Test2",
|
||||
"props": {},
|
||||
"state": {
|
||||
"value": 0
|
||||
},
|
||||
"type": "CustomSensor"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 1.9 MiB |
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"basic": {
|
||||
"name": "PALE ALE"
|
||||
},
|
||||
"profile": [
|
||||
{
|
||||
"id": "SeL6hT9WxvA5yTsTakZuu8",
|
||||
"name": "Pump Left",
|
||||
"props": {
|
||||
"Actor": "YwGzXvWMpmbLb6XobesL8n",
|
||||
"Timer": "5"
|
||||
},
|
||||
"status": "P",
|
||||
"type": "ActorStep"
|
||||
},
|
||||
{
|
||||
"id": "YwyRyzA2ePiiXXnET5gEeH",
|
||||
"name": "Pump Right",
|
||||
"props": {
|
||||
"Actor": "EsmZwWi9Qp3bzmXqq7N3Ly",
|
||||
"Kettle": "oHxKz3z5RjbsxfSz6KUgov",
|
||||
"Sensor": "JUGteK9KrSVPDxboWjBS4N",
|
||||
"Temp": "2",
|
||||
"Timer": "5"
|
||||
},
|
||||
"status": "I",
|
||||
"type": "ActorStep"
|
||||
}
|
||||
]
|
||||
}
|
208
sample.py
|
@ -0,0 +1,208 @@
|
|||
|
||||
|
||||
from abc import abstractmethod
|
||||
import asyncio
|
||||
from asyncio import tasks
|
||||
from cbpi.extension.mashstep import MyStep
|
||||
from cbpi.controller.step_controller import StepController
|
||||
from cbpi.extension.gpioactor import GPIOActor
|
||||
from cbpi.api.dataclasses import Actor, Props, Step
|
||||
from cbpi.controller.basic_controller2 import BasicController
|
||||
import time
|
||||
import math
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
async def main():
|
||||
cbpi = MagicMock()
|
||||
cbpi.sensor.get_value.return_value = 99
|
||||
app = MagicMock()
|
||||
|
||||
types = {"GPIOActor":{"name": "GPIOActor", "class": GPIOActor, "properties": [], "actions": []}}
|
||||
|
||||
|
||||
controller = StepController(cbpi)
|
||||
controller.types = types = {"MyStep":{"name": "MyStep", "class": MyStep, "properties": [], "actions": []}}
|
||||
|
||||
controller.load()
|
||||
await controller.stop()
|
||||
await controller.reset_all()
|
||||
|
||||
|
||||
await controller.start()
|
||||
|
||||
|
||||
#await controller.start()
|
||||
await asyncio.sleep(2)
|
||||
await controller.next()
|
||||
await asyncio.sleep(2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
'''
|
||||
class Timer(object):
|
||||
|
||||
def __init__(self, timeout, on_done = None, on_update = None) -> None:
|
||||
super().__init__()
|
||||
self.timeout = timeout
|
||||
self._timemout = self.timeout
|
||||
self._task = None
|
||||
self._callback = on_done
|
||||
self._update = on_update
|
||||
self.start_time = None
|
||||
|
||||
def done(self, task):
|
||||
if self._callback is not None:
|
||||
asyncio.create_task(self._callback(self))
|
||||
|
||||
async def _job(self):
|
||||
self.start_time = time.time()
|
||||
self.count = int(round(self._timemout, 0))
|
||||
try:
|
||||
for seconds in range(self.count, 0, -1):
|
||||
if self._update is not None:
|
||||
await self._update(self,seconds)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
end = time.time()
|
||||
duration = end - self.start_time
|
||||
self._timemout = self._timemout - duration
|
||||
|
||||
|
||||
def start(self):
|
||||
self._task = asyncio.create_task(self._job())
|
||||
self._task.add_done_callback(self.done)
|
||||
|
||||
async def stop(self):
|
||||
print(self._task.done())
|
||||
if self._task.done() is False:
|
||||
self._task.cancel()
|
||||
await self._task
|
||||
|
||||
def reset(self):
|
||||
if self.is_running is True:
|
||||
return
|
||||
self._timemout = self.timeout
|
||||
|
||||
def is_running(self):
|
||||
return not self._task.done()
|
||||
|
||||
def set_time(self,timeout):
|
||||
if self.is_running is True:
|
||||
return
|
||||
self.timeout = timeout
|
||||
|
||||
def get_time(self):
|
||||
return self.format_time(int(round(self._timemout,0)))
|
||||
|
||||
@classmethod
|
||||
def format_time(cls, time):
|
||||
pattern = '{0:02d}:{1:02d}:{2:02d}'
|
||||
seconds = time % 60
|
||||
minutes = math.floor(time / 60) % 60
|
||||
hours = math.floor(time / 3600)
|
||||
return pattern.format(hours, minutes, seconds)
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class StepResult(Enum):
|
||||
STOP=1
|
||||
NEXT=2
|
||||
DONE=3
|
||||
|
||||
class Step():
|
||||
|
||||
def __init__(self, name, props, on_done) -> None:
|
||||
self.name = name
|
||||
self.timer = None
|
||||
self._done_callback = on_done
|
||||
self.props = props
|
||||
self.cancel_reason: StepResult = None
|
||||
|
||||
def _done(self, task):
|
||||
print("HALLO")
|
||||
self._done_callback(self, task.result())
|
||||
|
||||
async def start(self):
|
||||
self.task = asyncio.create_task(self._run())
|
||||
self.task.add_done_callback(self._done)
|
||||
|
||||
async def next(self):
|
||||
self.cancel_reason = StepResult.NEXT
|
||||
self.task.cancel()
|
||||
await self.task
|
||||
|
||||
async def stop(self):
|
||||
self.cancel_reason = StepResult.STOP
|
||||
self.task.cancel()
|
||||
await self.task
|
||||
async def reset(self):
|
||||
pass
|
||||
|
||||
async def on_props_update(self, props):
|
||||
self.props = {**self.props, **props}
|
||||
|
||||
async def save_props(self, props):
|
||||
pass
|
||||
|
||||
async def push_state(self, msg):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
async def on_start(self):
|
||||
pass
|
||||
|
||||
async def on_stop(self):
|
||||
pass
|
||||
|
||||
async def _run(self):
|
||||
|
||||
try:
|
||||
await self.on_start()
|
||||
self.cancel_reason = await self.run()
|
||||
except asyncio.CancelledError as e:
|
||||
pass
|
||||
finally:
|
||||
await self.on_stop()
|
||||
return self.cancel_reason
|
||||
|
||||
@abstractmethod
|
||||
async def run(self):
|
||||
pass
|
||||
|
||||
|
||||
class MyStep(Step):
|
||||
|
||||
async def timer_update(self, timer, seconds):
|
||||
print(Timer.format_time(seconds))
|
||||
|
||||
async def timer_done(self, timer):
|
||||
print("TIMER DONE")
|
||||
await self.next()
|
||||
async def on_start(self):
|
||||
if self.timer is None:
|
||||
self.timer = Timer(20, on_done=self.timer_done, on_update=self.timer_update)
|
||||
self.timer.start()
|
||||
|
||||
async def on_stop(self):
|
||||
await self.timer.stop()
|
||||
|
||||
async def run(self):
|
||||
for i in range(10):
|
||||
print("RUNNING")
|
||||
await asyncio.sleep(1)
|
||||
await self.timer.stop()
|
||||
return StepResult.DONE
|
||||
|
||||
|
||||
'''
|