From b3d87bc027ba1bd2d61dd2818f4bd96258709818 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sun, 30 May 2021 11:58:18 +0200 Subject: [PATCH] Added api path for recipe creation - Added path parameter in config.json for settings - If empty, 'upload' will be used per default as api path - If something is entered, a different api path will be used for the creation of recipes. -> this allows the standard usage of the recipe upload and selection via cbpi4, but adds the possibility that custom plugins can be written to create cbpi recipe flows from the uploaded files ********************* + some prep work to create the http endpoints --- cbpi/config/config.json | 7 ++ cbpi/craftbeerpi.py | 4 +- cbpi/extension/RecipeUpload/__init__.py | 21 ++++ cbpi/extension/mashstep/__init__.py | 15 ++- cbpi/http_endpoints/http_step.py | 1 - cbpi/http_endpoints/http_upload.py | 125 ++++++++++++++++++++++++ setup.py | 3 +- 7 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 cbpi/http_endpoints/http_upload.py diff --git a/cbpi/config/config.json b/cbpi/config/config.json index f686bbc..26399a7 100644 --- a/cbpi/config/config.json +++ b/cbpi/config/config.json @@ -20,6 +20,13 @@ "type": "kettle", "value": "" }, + "RECIPE_CREATION_PATH": { + "description": "API path to creation plugin. Default: empty", + "name": "RECIPE_CREATION_PATH", + "options": null, + "type": "string", + "value": "" + }, "TEMP_UNIT": { "description": "Temperature Unit", "name": "TEMP_UNIT", diff --git a/cbpi/craftbeerpi.py b/cbpi/craftbeerpi.py index 8166aff..c23ffa8 100644 --- a/cbpi/craftbeerpi.py +++ b/cbpi/craftbeerpi.py @@ -46,6 +46,7 @@ from cbpi.http_endpoints.http_plugin import PluginHttpEndpoints from cbpi.http_endpoints.http_system import SystemHttpEndpoints from cbpi.http_endpoints.http_log import LogHttpEndpoints from cbpi.http_endpoints.http_notification import NotificationHttpEndpoints +from cbpi.http_endpoints.http_upload import UploadHttpEndpoints import shortuuid logger = logging.getLogger(__name__) @@ -123,6 +124,8 @@ class CraftBeerPi: self.http_system = SystemHttpEndpoints(self) self.http_log = LogHttpEndpoints(self) self.http_notification = NotificationHttpEndpoints(self) + self.http_upload = UploadHttpEndpoints(self) + self.login = Login(self) @@ -219,7 +222,6 @@ class CraftBeerPi: contact="info@craftbeerpi.com") - def notify(self, title: str, message: str, type: NotificationType = NotificationType.INFO, action=[]) -> None: self.notification.notify(title, message, type, action) diff --git a/cbpi/extension/RecipeUpload/__init__.py b/cbpi/extension/RecipeUpload/__init__.py index eb51295..beca115 100644 --- a/cbpi/extension/RecipeUpload/__init__.py +++ b/cbpi/extension/RecipeUpload/__init__.py @@ -57,6 +57,12 @@ class RecipeUpload(CBPiExtension): def allowed_file(self, filename, extension): return '.' in filename and filename.rsplit('.', 1)[1] in set([extension]) + def get_creation_path(self): + creation_path = self.cbpi.config.get("RECIPE_CREATION_PATH", "upload") + path = {'path': 'upload'} if creation_path == '' else {'path': creation_path} + return path + + @request_mapping(path='/', method="POST", auth_required=False) async def RecipeUpload(self, request): data = await request.post() @@ -123,6 +129,21 @@ class RecipeUpload(CBPiExtension): await self.xml_recipe_creation(xml_id['id']) return web.Response(status=200) + @request_mapping(path="/getpath", auth_required=False) + async def http_getpath(self, request): + + """ + + --- + description: get path for recipe creation + tags: + - Upload + responses: + "200": + description: successful operation + """ + return web.json_response(data=self.get_creation_path()) + async def kbh_recipe_creation(self, Recipe_ID): self.kettle = None diff --git a/cbpi/extension/mashstep/__init__.py b/cbpi/extension/mashstep/__init__.py index 4bf04d1..dac9a8d 100644 --- a/cbpi/extension/mashstep/__init__.py +++ b/cbpi/extension/mashstep/__init__.py @@ -15,9 +15,11 @@ from typing import KeysView from cbpi.api.config import ConfigType from cbpi.api.base import CBPiBase import numpy as np +#import scipy.optimize import warnings + @parameters([Property.Text(label="Notification",configurable = True, description = "Text for notification"), Property.Select(label="AutoNext",options=["Yes","No"], description="Automatically move to next step (Yes) or pause after Notification (No)")]) class NotificationStep(CBPiStep): @@ -412,7 +414,7 @@ class CooldownStep(CBPiStep): self.kettle = self.get_kettle(self.props.get("Kettle", None)) self.actor = self.props.get("Actor", None) self.target_temp = int(self.props.get("Temp",0)) - self.Interval = 10 # Interval in minutes on how often cooldwon end time is calculated + self.Interval = 15 # Interval in minutes on how often cooldwon end time is calculated self.cbpi.notify(self.name, 'Cool down to {}°'.format(self.target_temp), NotificationType.INFO) if self.timer is None: @@ -422,6 +424,7 @@ class CooldownStep(CBPiStep): self.time_array.append(time.time()) self.next_check = self.start_time + self.Interval * 60 self.count = 0 + self.initial_date = None async def on_stop(self): await self.timer.stop() @@ -441,13 +444,17 @@ class CooldownStep(CBPiStep): await self.push_update() while self.running == True: current_temp = self.get_sensor_value(self.props.get("Sensor", None)).get("value") - if self.count == 19: + if self.count == 10: self.temp_array.append(current_temp) - self.time_array.append(time.time()) + current_time = time.time() + if self.initial_date == None: + self.initial_date = current_time + self.time_array.append(current_time) self.count = 0 if time.time() >= self.next_check: self.next_check = time.time() + (self.Interval * 60) - cooldown_model = np.poly1d(np.polyfit(self.temp_array, self.time_array, 4)) + + cooldown_model = np.poly1d(np.polyfit(self.temp_array, self.time_array, 2)) target_time=cooldown_model(self.target_temp) target_timestring= datetime.fromtimestamp(target_time) self.summary="ECT: {}".format(target_timestring.strftime("%H:%M")) diff --git a/cbpi/http_endpoints/http_step.py b/cbpi/http_endpoints/http_step.py index c499d36..d7b2242 100644 --- a/cbpi/http_endpoints/http_step.py +++ b/cbpi/http_endpoints/http_step.py @@ -286,7 +286,6 @@ class StepHttpEndpoints(): await self.controller.savetobook() return web.Response(status=204) - diff --git a/cbpi/http_endpoints/http_upload.py b/cbpi/http_endpoints/http_upload.py new file mode 100644 index 0000000..07a94ae --- /dev/null +++ b/cbpi/http_endpoints/http_upload.py @@ -0,0 +1,125 @@ +#from cbpi.controller.recipe_controller import RecipeController +from cbpi.api.dataclasses import Props, Step +from aiohttp import web +from cbpi.api import * + +class UploadHttpEndpoints(): + + def __init__(self, cbpi): + self.cbpi = cbpi +# self.controller : RecipeController = cbpi.recipe + self.cbpi.register(self, "/fileupload") + + + @request_mapping(path='/', method="POST", auth_required=False) + async def RecipeUpload(self, request): + """ + + --- + description: Upload XML file or database from KBH V2 + tags: + - FileUpload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + orderId: + type: integer + userId: + type: integer + fileName: + type: string + format: binary + responses: + "200": + description: successful operation + """ + + data = await request.post() + fileData = data['File'] + logging.info(fileData) + + + @request_mapping(path='/kbh', method="GET", auth_required=False) + async def get_kbh_list(self, request): + """ + + --- + description: Get Recipe list from Kleiner Brauhelfer + tags: + - FileUpload + responses: + "200": + description: successful operation + """ + + kbh_list = await get_kbh_recipes() + return web.json_response(kbh_list) + + @request_mapping(path='/kbh', method="POST", auth_required=False) + async def create_kbh_recipe(self, request): + """ + + --- + description: Create Recipe from KBH database with selected ID + tags: + - FileUpload + responses: + "200": + description: successful operation + """ + + kbh_id = await request.json() + await self.kbh_recipe_creation(kbh_id['id']) + return web.Response(status=200) + + @request_mapping(path='/xml', method="GET", auth_required=False) + async def get_xml_list(self, request): + """ + + --- + description: Get recipe list from xml file + tags: + - FileUpload + responses: + "200": + description: successful operation + """ + + xml_list = await get_xml_recipes() + return web.json_response(xml_list) + + @request_mapping(path='/xml', method="POST", auth_required=False) + async def create_xml_recipe(self, request): + """ + + --- + description: Create recipe from xml file with selected id + tags: + - FileUpload + responses: + "200": + description: successful operation + """ + + xml_id = await request.json() + await self.xml_recipe_creation(xml_id['id']) + return web.Response(status=200) + + @request_mapping(path="/getpath", auth_required=False) + async def http_getpath(self, request): + + """ + + --- + description: get path for recipe creation + tags: + - FileUpload + responses: + "200": + description: successful operation + """ + return web.json_response(data=self.get_creation_path()) + diff --git a/setup.py b/setup.py index a5dd517..72b9a28 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ setup(name='cbpi', 'shortuuid==1.0.1', 'tabulate==0.8.7', 'asyncio-mqtt', + 'scipy', 'cbpi4ui', 'RPi.GPIO; sys_platform == "linux"' ], @@ -46,4 +47,4 @@ setup(name='cbpi', "cbpi=cbpi.cli:main", ] } -) \ No newline at end of file +)