mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-09 17:07:43 +01:00
"recipe book added"
This commit is contained in:
parent
79fd2b8727
commit
a55ed2a5e5
21 changed files with 476 additions and 262 deletions
|
@ -1 +1 @@
|
||||||
__version__ = "4.0.0.26"
|
__version__ = "4.0.0.27"
|
|
@ -4,5 +4,8 @@ class ConfigType(Enum):
|
||||||
STRING = "string"
|
STRING = "string"
|
||||||
NUMBER = "number"
|
NUMBER = "number"
|
||||||
SELECT = "select"
|
SELECT = "select"
|
||||||
|
KETTLE = "kettle"
|
||||||
|
ACTOR = "actor"
|
||||||
|
SENSOR = "sensor"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ def create_home_folder_structure():
|
||||||
pathlib.Path(os.path.join(".", 'config')).mkdir(parents=True, exist_ok=True)
|
pathlib.Path(os.path.join(".", 'config')).mkdir(parents=True, exist_ok=True)
|
||||||
pathlib.Path(os.path.join(".", 'config/dashboard')).mkdir(parents=True, exist_ok=True)
|
pathlib.Path(os.path.join(".", 'config/dashboard')).mkdir(parents=True, exist_ok=True)
|
||||||
pathlib.Path(os.path.join(".", 'config/dashboard/widgets')).mkdir(parents=True, exist_ok=True)
|
pathlib.Path(os.path.join(".", 'config/dashboard/widgets')).mkdir(parents=True, exist_ok=True)
|
||||||
|
pathlib.Path(os.path.join(".", 'config/recipes')).mkdir(parents=True, exist_ok=True)
|
||||||
print("Folder created")
|
print("Folder created")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
{
|
{
|
||||||
|
"AUTHOR": {
|
||||||
|
"description": "Author",
|
||||||
|
"name": "AUTHOR",
|
||||||
|
"options": null,
|
||||||
|
"type": "string",
|
||||||
|
"value": "John Doe"
|
||||||
|
},
|
||||||
"BREWERY_NAME": {
|
"BREWERY_NAME": {
|
||||||
"description": "Brewery Name",
|
"description": "Brewery Name",
|
||||||
"name": "BREWERY_NAME",
|
"name": "BREWERY_NAME",
|
||||||
"options": null,
|
"options": null,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"value": "MANUL"
|
"value": "CraftBeerPi Brewery"
|
||||||
},
|
},
|
||||||
"NAME": {
|
"MASH_TUN": {
|
||||||
"description": "Brew Name",
|
"description": "Default Mash Tun",
|
||||||
"name": "NAME",
|
"name": "MASH_TUN",
|
||||||
"options": null,
|
"options": null,
|
||||||
"type": "string",
|
"type": "kettle",
|
||||||
"value": "HEDER"
|
"value": ""
|
||||||
},
|
},
|
||||||
"TEMP_UNIT": {
|
"TEMP_UNIT": {
|
||||||
"description": "Temperature Unit",
|
"description": "Temperature Unit",
|
||||||
|
@ -27,6 +34,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"value": "F"
|
"value": "C"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"basic": {
|
"basic": {
|
||||||
"name": ""
|
"name": ""
|
||||||
},
|
},
|
||||||
"profile": [
|
"steps": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -38,12 +38,10 @@ class LogController:
|
||||||
async def get_data(self, names, sample_rate='60s'):
|
async def get_data(self, names, sample_rate='60s'):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
:param names: name as string or list of names as string
|
:param names: name as string or list of names as string
|
||||||
:param sample_rate: rate for resampling the data
|
:param sample_rate: rate for resampling the data
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# make string to array
|
# make string to array
|
||||||
if isinstance(names, list) is False:
|
if isinstance(names, list) is False:
|
||||||
names = [names]
|
names = [names]
|
||||||
|
@ -87,14 +85,23 @@ class LogController:
|
||||||
|
|
||||||
if len(names) > 1:
|
if len(names) > 1:
|
||||||
for name in names:
|
for name in names:
|
||||||
|
|
||||||
data[name] = result[name].interpolate(limit_direction='both', limit=10).tolist()
|
data[name] = result[name].interpolate(limit_direction='both', limit=10).tolist()
|
||||||
else:
|
else:
|
||||||
data[name] = result.interpolate().tolist()
|
data[name] = result.interpolate().tolist()
|
||||||
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
async def get_data2(self, ids) -> dict:
|
||||||
|
def dateparse(time_in_secs):
|
||||||
|
return datetime.datetime.strptime(time_in_secs, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
result = dict()
|
||||||
|
for id in ids:
|
||||||
|
df = pd.read_csv("./logs/sensor_%s.log" % id, parse_dates=True, date_parser=dateparse, index_col='DateTime', names=['DateTime',"Values"], header=None)
|
||||||
|
result[id] = {"time": df.index.astype(str).tolist(), "value":df.Values.tolist()}
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_logfile_names(self, name:str ) -> list:
|
def get_logfile_names(self, name:str ) -> list:
|
||||||
'''
|
'''
|
||||||
|
@ -106,14 +113,9 @@ class LogController:
|
||||||
return [os.path.basename(x) for x in glob.glob('./logs/sensor_%s.log*' % name)]
|
return [os.path.basename(x) for x in glob.glob('./logs/sensor_%s.log*' % name)]
|
||||||
|
|
||||||
def clear_log(self, name:str ) -> str:
|
def clear_log(self, name:str ) -> str:
|
||||||
'''
|
|
||||||
|
|
||||||
:param name: log name as string. pattern /logs/sensor_%s.log*
|
|
||||||
:return: None
|
|
||||||
'''
|
|
||||||
all_filenames = glob.glob('./logs/sensor_%s.log*' % name)
|
all_filenames = glob.glob('./logs/sensor_%s.log*' % name)
|
||||||
for f in all_filenames:
|
for f in all_filenames:
|
||||||
|
|
||||||
os.remove(f)
|
os.remove(f)
|
||||||
|
|
||||||
if name in self.datalogger:
|
if name in self.datalogger:
|
||||||
|
|
85
cbpi/controller/recipe_controller.py
Normal file
85
cbpi/controller/recipe_controller.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os.path
|
||||||
|
from os import listdir
|
||||||
|
from os.path import isfile, join
|
||||||
|
import json
|
||||||
|
import shortuuid
|
||||||
|
import yaml
|
||||||
|
from ..api.step import StepMove, StepResult, StepState
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
class RecipeController:
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, cbpi):
|
||||||
|
self.cbpi = cbpi
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def urlify(self, s):
|
||||||
|
|
||||||
|
# Remove all non-word characters (everything except numbers and letters)
|
||||||
|
s = re.sub(r"[^\w\s]", '', s)
|
||||||
|
|
||||||
|
# Replace all runs of whitespace with a single dash
|
||||||
|
s = re.sub(r"\s+", '-', s)
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
async def create(self, name):
|
||||||
|
id = shortuuid.uuid()
|
||||||
|
path = os.path.join(".", 'config', "recipes", "{}.yaml".format(id))
|
||||||
|
data = dict(basic=dict(name=name, author=self.cbpi.config.get("AUTHOR", "John Doe")), steps=[])
|
||||||
|
with open(path, "w") as file:
|
||||||
|
yaml.dump(data, file)
|
||||||
|
return id
|
||||||
|
|
||||||
|
async def save(self, name, data):
|
||||||
|
path = os.path.join(".", 'config', "recipes", "{}.yaml".format(name))
|
||||||
|
with open(path, "w") as file:
|
||||||
|
yaml.dump(data, file, indent=4, sort_keys=True)
|
||||||
|
self.cbpi.notify("{} saved".format(data["basic"].get("name")))
|
||||||
|
|
||||||
|
async def get_recipes(self):
|
||||||
|
path = os.path.join(".", 'config', "recipes")
|
||||||
|
onlyfiles = [os.path.splitext(f)[0] for f in listdir(path) if isfile(join(path, f)) and f.endswith(".yaml")]
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for filename in onlyfiles:
|
||||||
|
recipe_path = os.path.join(".", 'config', "recipes", "%s.yaml" % filename)
|
||||||
|
with open(recipe_path) as file:
|
||||||
|
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||||
|
dataset = data["basic"]
|
||||||
|
dataset["file"] = filename
|
||||||
|
result.append(dataset)
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def get_by_name(self, name):
|
||||||
|
|
||||||
|
recipe_path = os.path.join(".", 'config', "recipes", "%s.yaml" % name)
|
||||||
|
with open(recipe_path) as file:
|
||||||
|
return yaml.load(file, Loader=yaml.FullLoader)
|
||||||
|
|
||||||
|
|
||||||
|
async def remove(self, name):
|
||||||
|
path = os.path.join(".", 'config', "recipes", "{}.yaml".format(name))
|
||||||
|
os.remove(path)
|
||||||
|
self.cbpi.notify("{} delted".format(name))
|
||||||
|
|
||||||
|
async def brew(self, name):
|
||||||
|
|
||||||
|
recipe_path = os.path.join(".", 'config', "recipes", "%s.yaml" % name)
|
||||||
|
with open(recipe_path) as file:
|
||||||
|
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||||
|
await self.cbpi.step.load_recipe(data)
|
||||||
|
|
||||||
|
async def clone(self, id, new_name):
|
||||||
|
recipe_path = os.path.join(".", 'config', "recipes", "%s.yaml" % id)
|
||||||
|
with open(recipe_path) as file:
|
||||||
|
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||||
|
data["basic"]["name"] = new_name
|
||||||
|
new_id = shortuuid.uuid()
|
||||||
|
await self.save(new_id, data)
|
||||||
|
|
||||||
|
return new_id
|
|
@ -3,7 +3,8 @@ import copy
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
from os import listdir
|
||||||
|
from os.path import isfile, join
|
||||||
import shortuuid
|
import shortuuid
|
||||||
from cbpi.api.dataclasses import Props, Step
|
from cbpi.api.dataclasses import Props, Step
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
@ -54,13 +55,14 @@ class StepController:
|
||||||
# create file if not exists
|
# create file if not exists
|
||||||
if os.path.exists(self.path) is False:
|
if os.path.exists(self.path) is False:
|
||||||
with open(self.path, "w") as file:
|
with open(self.path, "w") as file:
|
||||||
json.dump(dict(basic={}, profile=[]), file, indent=4, sort_keys=True)
|
json.dump(dict(basic={}, steps=[]), file, indent=4, sort_keys=True)
|
||||||
|
|
||||||
#load from json file
|
#load from json file
|
||||||
with open(self.path) as json_file:
|
with open(self.path) as json_file:
|
||||||
data = json.load(json_file)
|
data = json.load(json_file)
|
||||||
self.basic_data = data["basic"]
|
self.basic_data = data["basic"]
|
||||||
self.profile = data["profile"]
|
self.profile = data["steps"]
|
||||||
|
|
||||||
|
|
||||||
# Start step after start up
|
# Start step after start up
|
||||||
self.profile = list(map(lambda item: self.create(item), self.profile))
|
self.profile = list(map(lambda item: self.create(item), self.profile))
|
||||||
|
@ -73,7 +75,6 @@ class StepController:
|
||||||
logging.debug("Add step")
|
logging.debug("Add step")
|
||||||
item.id = shortuuid.uuid()
|
item.id = shortuuid.uuid()
|
||||||
item.status = StepState.INITIAL
|
item.status = StepState.INITIAL
|
||||||
print(item)
|
|
||||||
try:
|
try:
|
||||||
type_cfg = self.types.get(item.type)
|
type_cfg = self.types.get(item.type)
|
||||||
clazz = type_cfg.get("class")
|
clazz = type_cfg.get("class")
|
||||||
|
@ -104,7 +105,7 @@ class StepController:
|
||||||
|
|
||||||
async def save(self):
|
async def save(self):
|
||||||
logging.debug("save profile")
|
logging.debug("save profile")
|
||||||
data = dict(basic=self.basic_data, profile=list(map(lambda item: item.to_dict(), self.profile)))
|
data = dict(basic=self.basic_data, steps=list(map(lambda item: item.to_dict(), self.profile)))
|
||||||
with open(self.path, "w") as file:
|
with open(self.path, "w") as file:
|
||||||
json.dump(data, file, indent=4, sort_keys=True)
|
json.dump(data, file, indent=4, sort_keys=True)
|
||||||
self.push_udpate()
|
self.push_udpate()
|
||||||
|
@ -132,6 +133,10 @@ class StepController:
|
||||||
self.cbpi.notify(message="BREWING COMPLETE")
|
self.cbpi.notify(message="BREWING COMPLETE")
|
||||||
logging.info("BREWING COMPLETE")
|
logging.info("BREWING COMPLETE")
|
||||||
|
|
||||||
|
async def previous(self):
|
||||||
|
logging.info("Trigger Next")
|
||||||
|
|
||||||
|
|
||||||
async def next(self):
|
async def next(self):
|
||||||
logging.info("Trigger Next")
|
logging.info("Trigger Next")
|
||||||
step = self.find_by_status(StepState.ACTIVE)
|
step = self.find_by_status(StepState.ACTIVE)
|
||||||
|
@ -190,7 +195,7 @@ class StepController:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
return {"basic": self.basic_data, "profile": list(map(lambda item: item.to_dict(), self.profile)), "types":self.get_types()}
|
return {"basic": self.basic_data, "steps": list(map(lambda item: item.to_dict(), self.profile)), "types":self.get_types()}
|
||||||
|
|
||||||
async def move(self, id, direction: StepMove):
|
async def move(self, id, direction: StepMove):
|
||||||
index = self.get_index_by_id(id)
|
index = self.get_index_by_id(id)
|
||||||
|
@ -215,7 +220,7 @@ class StepController:
|
||||||
self.profile = list(filter(lambda item: item.id != id, self.profile))
|
self.profile = list(filter(lambda item: item.id != id, self.profile))
|
||||||
await self.save()
|
await self.save()
|
||||||
|
|
||||||
async def shutdown(self, app):
|
async def shutdown(self, app=None):
|
||||||
logging.info("Mash Profile Shutdonw")
|
logging.info("Mash Profile Shutdonw")
|
||||||
for p in self.profile:
|
for p in self.profile:
|
||||||
instance = p.instance
|
instance = p.instance
|
||||||
|
@ -225,6 +230,7 @@ class StepController:
|
||||||
await instance.stop()
|
await instance.stop()
|
||||||
await instance.task
|
await instance.task
|
||||||
await self.save()
|
await self.save()
|
||||||
|
self.push_udpate()
|
||||||
|
|
||||||
def done(self, step, result):
|
def done(self, step, result):
|
||||||
if result == StepResult.NEXT:
|
if result == StepResult.NEXT:
|
||||||
|
@ -245,8 +251,11 @@ class StepController:
|
||||||
def get_index_by_id(self, id):
|
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):
|
def push_udpate(self, complete=False):
|
||||||
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda item: item.to_dict(), self.profile))))
|
if complete is True:
|
||||||
|
self.cbpi.ws.send(dict(topic="mash_profile_update", data=self.get_state()))
|
||||||
|
else:
|
||||||
|
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda item: item.to_dict(), self.profile))))
|
||||||
|
|
||||||
async def start_step(self,step):
|
async def start_step(self,step):
|
||||||
try:
|
try:
|
||||||
|
@ -269,3 +278,31 @@ class StepController:
|
||||||
await item.instance.__getattribute__(action)(**parameter)
|
await item.instance.__getattribute__(action)(**parameter)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Step Controller -Faild to call action on {} {} {}".format(id, action, e))
|
logging.error("Step Controller -Faild to call action on {} {} {}".format(id, action, e))
|
||||||
|
|
||||||
|
async def load_recipe(self, data):
|
||||||
|
try:
|
||||||
|
await self.shutdown()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
def add_runtime_data(item):
|
||||||
|
item["status"] = "I"
|
||||||
|
item["id"] = shortuuid.uuid()
|
||||||
|
list(map(lambda item: add_runtime_data(item), data.get("steps")))
|
||||||
|
with open(self.path, "w") as file:
|
||||||
|
json.dump(data, file, indent=4, sort_keys=True)
|
||||||
|
self.load()
|
||||||
|
self.push_udpate(complete=True)
|
||||||
|
|
||||||
|
async def clear(self):
|
||||||
|
try:
|
||||||
|
await self.shutdown()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
data = dict(basic=dict(), steps=[])
|
||||||
|
with open(self.path, "w") as file:
|
||||||
|
json.dump(data, file, indent=4, sort_keys=True)
|
||||||
|
|
||||||
|
self.load()
|
||||||
|
self.push_udpate(complete=True)
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ from cbpi.controller.kettle_controller import KettleController
|
||||||
from cbpi.controller.plugin_controller import PluginController
|
from cbpi.controller.plugin_controller import PluginController
|
||||||
from cbpi.controller.sensor_controller import SensorController
|
from cbpi.controller.sensor_controller import SensorController
|
||||||
from cbpi.controller.step_controller import StepController
|
from cbpi.controller.step_controller import StepController
|
||||||
|
from cbpi.controller.recipe_controller import RecipeController
|
||||||
from cbpi.controller.system_controller import SystemController
|
from cbpi.controller.system_controller import SystemController
|
||||||
|
|
||||||
from cbpi.controller.log_file_controller import LogController
|
from cbpi.controller.log_file_controller import LogController
|
||||||
|
@ -35,7 +35,7 @@ from cbpi.http_endpoints.http_dashboard import DashBoardHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_kettle import KettleHttpEndpoints
|
from cbpi.http_endpoints.http_kettle import KettleHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_sensor import SensorHttpEndpoints
|
from cbpi.http_endpoints.http_sensor import SensorHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_step import StepHttpEndpoints
|
from cbpi.http_endpoints.http_step import StepHttpEndpoints
|
||||||
|
from cbpi.http_endpoints.http_recipe import RecipeHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_plugin import PluginHttpEndpoints
|
from cbpi.http_endpoints.http_plugin import PluginHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_system import SystemHttpEndpoints
|
from cbpi.http_endpoints.http_system import SystemHttpEndpoints
|
||||||
from cbpi.http_endpoints.http_log import LogHttpEndpoints
|
from cbpi.http_endpoints.http_log import LogHttpEndpoints
|
||||||
|
@ -97,10 +97,12 @@ class CraftBeerPi:
|
||||||
self.system = SystemController(self)
|
self.system = SystemController(self)
|
||||||
self.kettle = KettleController(self)
|
self.kettle = KettleController(self)
|
||||||
self.step : StepController = StepController(self)
|
self.step : StepController = StepController(self)
|
||||||
|
self.recipe : RecipeController = RecipeController(self)
|
||||||
#self.satellite: SatelliteController = SatelliteController(self)
|
#self.satellite: SatelliteController = SatelliteController(self)
|
||||||
self.dashboard = DashboardController(self)
|
self.dashboard = DashboardController(self)
|
||||||
|
|
||||||
self.http_step = StepHttpEndpoints(self)
|
self.http_step = StepHttpEndpoints(self)
|
||||||
|
self.http_recipe = RecipeHttpEndpoints(self)
|
||||||
self.http_sensor = SensorHttpEndpoints(self)
|
self.http_sensor = SensorHttpEndpoints(self)
|
||||||
self.http_config = ConfigHttpEndpoints(self)
|
self.http_config = ConfigHttpEndpoints(self)
|
||||||
self.http_actor = ActorHttpEndpoints(self)
|
self.http_actor = ActorHttpEndpoints(self)
|
||||||
|
|
|
@ -13,18 +13,12 @@ class CustomSensor(CBPiSensor):
|
||||||
def __init__(self, cbpi, id, props):
|
def __init__(self, cbpi, id, props):
|
||||||
super(CustomSensor, self).__init__(cbpi, id, props)
|
super(CustomSensor, self).__init__(cbpi, id, props)
|
||||||
self.value = 0
|
self.value = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
|
|
||||||
while self.running is True:
|
while self.running is True:
|
||||||
|
|
||||||
self.value = random.randint(0,10)
|
self.value = random.randint(0,10)
|
||||||
self.push_update(self.value)
|
self.push_update(self.value)
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
return dict(value=self.value)
|
return dict(value=self.value)
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,16 @@ 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")])
|
@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):
|
class GPIOActor(CBPiActor):
|
||||||
|
|
||||||
|
@action(key="Cusotm Action", parameters=[Property.Number("Value", configurable=True), Property.Kettle("Kettle")])
|
||||||
|
async def custom_action(self, **kwargs):
|
||||||
|
print("ACTION", kwargs)
|
||||||
|
self.cbpi.notify("ACTION CALLED")
|
||||||
|
|
||||||
|
@action(key="Cusotm Action2", parameters=[Property.Number("Value", configurable=True)])
|
||||||
|
async def custom_action2(self, **kwargs):
|
||||||
|
print("ACTION2")
|
||||||
|
self.cbpi.notify("ACTION CALLED")
|
||||||
|
|
||||||
def get_GPIO_state(self, state):
|
def get_GPIO_state(self, state):
|
||||||
# ON
|
# ON
|
||||||
if state == 1:
|
if state == 1:
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from cbpi.api.step import CBPiStep, StepResult
|
from cbpi.api.step import CBPiStep, StepResult
|
||||||
from cbpi.api.timer import Timer
|
from cbpi.api.timer import Timer
|
||||||
|
|
||||||
from cbpi.api import *
|
from cbpi.api import *
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -12,13 +11,17 @@ import logging
|
||||||
Property.Kettle(label="Kettle")])
|
Property.Kettle(label="Kettle")])
|
||||||
class MashStep(CBPiStep):
|
class MashStep(CBPiStep):
|
||||||
|
|
||||||
@action(key="Custom Step Action", parameters=[])
|
@action(key="Custom RESET", parameters=[])
|
||||||
async def hello(self, **kwargs):
|
async def custom_reset(self, **kwargs):
|
||||||
print("ACTION")
|
self.summary = ""
|
||||||
|
await self.push_update()
|
||||||
|
|
||||||
@action(key="Custom Step Action 2", parameters=[])
|
|
||||||
async def hello2(self, **kwargs):
|
@action(key="Custom Action", parameters=[Property.Number(label="Value", configurable=True)])
|
||||||
print("ACTION2")
|
async def custom_action(self, Value, **kwargs):
|
||||||
|
self.summary = "VALUE FROM ACTION {}".format(Value)
|
||||||
|
await self.push_update()
|
||||||
|
self.cbpi.notify("ACTION 2 CALLED".format(Value))
|
||||||
|
|
||||||
async def on_timer_done(self,timer):
|
async def on_timer_done(self,timer):
|
||||||
self.summary = ""
|
self.summary = ""
|
||||||
|
@ -40,6 +43,7 @@ class MashStep(CBPiStep):
|
||||||
await self.push_update()
|
await self.push_update()
|
||||||
|
|
||||||
async def reset(self):
|
async def reset(self):
|
||||||
|
self.summary = ""
|
||||||
self.timer = Timer(int(self.props.Timer) *60 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
|
self.timer = Timer(int(self.props.Timer) *60 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
|
@ -56,10 +60,12 @@ class WaitStep(CBPiStep):
|
||||||
@action(key="Custom Step Action", parameters=[])
|
@action(key="Custom Step Action", parameters=[])
|
||||||
async def hello(self, **kwargs):
|
async def hello(self, **kwargs):
|
||||||
print("ACTION")
|
print("ACTION")
|
||||||
|
self.cbpi.notify("ACTION 1 CALLED")
|
||||||
|
|
||||||
@action(key="Custom Step Action 2", parameters=[])
|
@action(key="Custom Step Action 2", parameters=[])
|
||||||
async def hello2(self, **kwargs):
|
async def hello2(self, **kwargs):
|
||||||
print("ACTION2")
|
print("ACTION2")
|
||||||
|
self.cbpi.notify("ACTION 2 CALLED")
|
||||||
|
|
||||||
async def on_timer_done(self,timer):
|
async def on_timer_done(self,timer):
|
||||||
self.summary = ""
|
self.summary = ""
|
||||||
|
|
|
@ -7,7 +7,7 @@ from aiohttp import web
|
||||||
from cbpi.api import *
|
from cbpi.api import *
|
||||||
import os, re, threading, time
|
import os, re, threading, time
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
|
import random
|
||||||
|
|
||||||
def getSensors():
|
def getSensors():
|
||||||
try:
|
try:
|
||||||
|
@ -56,7 +56,7 @@ class OneWire(CBPiSensor):
|
||||||
|
|
||||||
def __init__(self, cbpi, id, props):
|
def __init__(self, cbpi, id, props):
|
||||||
super(OneWire, self).__init__(cbpi, id, props)
|
super(OneWire, self).__init__(cbpi, id, props)
|
||||||
self.value = 0
|
self.value = 200
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
await super().start()
|
await super().start()
|
||||||
|
@ -79,6 +79,7 @@ class OneWire(CBPiSensor):
|
||||||
async def run(self):
|
async def run(self):
|
||||||
while True:
|
while True:
|
||||||
self.value = self.t.value
|
self.value = self.t.value
|
||||||
|
|
||||||
self.log_data(self.value)
|
self.log_data(self.value)
|
||||||
self.push_update(self.value)
|
self.push_update(self.value)
|
||||||
await asyncio.sleep(self.interval)
|
await asyncio.sleep(self.interval)
|
||||||
|
|
|
@ -206,6 +206,7 @@ class ActorHttpEndpoints():
|
||||||
"""
|
"""
|
||||||
actor_id = request.match_info['id']
|
actor_id = request.match_info['id']
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
await self.controller.call_action(actor_id, data.get("name"), data.get("parameter"))
|
print(data)
|
||||||
|
await self.controller.call_action(actor_id, data.get("action"), data.get("parameter"))
|
||||||
|
|
||||||
return web.Response(status=204)
|
return web.Response(status=204)
|
|
@ -1,7 +1,8 @@
|
||||||
|
from cbpi.utils.encoder import ComplexEncoder
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from cbpi.utils.utils import json_dumps
|
from cbpi.utils.utils import json_dumps
|
||||||
from cbpi.api import request_mapping
|
from cbpi.api import request_mapping
|
||||||
|
import json
|
||||||
class LogHttpEndpoints:
|
class LogHttpEndpoints:
|
||||||
|
|
||||||
def __init__(self,cbpi):
|
def __init__(self,cbpi):
|
||||||
|
@ -140,7 +141,7 @@ class LogHttpEndpoints:
|
||||||
return web.json_response(data, dumps=json_dumps)
|
return web.json_response(data, dumps=json_dumps)
|
||||||
|
|
||||||
@request_mapping(path="/{name}", method="GET", auth_required=False)
|
@request_mapping(path="/{name}", method="GET", auth_required=False)
|
||||||
async def delete_log(self, request):
|
async def get_log(self, request):
|
||||||
"""
|
"""
|
||||||
---
|
---
|
||||||
description: delete log data for sensor
|
description: delete log data for sensor
|
||||||
|
@ -163,19 +164,82 @@ class LogHttpEndpoints:
|
||||||
data = await self.cbpi.log.get_data(log_name)
|
data = await self.cbpi.log.get_data(log_name)
|
||||||
return web.json_response(data, dumps=json_dumps)
|
return web.json_response(data, dumps=json_dumps)
|
||||||
|
|
||||||
|
|
||||||
|
@request_mapping(path="/", method="POST", auth_required=False)
|
||||||
|
async def get_log2(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: delete log data for sensor
|
||||||
|
tags:
|
||||||
|
- Log
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Sensor Ids
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation.
|
||||||
|
"""
|
||||||
|
data = await request.json()
|
||||||
|
print(data)
|
||||||
|
return web.json_response(await self.cbpi.log.get_data2(data), dumps=json_dumps)
|
||||||
|
|
||||||
|
|
||||||
@request_mapping(path="/{name}", method="DELETE", auth_required=False)
|
@request_mapping(path="/{name}", method="DELETE", auth_required=False)
|
||||||
async def delete_all_logs(self, request):
|
async def clear_log(self, request):
|
||||||
"""
|
"""
|
||||||
---
|
---
|
||||||
description: Get log data for sensor
|
description: Get log data for sensor
|
||||||
tags:
|
tags:
|
||||||
- Log
|
- Log
|
||||||
parameters:
|
parameters:
|
||||||
|
- name: "name"
|
||||||
|
in: "path"
|
||||||
|
description: "Sensor ID"
|
||||||
|
required: true
|
||||||
|
type: "integer"
|
||||||
|
format: "int64"
|
||||||
responses:
|
responses:
|
||||||
"204":
|
"204":
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
"""
|
"""
|
||||||
log_name = request.match_info['name']
|
log_name = request.match_info['name']
|
||||||
await self.cbpi.log.clear_logs(log_name)
|
self.cbpi.log.clear_log(log_name)
|
||||||
return web.Response(status=204)
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
@request_mapping(path="/logs", method="POST", auth_required=False)
|
||||||
|
async def get_logs(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Get Logs
|
||||||
|
tags:
|
||||||
|
- Log
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Sensor Ids
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation.
|
||||||
|
"""
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
result = await self.cbpi.log.get_data(data)
|
||||||
|
print("JSON")
|
||||||
|
print(json.dumps(result, cls=ComplexEncoder))
|
||||||
|
print("JSON----")
|
||||||
|
return web.json_response(result, dumps=json_dumps)
|
170
cbpi/http_endpoints/http_recipe.py
Normal file
170
cbpi/http_endpoints/http_recipe.py
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
from cbpi.controller.recipe_controller import RecipeController
|
||||||
|
from cbpi.api.dataclasses import Props, Step
|
||||||
|
from aiohttp import web
|
||||||
|
from cbpi.api import *
|
||||||
|
|
||||||
|
class RecipeHttpEndpoints():
|
||||||
|
|
||||||
|
def __init__(self, cbpi):
|
||||||
|
self.cbpi = cbpi
|
||||||
|
self.controller : RecipeController = cbpi.recipe
|
||||||
|
self.cbpi.register(self, "/recipe")
|
||||||
|
|
||||||
|
@request_mapping(path="/", method="GET", auth_required=False)
|
||||||
|
async def http_get_all(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Get all recipes
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
return web.json_response(await self.controller.get_recipes())
|
||||||
|
|
||||||
|
@request_mapping(path="/{name}", method="GET", auth_required=False)
|
||||||
|
async def get_by_name(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Get all recipes
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
parameters:
|
||||||
|
- name: "name"
|
||||||
|
in: "path"
|
||||||
|
description: "Recipe Name"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
name = request.match_info['name']
|
||||||
|
return web.json_response(await self.controller.get_by_name(name))
|
||||||
|
|
||||||
|
@request_mapping(path="/create", method="POST", auth_required=False)
|
||||||
|
async def http_create(self, request):
|
||||||
|
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Add Recipe
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
data = await request.json()
|
||||||
|
print(data)
|
||||||
|
return web.json_response(dict(id=await self.controller.create(data.get("name"))))
|
||||||
|
|
||||||
|
|
||||||
|
@request_mapping(path="/{name}", method="PUT", auth_required=False)
|
||||||
|
async def http_save(self, request):
|
||||||
|
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Save Recipe
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Recipe Id"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Recipe Data
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
data = await request.json()
|
||||||
|
name = request.match_info['name']
|
||||||
|
await self.controller.save(name, data)
|
||||||
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
@request_mapping(path="/{name}", method="DELETE", auth_required=False)
|
||||||
|
async def http_remove(self, request):
|
||||||
|
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Delete
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Recipe Id"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
name = request.match_info['name']
|
||||||
|
await self.controller.remove(name)
|
||||||
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
@request_mapping(path="/{name}/brew", method="POST", auth_required=False)
|
||||||
|
async def http_brew(self, request):
|
||||||
|
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Brew
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
parameters:
|
||||||
|
- name: "name"
|
||||||
|
in: "path"
|
||||||
|
description: "Recipe Id"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
|
||||||
|
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
name = request.match_info['name']
|
||||||
|
await self.controller.brew(name)
|
||||||
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
@request_mapping(path="/{id}/clone", method="POST", auth_required=False)
|
||||||
|
async def http_clone(self, request):
|
||||||
|
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Brew
|
||||||
|
tags:
|
||||||
|
- Recipe
|
||||||
|
parameters:
|
||||||
|
- name: "id"
|
||||||
|
in: "path"
|
||||||
|
description: "Recipe Id"
|
||||||
|
required: true
|
||||||
|
type: "string"
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Recipe Data
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
id = request.match_info['id']
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
return web.json_response(dict(id=await self.controller.clone(id, data.get("name"))))
|
||||||
|
|
|
@ -253,5 +253,25 @@ class StepHttpEndpoints():
|
||||||
await self.controller.call_action(id,data.get("action"), data.get("parameter",[]))
|
await self.controller.call_action(id,data.get("action"), data.get("parameter",[]))
|
||||||
return web.Response(status=204)
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
@request_mapping(path="/clear", method="POST", auth_required=False)
|
||||||
|
async def http_clear(self, request):
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Clear ALll
|
||||||
|
tags:
|
||||||
|
- Step
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
|
||||||
|
await self.controller.clear()
|
||||||
|
return web.Response(status=204)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
|
|
||||||
|
from pandas import Timestamp
|
||||||
|
|
||||||
class ComplexEncoder(JSONEncoder):
|
class ComplexEncoder(JSONEncoder):
|
||||||
|
|
||||||
|
@ -11,7 +12,11 @@ class ComplexEncoder(JSONEncoder):
|
||||||
return obj.to_json()
|
return obj.to_json()
|
||||||
elif isinstance(obj, datetime.datetime):
|
elif isinstance(obj, datetime.datetime):
|
||||||
return obj.__str__()
|
return obj.__str__()
|
||||||
|
elif isinstance(obj, Timestamp):
|
||||||
|
print("TIMe")
|
||||||
|
return obj.__str__()
|
||||||
else:
|
else:
|
||||||
|
print(type(obj))
|
||||||
raise TypeError()
|
raise TypeError()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
aiohttp==3.7.3
|
aiohttp==3.7.4
|
||||||
aiohttp-auth==0.1.1
|
aiohttp-auth==0.1.1
|
||||||
aiohttp-route-decorator==0.1.4
|
aiohttp-route-decorator==0.1.4
|
||||||
aiohttp-security==0.4.0
|
aiohttp-security==0.4.0
|
||||||
|
|
216
sample.py
216
sample.py
|
@ -1,208 +1,14 @@
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import datetime
|
||||||
|
|
||||||
from abc import abstractmethod
|
def dateparse(time_in_secs):
|
||||||
import asyncio
|
'''
|
||||||
from asyncio import tasks
|
Internal helper for date parsing
|
||||||
from cbpi.extension.mashstep import MyStep
|
:param time_in_secs:
|
||||||
from cbpi.controller.step_controller import StepController
|
:return:
|
||||||
from cbpi.extension.gpioactor import GPIOActor
|
'''
|
||||||
from cbpi.api.dataclasses import Actor, Props, Step
|
return datetime.datetime.strptime(time_in_secs, '%Y-%m-%d %H:%M:%S')
|
||||||
from cbpi.controller.basic_controller2 import BasicController
|
|
||||||
import time
|
|
||||||
import math
|
|
||||||
import json
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
from unittest.mock import MagicMock, patch
|
df = pd.read_csv("./logs/sensor_JUGteK9KrSVPDxboWjBS4N.log", parse_dates=True, date_parser=dateparse, index_col='DateTime', names=['DateTime',"Values"], header=None)
|
||||||
|
print({"JUGteK9KrSVPDxboWjBS4N": {"time": df.index.astype(str).tolist(), "value":df.Values.tolist()}})
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -15,7 +15,7 @@ setup(name='cbpi',
|
||||||
'cbpi': ['*','*.txt', '*.rst', '*.yaml']},
|
'cbpi': ['*','*.txt', '*.rst', '*.yaml']},
|
||||||
|
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"aiohttp==3.7.3",
|
"aiohttp==3.7.4",
|
||||||
"aiohttp-auth==0.1.1",
|
"aiohttp-auth==0.1.1",
|
||||||
"aiohttp-route-decorator==0.1.4",
|
"aiohttp-route-decorator==0.1.4",
|
||||||
"aiohttp-security==0.4.0",
|
"aiohttp-security==0.4.0",
|
||||||
|
|
Loading…
Reference in a new issue