From eb1365d329165a199f7f8cbf2318d0d1407d7a6c Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sun, 16 Jun 2024 20:21:46 +0200 Subject: [PATCH 01/16] change version number to release --- cbpi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 291f229..9906eab 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.rc1" +__version__ = "4.4.1" __codename__ = "Yeast Starter" From 97845bc2dc3dd1a0e78b86f866c2b4b9ddd2f236 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:40:38 +0200 Subject: [PATCH 02/16] brewfatherapiv2test --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 57 ++++++++++++++++++---------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 9906eab..2c39745 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1" +__version__ = "4.4.2.a1" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index 2c54e4b..51efffd 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -23,7 +23,6 @@ from ..api.step import StepMove, StepResult, StepState import re import base64 - class UploadController: def __init__(self, cbpi): @@ -70,9 +69,12 @@ class UploadController: return [] async def get_brewfather_recipes(self,offset=0): + limit = 50 + loop=0 + repeat = True brewfather = True result=[] - self.url="https://api.brewfather.app/v1/recipes" + self.url="https://api.brewfather.app/v2/recipes" brewfather_user_id = self.cbpi.config.get("brewfather_user_id", None) if brewfather_user_id == "" or brewfather_user_id is None: brewfather = False @@ -84,25 +86,40 @@ class UploadController: if brewfather == True: encodedData = base64.b64encode(bytes(f"{brewfather_user_id}:{brewfather_api_key}", "ISO-8859-1")).decode("ascii") headers={"Authorization": "Basic %s" % encodedData} - parameters={"limit": 50, 'offset': offset} - async with aiohttp.ClientSession(headers=headers) as bf_session: - async with bf_session.get(self.url, params=parameters) as r: - bf_recipe_list = await r.json() - await bf_session.close() + parameters={"limit": limit} + while repeat == True: + try: + async with aiohttp.ClientSession(headers=headers) as bf_session: + async with bf_session.get(self.url, params=parameters) as r: + bf_recipe_list = await r.json() + await bf_session.close() + except Exception as e: + logging.error(e) - if bf_recipe_list: - for row in bf_recipe_list: - recipe_id = row['_id'] - name = row['name'] - element = {'value': recipe_id, 'label': name} - result.append(element) - return result - else: - return [] - - else: - return [] + if bf_recipe_list: + loop +=1 + for row in bf_recipe_list: + recipe_id = row['_id'] + name = row['name'] + element = {'value': recipe_id, 'label': name} + result.append(element) + else: + repeat = False + if len(bf_recipe_list) != limit: + logging.info(loop) + repeat = False + else: + parameters={"limit": limit, 'start_after': recipe_id} + + logging.info(len(result)) + try: + newlist = sorted(result, key=lambda d: d['label']) + logging.error(newlist) + return newlist + except: + return result + def get_creation_path(self): creation_path = self.cbpi.config.get("RECIPE_CREATION_PATH", "upload") @@ -738,7 +755,7 @@ class UploadController: brewfather = True result=[] - self.bf_url="https://api.brewfather.app/v1/recipes/" + Recipe_ID + self.bf_url="https://api.brewfather.app/v2/recipes/" + Recipe_ID brewfather_user_id = self.cbpi.config.get("brewfather_user_id", None) if brewfather_user_id == "" or brewfather_user_id is None: brewfather = False From 45592733da9822e70951b496c4a53f0399c1e94f Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Tue, 18 Jun 2024 06:57:53 +0200 Subject: [PATCH 03/16] reduce logging on bf v2 api calls --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 2c39745..2214a03 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2.a1" +__version__ = "4.4.2.a2" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index 51efffd..6e967ed 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -70,7 +70,7 @@ class UploadController: async def get_brewfather_recipes(self,offset=0): limit = 50 - loop=0 + #loop=0 repeat = True brewfather = True result=[] @@ -97,7 +97,7 @@ class UploadController: logging.error(e) if bf_recipe_list: - loop +=1 + #loop +=1 for row in bf_recipe_list: recipe_id = row['_id'] name = row['name'] @@ -107,15 +107,15 @@ class UploadController: repeat = False if len(bf_recipe_list) != limit: - logging.info(loop) + #logging.info(loop) repeat = False else: parameters={"limit": limit, 'start_after': recipe_id} - logging.info(len(result)) + #logging.info(len(result)) try: newlist = sorted(result, key=lambda d: d['label']) - logging.error(newlist) + #logging.info(newlist) return newlist except: return result From 31aa275d9632c265fb7ba67e030a3dc93e618234 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:28:31 +0200 Subject: [PATCH 04/16] add parameter for bf recipe list length --- cbpi/__init__.py | 2 +- cbpi/extension/ConfigUpdate/__init__.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 2214a03..517bf0f 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2.a2" +__version__ = "4.4.2.a3" __codename__ = "Yeast Starter" diff --git a/cbpi/extension/ConfigUpdate/__init__.py b/cbpi/extension/ConfigUpdate/__init__.py index eba146a..95f2a6c 100644 --- a/cbpi/extension/ConfigUpdate/__init__.py +++ b/cbpi/extension/ConfigUpdate/__init__.py @@ -61,6 +61,7 @@ class ConfigUpdate(CBPiExtension): AddMashIn = self.cbpi.config.get("AddMashInStep", None) bfuserid = self.cbpi.config.get("brewfather_user_id", None) bfapikey = self.cbpi.config.get("brewfather_api_key", None) + bflistlength = self.cbpi.config.get("brewfather_list_length", None) RecipeCreationPath = self.cbpi.config.get("RECIPE_CREATION_PATH", None) BoilKettle = self.cbpi.config.get("BoilKettle", None) CONFIG_STATUS = self.cbpi.config.get("CONFIG_STATUS", None) @@ -244,6 +245,21 @@ class ConfigUpdate(CBPiExtension): await self.cbpi.config.add("brewfather_api_key", "", type=ConfigType.STRING, description="Brewfather API Key", source="craftbeerpi") except: logger.warning('Unable to update config') + + ## Check if Brewfather API Key is in config + + if bflistlength is None: + logger.info("INIT Brewfather Recipe List Length") + try: + await self.cbpi.config.add("brewfather_list_length", 50, type=ConfigType.SELECT, description="Brewfather Recipe List length", + source="craftbeerpi", + options= [{"label": "5", "value": 5}, + {"label": "10", "value": 10}, + {"label": "25", "value": 25}, + {"label": "50", "value": 50}, + {"label": "100", "value": 100}]) + except: + logger.warning('Unable to update config') ## Check if Brewfather API Key is in config From 3bf8a9ba6bb5d1993f5792d50e75b2d52c3caaba Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:53:03 +0200 Subject: [PATCH 05/16] further change for BF v2 api --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 517bf0f..180fe34 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2.a3" +__version__ = "4.4.2.a4" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index 6e967ed..2567689 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -17,7 +17,7 @@ import os.path from os import listdir from os.path import isfile, join import json -import shortuuid +import math import yaml from ..api.step import StepMove, StepResult, StepState import re @@ -70,7 +70,7 @@ class UploadController: async def get_brewfather_recipes(self,offset=0): limit = 50 - #loop=0 + length = self.cbpi.config.get('brewfather_list_length',50) repeat = True brewfather = True result=[] @@ -91,10 +91,16 @@ class UploadController: try: async with aiohttp.ClientSession(headers=headers) as bf_session: async with bf_session.get(self.url, params=parameters) as r: - bf_recipe_list = await r.json() + if r.status == 429: + logging.error("Too many requests to BF api. Try again later") + self.cbpi.notify("Error", "Too many requests to BF api. Try again later", NotificationType.ERROR) + repeat = False + else: + bf_recipe_list = await r.json() await bf_session.close() except Exception as e: logging.error(e) + repeat = False if bf_recipe_list: #loop +=1 @@ -112,13 +118,21 @@ class UploadController: else: parameters={"limit": limit, 'start_after': recipe_id} - #logging.info(len(result)) + try: newlist = sorted(result, key=lambda d: d['label']) - #logging.info(newlist) - return newlist + listlength=len(newlist) + max=math.floor(listlength/length) + #logging.error(listlength) + #logging.error(length) + sortlist=[] + for i in range(0 , max+1): + sortlist.append({ 'value': i*length, 'label': i*length }) + #logging.error(sortlist) + return newlist, sortlist, length except: - return result + sortlist=[{ 'value': 0, 'label': '0' }] + return result, sortlist, length def get_creation_path(self): From 598f1e9dcecd9dd97c095c63b3ddb722e48f5b02 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sat, 29 Jun 2024 07:02:15 +0200 Subject: [PATCH 06/16] call bf recipes at startup and add list to state parameters. -> recudtion of bf api calls --- cbpi/__init__.py | 2 +- cbpi/http_endpoints/http_system.py | 1 + cbpi/http_endpoints/http_upload.py | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 180fe34..35fff87 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2.a4" +__version__ = "4.4.2.a5" __codename__ = "Yeast Starter" diff --git a/cbpi/http_endpoints/http_system.py b/cbpi/http_endpoints/http_system.py index 7a556d2..3e1ff07 100644 --- a/cbpi/http_endpoints/http_system.py +++ b/cbpi/http_endpoints/http_system.py @@ -43,6 +43,7 @@ class SystemHttpEndpoints: fermentersteps=self.cbpi.fermenter.get_fermenter_steps(), config=self.cbpi.config.get_state(), notifications=self.cbpi.notification.get_state(), + bf_recipes=await self.cbpi.upload.get_brewfather_recipes(0), version=__version__, guiversion=version, codename=__codename__) diff --git a/cbpi/http_endpoints/http_upload.py b/cbpi/http_endpoints/http_upload.py index 2df6fed..9bf52d4 100644 --- a/cbpi/http_endpoints/http_upload.py +++ b/cbpi/http_endpoints/http_upload.py @@ -159,6 +159,23 @@ class UploadHttpEndpoints(): return web.json_response(bf_list) + @request_mapping(path='/bfupdate/', method="GET", auth_required=False) + async def get_bf_update(self, request): + """ + + --- + description: Get recipe list update from Brewfather App + tags: + - Upload + responses: + "200": + description: successful operation + """ + #offset = request.match_info['offset'] + bf_list = await self.controller.get_brewfather_recipes() + self.cbpi.ws.send(dict(topic="bfupdate", data=bf_list)) + return web.Response(status=200) + @request_mapping(path='/bf', method="POST", auth_required=False) async def create_bf_recipe(self, request): """ From 607a403a8c8925126e9925c9ee34f17dcc9e7e85 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:13:46 +0200 Subject: [PATCH 07/16] Additional checks, if bf api is not accessible --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 41 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 35fff87..b45636e 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2.a5" +__version__ = "4.4.2.a6" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index 2567689..a4d86fd 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -101,36 +101,35 @@ class UploadController: except Exception as e: logging.error(e) repeat = False - - if bf_recipe_list: - #loop +=1 - for row in bf_recipe_list: - recipe_id = row['_id'] - name = row['name'] - element = {'value': recipe_id, 'label': name} - result.append(element) - else: - repeat = False - - if len(bf_recipe_list) != limit: - #logging.info(loop) - repeat = False - else: - parameters={"limit": limit, 'start_after': recipe_id} - - + try: + if bf_recipe_list: + for row in bf_recipe_list: + recipe_id = row['_id'] + name = row['name'] + element = {'value': recipe_id, 'label': name} + result.append(element) + else: + repeat = False + except Exception as e: + logging.error(e) + try: + if len(bf_recipe_list) != limit: + repeat = False + else: + parameters={"limit": limit, 'start_after': recipe_id} + except Exception as e: + logging.error(e) + try: newlist = sorted(result, key=lambda d: d['label']) listlength=len(newlist) max=math.floor(listlength/length) - #logging.error(listlength) - #logging.error(length) sortlist=[] for i in range(0 , max+1): sortlist.append({ 'value': i*length, 'label': i*length }) - #logging.error(sortlist) return newlist, sortlist, length except: + logging.error("Return empty BF recipe list") sortlist=[{ 'value': 0, 'label': '0' }] return result, sortlist, length From c26891180d9dc14a65c617028640765f14ff7132 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Mon, 1 Jul 2024 19:32:26 +0200 Subject: [PATCH 08/16] bump version --- cbpi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index b45636e..9c72a9f 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2.a6" +__version__ = "4.4.2" __codename__ = "Yeast Starter" From 591f933db0a7e2ae1556d02091317273df8a41f9 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Mon, 1 Jul 2024 22:52:00 +0200 Subject: [PATCH 09/16] usage of retry-after header information in bf api call --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 9c72a9f..347c28e 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.2" +__version__ = "4.4.3.a1" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index a4d86fd..1663730 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -92,9 +92,19 @@ class UploadController: async with aiohttp.ClientSession(headers=headers) as bf_session: async with bf_session.get(self.url, params=parameters) as r: if r.status == 429: - logging.error("Too many requests to BF api. Try again later") - self.cbpi.notify("Error", "Too many requests to BF api. Try again later", NotificationType.ERROR) + try: + seconds=int(r.headers['Retry-After']) + minutes=round(seconds/60) + except: + seconds=None + if not seconds: + logging.error("Too many requests to BF api. Try again later") + self.cbpi.notify("Error", "Too many requests to BF api. Try again later", NotificationType.ERROR) + else: + logging.error(f"Too many requests to BF api. Try in {minutes} minutes again.") + self.cbpi.notify("Error", f"Too many requests to BF api. Try in {minutes} minutes again.", NotificationType.ERROR) repeat = False + logging.error(r.headers['Retry-After']) else: bf_recipe_list = await r.json() await bf_session.close() From 62370980e0b052a9d126d7e753a0dbe9282bdafd Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Tue, 2 Jul 2024 21:17:34 +0200 Subject: [PATCH 10/16] test for autostart with different routine to detect user --- cbpi/__init__.py | 2 +- cbpi/cli.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 347c28e..33fa074 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a1" +__version__ = "4.4.3.a2" __codename__ = "Yeast Starter" diff --git a/cbpi/cli.py b/cbpi/cli.py index c4e21fa..86e9e89 100644 --- a/cbpi/cli.py +++ b/cbpi/cli.py @@ -7,7 +7,7 @@ from cbpi.configFolder import ConfigFolder from cbpi.utils.utils import load_config from zipfile import ZipFile from cbpi.craftbeerpi import CraftBeerPi -import os +import os, pwd import pkgutil import shutil import click @@ -165,7 +165,8 @@ class CraftBeerPiCli(): else: print("CraftBeerPi Autostart is {}OFF{}".format(Fore.RED,Style.RESET_ALL)) elif(name == "on"): - user=os.getlogin() + #user=os.getlogin() + user=pwd.getpwuid(os.getuid()).pw_name path="/usr/local/bin/cbpi" if os.path.exists("/home/"+user+"/.local/bin/cbpi") is True: path="/home/"+user+"/.local/bin/cbpi" From c58ca462199a9f260ff652759c8eea4ab5eb7309 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sat, 6 Jul 2024 09:06:48 +0200 Subject: [PATCH 11/16] test on optional offset for mqtt sensor (#139) --- cbpi/__init__.py | 2 +- cbpi/cli.py | 7 +- cbpi/config/config.yaml | 1 + cbpi/extension/ConfigUpdate/__init__.py | 20 +++- cbpi/extension/mqtt_sensor/__init__.py | 153 ++++++++++++++++++++++-- 5 files changed, 169 insertions(+), 14 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 33fa074..46e01d8 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a2" +__version__ = "4.4.3.a3" __codename__ = "Yeast Starter" diff --git a/cbpi/cli.py b/cbpi/cli.py index 86e9e89..8c99144 100644 --- a/cbpi/cli.py +++ b/cbpi/cli.py @@ -7,7 +7,12 @@ from cbpi.configFolder import ConfigFolder from cbpi.utils.utils import load_config from zipfile import ZipFile from cbpi.craftbeerpi import CraftBeerPi -import os, pwd +import os +try: + import pwd + module_pwd=True +except: + module_pwd=False import pkgutil import shutil import click diff --git a/cbpi/config/config.yaml b/cbpi/config/config.yaml index b15c777..eddc66d 100644 --- a/cbpi/config/config.yaml +++ b/cbpi/config/config.yaml @@ -11,6 +11,7 @@ mqtt_host: localhost mqtt_port: 1883 mqtt_username: "" mqtt_password: "" +mqtt_offset: false username: cbpi password: 123 diff --git a/cbpi/extension/ConfigUpdate/__init__.py b/cbpi/extension/ConfigUpdate/__init__.py index 95f2a6c..ab532c2 100644 --- a/cbpi/extension/ConfigUpdate/__init__.py +++ b/cbpi/extension/ConfigUpdate/__init__.py @@ -8,7 +8,7 @@ import json from cbpi.api import * from cbpi.api.config import ConfigType from cbpi.api.base import CBPiBase -import glob +import glob, yaml from cbpi import __version__ logger = logging.getLogger(__name__) @@ -19,6 +19,12 @@ class ConfigUpdate(CBPiExtension): self.cbpi = cbpi self._task = asyncio.create_task(self.run()) + def append_to_yaml(self, file_path, data_to_append): + + with open(file_path[0], 'a+') as file: + file.seek(0) + yaml.dump(data_to_append, file, default_flow_style=False) + async def run(self): logging.info("Check Config for required changes") @@ -67,7 +73,7 @@ class ConfigUpdate(CBPiExtension): CONFIG_STATUS = self.cbpi.config.get("CONFIG_STATUS", None) self.version=__version__ current_grid = self.cbpi.config.get("current_grid", None) - + mqtt_offset=self.cbpi.static_config.get("mqtt_offset", None) if boil_temp is None: logger.info("INIT Boil Temp Setting") @@ -558,6 +564,16 @@ class ConfigUpdate(CBPiExtension): except Exception as e: logging.error(e) + if mqtt_offset is None: + logging.info("INIT MQTT Offset in static config") + try: + static_config_file=glob.glob(self.cbpi.config_folder.get_file_path('config.yaml')) + data_to_append = {'mqtt_offset': False} + self.append_to_yaml(static_config_file, data_to_append) + pass + except Exception as e: + logging.error(e) + logging.warning('Unable to update database') ## Check if influxdbname is in config if CONFIG_STATUS is None or CONFIG_STATUS != self.version: diff --git a/cbpi/extension/mqtt_sensor/__init__.py b/cbpi/extension/mqtt_sensor/__init__.py index 68db819..9dc6d25 100644 --- a/cbpi/extension/mqtt_sensor/__init__.py +++ b/cbpi/extension/mqtt_sensor/__init__.py @@ -8,16 +8,17 @@ import json import time from datetime import datetime + @parameters([Property.Text(label="Topic", configurable=True, description="MQTT Topic"), - Property.Text(label="PayloadDictionary", configurable=True, default_value="", - description="Where to find msg in payload, leave blank for raw payload"), - Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"), - Property.Fermenter(label="Fermenter", description="Reduced logging if Fermenter is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"), - Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"), - Property.Number(label="Timeout", configurable=True, unit="sec", - description="Timeout in seconds to send notification (default:60 | deactivated: 0)"), - Property.Number(label="TempRange", configurable=True, unit="degree", - description="Temp range in degree between reading and target temp of fermenter/kettle. Larger difference shows different color in dashboard (default:0 | deactivated: 0)")]) + Property.Text(label="PayloadDictionary", configurable=True, default_value="", + description="Where to find msg in payload, leave blank for raw payload"), + Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"), + Property.Fermenter(label="Fermenter", description="Reduced logging if Fermenter is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"), + Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"), + Property.Number(label="Timeout", configurable=True, unit="sec", + description="Timeout in seconds to send notification (default:60 | deactivated: 0)"), + Property.Number(label="TempRange", configurable=True, unit="degree", + description="Temp range in degree between reading and target temp of fermenter/kettle. Larger difference shows different color in dashboard (default:0 | deactivated: 0)")]) class MQTTSensor(CBPiSensor): def __init__(self, cbpi, id, props): @@ -135,6 +136,135 @@ class MQTTSensor(CBPiSensor): async def on_stop(self): self.subscribed = self.cbpi.satellite.unsubscribe(self.Topic, self.on_message) +@parameters([Property.Text(label="Topic", configurable=True, description="MQTT Topic"), + Property.Text(label="PayloadDictionary", configurable=True, default_value="", + description="Where to find msg in payload, leave blank for raw payload"), + Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"), + Property.Fermenter(label="Fermenter", description="Reduced logging if Fermenter is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"), + Property.Number(label="Offset", configurable=True, description="Offset for MQTT Sensor (default is 0). !!! Use this only with caution as offset for MQTT sensor should be defined on Sensor side !!!"), + Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"), + Property.Number(label="Timeout", configurable=True, unit="sec", + description="Timeout in seconds to send notification (default:60 | deactivated: 0)"), + Property.Number(label="TempRange", configurable=True, unit="degree", + description="Temp range in degree between reading and target temp of fermenter/kettle. Larger difference shows different color in dashboard (default:0 | deactivated: 0)")]) +class MQTTSensorOffset(CBPiSensor): + + def __init__(self, cbpi, id, props): + super(MQTTSensorOffset, self).__init__(cbpi, id, props) + self.Topic = self.props.get("Topic", None) + self.offset = float(self.props.get("Offset", 0)) + self.payload_text = self.props.get("PayloadDictionary", None) + if self.payload_text != None: + self.payload_text = self.payload_text.split('.') + self.subscribed = self.cbpi.satellite.subscribe(self.Topic, self.on_message) + self.value: float = 999 + self.timeout=int(self.props.get("Timeout", 60)) + self.temprange=float(self.props.get("TempRange", 0)) + self.starttime = time.time() + self.notificationsend = False + self.nextchecktime=self.starttime+self.timeout + self.lastdata=time.time() + self.lastlog=0 + self.sensor=self.get_sensor(self.id) + self.reducedfrequency=int(self.props.get("ReducedLogging", 60)) + if self.reducedfrequency < 0: + self.reducedfrequency = 0 + self.kettleid=self.props.get("Kettle", None) + self.fermenterid=self.props.get("Fermenter", None) + self.reducedlogging = True if self.kettleid or self.fermenterid else False + + if self.kettleid is not None and self.fermenterid is not None: + self.reducedlogging=False + self.cbpi.notify("MQTTSensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging / range warning.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) + + async def Confirm(self, **kwargs): + self.nextchecktime = time.time() + self.timeout + self.notificationsend = False + pass + + async def message(self): + target_timestring= datetime.fromtimestamp(self.lastdata) + self.cbpi.notify("MQTTSensor Timeout", "Sensor '" + str(self.sensor.name) + "' did not respond. Last data received: "+target_timestring.strftime("%D %H:%M"), NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) + pass + + async def on_message(self, message): + val = json.loads(message.payload.decode()) + try: + if self.payload_text is not None: + for key in self.payload_text: + val = val.get(key, None) + + if isinstance(val, (int, float, str)): + self.value = float(val)+self.offset + self.push_update(self.value) + if self.reducedlogging == True: + await self.logvalue() + else: + logging.info("MQTTSensor {} regular logging".format(self.sensor.name)) + self.log_data(self.value) + self.lastlog = time.time() + + if self.timeout !=0: + self.nextchecktime = time.time() + self.timeout + self.notificationsend = False + self.lastdata=time.time() + except Exception as e: + logging.error("MQTT Sensor Error {}".format(e)) + + async def logvalue(self): + self.kettle = self.get_kettle(self.kettleid) if self.kettleid is not None else None + self.fermenter = self.get_fermenter(self.fermenterid) if self.fermenterid is not None else None + now=time.time() + if self.kettle is not None: + try: + kettlestatus=self.kettle.instance.state + except: + kettlestatus=False + if kettlestatus: + self.log_data(self.value) + logging.info("MQTTSensor {} Kettle Active".format(self.sensor.name)) + self.lastlog = time.time() + else: + logging.info("MQTTSensor {} Kettle Inactive".format(self.sensor.name)) + if self.reducedfrequency != 0: + if now >= self.lastlog + self.reducedfrequency: + self.log_data(self.value) + self.lastlog = time.time() + logging.info("Logged with reduced freqency") + pass + + if self.fermenter is not None: + try: + fermenterstatus=self.fermenter.instance.state + except: + fermenterstatus=False + if fermenterstatus: + self.log_data(self.value) + logging.info("MQTTSensor {} Fermenter Active".format(self.sensor.name)) + self.lastlog = time.time() + else: + logging.info("MQTTSensor {} Fermenter Inactive".format(self.sensor.name)) + if self.reducedfrequency != 0: + if now >= self.lastlog + self.reducedfrequency: + self.log_data(self.value) + self.lastlog = time.time() + logging.info("Logged with reduced freqency") + pass + + async def run(self): + while self.running: + if self.timeout !=0: + if time.time() > self.nextchecktime and self.notificationsend == False: + await self.message() + self.notificationsend=True + await asyncio.sleep(1) + + def get_state(self): + return dict(value=self.value) + + async def on_stop(self): + self.subscribed = self.cbpi.satellite.unsubscribe(self.Topic, self.on_message) + def setup(cbpi): ''' @@ -145,4 +275,7 @@ def setup(cbpi): :return: ''' if str(cbpi.static_config.get("mqtt", False)).lower() == "true": - cbpi.plugin.register("MQTTSensor", MQTTSensor) + if str(cbpi.static_config.get("mqtt_offset", False)).lower() == "false": + cbpi.plugin.register("MQTTSensor", MQTTSensor) + else: + cbpi.plugin.register("MQTTSensor", MQTTSensorOffset) From 909bc58e326e1d281a24472bdfc24e6559eef11d Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sun, 7 Jul 2024 19:14:55 +0200 Subject: [PATCH 12/16] adapt cooldown step to newer numpy function --- cbpi/__init__.py | 2 +- cbpi/extension/mashstep/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 46e01d8..b38749b 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a3" +__version__ = "4.4.3.a4" __codename__ = "Yeast Starter" diff --git a/cbpi/extension/mashstep/__init__.py b/cbpi/extension/mashstep/__init__.py index 3948abb..3ea7f0e 100644 --- a/cbpi/extension/mashstep/__init__.py +++ b/cbpi/extension/mashstep/__init__.py @@ -536,7 +536,7 @@ class CooldownStep(CBPiStep): 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, 2)) + cooldown_model = np.polynomial.polynomial.Polynomial.fit(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")) From 4de128f90244fc3753b8cdb4510129b4146d4186 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sun, 7 Jul 2024 19:25:00 +0200 Subject: [PATCH 13/16] change requirement for numpy to 2.0.0 --- cbpi/__init__.py | 2 +- requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index b38749b..73b7517 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a4" +__version__ = "4.4.3.a5" __codename__ = "Yeast Starter" diff --git a/requirements.txt b/requirements.txt index 806ce15..b1a8823 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ pyfiglet==1.0.2 pandas==2.2.2 shortuuid==1.0.13 tabulate==0.9.0 -numpy==1.26.4 +numpy==2.0.0 cbpi4gui click==8.1.7 importlib_metadata==4.11.1 diff --git a/setup.py b/setup.py index 266bf84..2ec0a18 100644 --- a/setup.py +++ b/setup.py @@ -62,7 +62,7 @@ setup(name='cbpi4', 'psutil==5.9.8', 'cbpi4gui', 'importlib_metadata', - 'numpy==1.26.4', + 'numpy==2.0.0', 'pandas==2.2.2'] + ( ['rpi-lgpio'] if raspberrypi else [] ) + ( ['systemd-python'] if localsystem == "Linux" else [] ), From e0b220d98082149496f746ce48eada2b0408fa28 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:37:31 +0200 Subject: [PATCH 14/16] get first ferment target temp from BF for cooldown step --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 73b7517..526d131 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a5" +__version__ = "4.4.3.a6" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index 1663730..f3614dc 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -815,6 +815,21 @@ class UploadController: except: miscs = None + try: + fermentation_steps=bf_recipe['fermentation']['steps'] + except: + fermentation_steps=None + + if fermentation_steps is not None: + try: + step=fermentation_steps[0] + self.fermentation_step_temp=int(step['stepTemp']) + except: + self.fermentation_step_temp=None + + if self.fermentation_step_temp is not None and self.TEMP_UNIT != "C": + self.fermentation_step_temp = round((9.0 / 5.0 * float(self.fermentation_step_temp)+ 32)) + FirstWort = self.getFirstWort(hops, "bf") await self.create_recipe(RecipeName) @@ -1052,8 +1067,9 @@ class UploadController: cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None) if cooldown_sensor is None or cooldown_sensor == '': cooldown_sensor = self.boilkettle.sensor # fall back to boilkettle sensor if no other sensor is specified - step_timer = "" - step_temp = int(self.CoolDownTemp) + step_timer = "" + + step_temp = int(self.CoolDownTemp) if (self.fermentation_step_temp is None or self.fermentation_step_temp <= int(self.CoolDownTemp)) else self.fermentation_step_temp step_string = { "name": "Cooldown", "props": { "Kettle": self.boilid, From ac1d407e0cba2342f5658488343ebfe81a6fb5d1 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:56:41 +0200 Subject: [PATCH 15/16] change requirements --- cbpi/__init__.py | 2 +- requirements.txt | 8 ++++---- setup.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 526d131..3064536 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a6" +__version__ = "4.4.3.a7" __codename__ = "Yeast Starter" diff --git a/requirements.txt b/requirements.txt index b1a8823..42e0495 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ typing-extensions>=4 -aiohttp==3.9.4 +aiohttp==3.9.5 aiohttp-auth==0.1.1 aiohttp-route-decorator==0.1.4 aiohttp-security==0.5.0 @@ -8,7 +8,7 @@ aiohttp-swagger==1.0.16 async-timeout==4.0.3 aiojobs==1.2.1 aiosqlite==0.17.0 -cryptography==42.0.5 +cryptography==42.0.8 pyopenssl==24.1.0 requests==2.32.2 voluptuous==0.14.2 @@ -20,8 +20,8 @@ numpy==2.0.0 cbpi4gui click==8.1.7 importlib_metadata==4.11.1 -aiomqtt==2.1.0 -psutil==5.9.8 +aiomqtt==2.2.0 +psutil==6.0.0 zipp>=0.5 colorama==0.4.6 pytest-aiohttp diff --git a/setup.py b/setup.py index 2ec0a18..fa17ff5 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ setup(name='cbpi4', long_description_content_type='text/markdown', install_requires=[ "typing-extensions>=4", - "aiohttp==3.9.4", + "aiohttp==3.9.5", "aiohttp-auth==0.1.1", "aiohttp-route-decorator==0.1.4", "aiohttp-security==0.5.0", @@ -48,7 +48,7 @@ setup(name='cbpi4', "async-timeout==4.0.3", "aiojobs==1.2.1 ", "aiosqlite==0.17.0", - "cryptography==42.0.5", + "cryptography==42.0.8", "pyopenssl==24.1.0", "requests==2.32.2", "voluptuous==0.14.2", @@ -56,10 +56,10 @@ setup(name='cbpi4', 'click==8.1.7', 'shortuuid==1.0.13', 'tabulate==0.9.0', - 'aiomqtt==2.1.0', + 'aiomqtt==2.2.0', 'inquirer==3.2.4', 'colorama==0.4.6', - 'psutil==5.9.8', + 'psutil==6.0.0', 'cbpi4gui', 'importlib_metadata', 'numpy==2.0.0', From 90289ef9498256516d7dee603a136e36aa072e03 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:25:27 +0200 Subject: [PATCH 16/16] bump version --- cbpi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 3064536..b809b20 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3.a7" +__version__ = "4.4.3" __codename__ = "Yeast Starter"