"recipe book added"

This commit is contained in:
Manuel Fritsch 2021-02-27 20:09:19 +01:00
parent 79fd2b8727
commit a55ed2a5e5
21 changed files with 476 additions and 262 deletions

View file

@ -1 +1 @@
__version__ = "4.0.0.26"
__version__ = "4.0.0.27"

View file

@ -4,5 +4,8 @@ class ConfigType(Enum):
STRING = "string"
NUMBER = "number"
SELECT = "select"
KETTLE = "kettle"
ACTOR = "actor"
SENSOR = "sensor"

View file

@ -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/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/recipes')).mkdir(parents=True, exist_ok=True)
print("Folder created")

View file

@ -1,17 +1,24 @@
{
"AUTHOR": {
"description": "Author",
"name": "AUTHOR",
"options": null,
"type": "string",
"value": "John Doe"
},
"BREWERY_NAME": {
"description": "Brewery Name",
"name": "BREWERY_NAME",
"options": null,
"type": "string",
"value": "MANUL"
"value": "CraftBeerPi Brewery"
},
"NAME": {
"description": "Brew Name",
"name": "NAME",
"MASH_TUN": {
"description": "Default Mash Tun",
"name": "MASH_TUN",
"options": null,
"type": "string",
"value": "HEDER"
"type": "kettle",
"value": ""
},
"TEMP_UNIT": {
"description": "Temperature Unit",
@ -27,6 +34,6 @@
}
],
"type": "select",
"value": "F"
"value": "C"
}
}

View file

@ -2,7 +2,7 @@
"basic": {
"name": ""
},
"profile": [
"steps": [
]
}

View file

@ -38,12 +38,10 @@ class LogController:
async def get_data(self, names, sample_rate='60s'):
'''
:param names: name as string or list of names as string
:param sample_rate: rate for resampling the data
:return:
'''
# make string to array
if isinstance(names, list) is False:
names = [names]
@ -87,14 +85,23 @@ class LogController:
if len(names) > 1:
for name in names:
data[name] = result[name].interpolate(limit_direction='both', limit=10).tolist()
else:
data[name] = result.interpolate().tolist()
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:
'''
@ -106,14 +113,9 @@ class LogController:
return [os.path.basename(x) for x in glob.glob('./logs/sensor_%s.log*' % name)]
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)
for f in all_filenames:
os.remove(f)
if name in self.datalogger:

View 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

View file

@ -3,7 +3,8 @@ import copy
import json
import logging
import os.path
from os import listdir
from os.path import isfile, join
import shortuuid
from cbpi.api.dataclasses import Props, Step
from tabulate import tabulate
@ -54,13 +55,14 @@ class StepController:
# create file if not exists
if os.path.exists(self.path) is False:
with open(self.path, "w") as file:
json.dump(dict(basic={}, profile=[]), file, indent=4, sort_keys=True)
json.dump(dict(basic={}, steps=[]), file, indent=4, sort_keys=True)
#load from json file
with open(self.path) as json_file:
data = json.load(json_file)
self.basic_data = data["basic"]
self.profile = data["profile"]
self.profile = data["steps"]
# Start step after start up
self.profile = list(map(lambda item: self.create(item), self.profile))
@ -73,7 +75,6 @@ class StepController:
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")
@ -104,7 +105,7 @@ class StepController:
async def save(self):
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:
json.dump(data, file, indent=4, sort_keys=True)
self.push_udpate()
@ -132,6 +133,10 @@ class StepController:
self.cbpi.notify(message="BREWING COMPLETE")
logging.info("BREWING COMPLETE")
async def previous(self):
logging.info("Trigger Next")
async def next(self):
logging.info("Trigger Next")
step = self.find_by_status(StepState.ACTIVE)
@ -190,7 +195,7 @@ class StepController:
return result
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):
index = self.get_index_by_id(id)
@ -215,7 +220,7 @@ class StepController:
self.profile = list(filter(lambda item: item.id != id, self.profile))
await self.save()
async def shutdown(self, app):
async def shutdown(self, app=None):
logging.info("Mash Profile Shutdonw")
for p in self.profile:
instance = p.instance
@ -225,6 +230,7 @@ class StepController:
await instance.stop()
await instance.task
await self.save()
self.push_udpate()
def done(self, step, result):
if result == StepResult.NEXT:
@ -245,8 +251,11 @@ class StepController:
def get_index_by_id(self, id):
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 item: item.to_dict(), self.profile))))
def push_udpate(self, complete=False):
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):
try:
@ -269,3 +278,31 @@ class StepController:
await item.instance.__getattribute__(action)(**parameter)
except Exception as 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)

View file

@ -19,7 +19,7 @@ from cbpi.controller.kettle_controller import KettleController
from cbpi.controller.plugin_controller import PluginController
from cbpi.controller.sensor_controller import SensorController
from cbpi.controller.step_controller import StepController
from cbpi.controller.recipe_controller import RecipeController
from cbpi.controller.system_controller import SystemController
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_sensor import SensorHttpEndpoints
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_system import SystemHttpEndpoints
from cbpi.http_endpoints.http_log import LogHttpEndpoints
@ -97,10 +97,12 @@ class CraftBeerPi:
self.system = SystemController(self)
self.kettle = KettleController(self)
self.step : StepController = StepController(self)
self.recipe : RecipeController = RecipeController(self)
#self.satellite: SatelliteController = SatelliteController(self)
self.dashboard = DashboardController(self)
self.http_step = StepHttpEndpoints(self)
self.http_recipe = RecipeHttpEndpoints(self)
self.http_sensor = SensorHttpEndpoints(self)
self.http_config = ConfigHttpEndpoints(self)
self.http_actor = ActorHttpEndpoints(self)

View file

@ -13,18 +13,12 @@ class CustomSensor(CBPiSensor):
def __init__(self, cbpi, id, props):
super(CustomSensor, self).__init__(cbpi, id, props)
self.value = 0
async def run(self):
while self.running is True:
self.value = random.randint(0,10)
self.push_update(self.value)
await asyncio.sleep(1)
def get_state(self):
return dict(value=self.value)

View file

@ -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")])
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):
# ON
if state == 1:

View file

@ -2,7 +2,6 @@
import asyncio
from cbpi.api.step import CBPiStep, StepResult
from cbpi.api.timer import Timer
from cbpi.api import *
import logging
@ -12,13 +11,17 @@ import logging
Property.Kettle(label="Kettle")])
class MashStep(CBPiStep):
@action(key="Custom Step Action", parameters=[])
async def hello(self, **kwargs):
print("ACTION")
@action(key="Custom RESET", parameters=[])
async def custom_reset(self, **kwargs):
self.summary = ""
await self.push_update()
@action(key="Custom Step Action 2", parameters=[])
async def hello2(self, **kwargs):
print("ACTION2")
@action(key="Custom Action", parameters=[Property.Number(label="Value", configurable=True)])
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):
self.summary = ""
@ -40,6 +43,7 @@ class MashStep(CBPiStep):
await self.push_update()
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)
async def run(self):
@ -56,10 +60,12 @@ class WaitStep(CBPiStep):
@action(key="Custom Step Action", parameters=[])
async def hello(self, **kwargs):
print("ACTION")
self.cbpi.notify("ACTION 1 CALLED")
@action(key="Custom Step Action 2", parameters=[])
async def hello2(self, **kwargs):
print("ACTION2")
self.cbpi.notify("ACTION 2 CALLED")
async def on_timer_done(self,timer):
self.summary = ""

View file

@ -7,7 +7,7 @@ from aiohttp import web
from cbpi.api import *
import os, re, threading, time
from subprocess import call
import random
def getSensors():
try:
@ -56,7 +56,7 @@ class OneWire(CBPiSensor):
def __init__(self, cbpi, id, props):
super(OneWire, self).__init__(cbpi, id, props)
self.value = 0
self.value = 200
async def start(self):
await super().start()
@ -79,6 +79,7 @@ class OneWire(CBPiSensor):
async def run(self):
while True:
self.value = self.t.value
self.log_data(self.value)
self.push_update(self.value)
await asyncio.sleep(self.interval)

View file

@ -206,6 +206,7 @@ class ActorHttpEndpoints():
"""
actor_id = request.match_info['id']
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)

View file

@ -1,7 +1,8 @@
from cbpi.utils.encoder import ComplexEncoder
from aiohttp import web
from cbpi.utils.utils import json_dumps
from cbpi.api import request_mapping
import json
class LogHttpEndpoints:
def __init__(self,cbpi):
@ -140,7 +141,7 @@ class LogHttpEndpoints:
return web.json_response(data, dumps=json_dumps)
@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
@ -163,19 +164,82 @@ class LogHttpEndpoints:
data = await self.cbpi.log.get_data(log_name)
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)
async def delete_all_logs(self, request):
async def clear_log(self, request):
"""
---
description: Get log data for sensor
tags:
- Log
parameters:
- name: "name"
in: "path"
description: "Sensor ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation.
"""
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)
@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)

View 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"))))

View file

@ -253,5 +253,25 @@ class StepHttpEndpoints():
await self.controller.call_action(id,data.get("action"), data.get("parameter",[]))
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)

View file

@ -1,6 +1,7 @@
import datetime
from json import JSONEncoder
from pandas import Timestamp
class ComplexEncoder(JSONEncoder):
@ -11,7 +12,11 @@ class ComplexEncoder(JSONEncoder):
return obj.to_json()
elif isinstance(obj, datetime.datetime):
return obj.__str__()
elif isinstance(obj, Timestamp):
print("TIMe")
return obj.__str__()
else:
print(type(obj))
raise TypeError()
except Exception as e:

View file

@ -1,4 +1,4 @@
aiohttp==3.7.3
aiohttp==3.7.4
aiohttp-auth==0.1.1
aiohttp-route-decorator==0.1.4
aiohttp-security==0.4.0

216
sample.py
View file

@ -1,208 +1,14 @@
import pandas as pd
import datetime
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
def dateparse(time_in_secs):
'''
Internal helper for date parsing
:param time_in_secs:
:return:
'''
return datetime.datetime.strptime(time_in_secs, '%Y-%m-%d %H:%M:%S')
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
'''
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()}})

View file

@ -15,7 +15,7 @@ setup(name='cbpi',
'cbpi': ['*','*.txt', '*.rst', '*.yaml']},
install_requires=[
"aiohttp==3.7.3",
"aiohttp==3.7.4",
"aiohttp-auth==0.1.1",
"aiohttp-route-decorator==0.1.4",
"aiohttp-security==0.4.0",