From 57572c777eec1846273f9418e1029568194ecf07 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:07:37 +0200 Subject: [PATCH 01/31] add check for logtime parameter in http_system endpoint as first test to address issue #132 --- cbpi/__init__.py | 2 +- cbpi/http_endpoints/http_system.py | 50 ++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 92c0b96..77ce4f9 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.0" +__version__ = "4.4.1.a1" __codename__ = "Yeast Starter" diff --git a/cbpi/http_endpoints/http_system.py b/cbpi/http_endpoints/http_system.py index 8de8af2..39b43b0 100644 --- a/cbpi/http_endpoints/http_system.py +++ b/cbpi/http_endpoints/http_system.py @@ -165,31 +165,49 @@ class SystemHttpEndpoints: description: Zip and download craftbeerpi.service log tags: - System + parameters: + - name: "logtime" + in: "path" + description: "Logtime in hours" + required: true + type: "integer" + format: "int64" responses: "200": description: successful operation content: # Response body application/zip: # Media type """ - logtime = request.match_info['logtime'] - await self.controller.downloadlog(logtime) - filename = "cbpi4_log.zip" - file_name = pathlib.Path(os.path.join(".", filename)) + checklogtime = False + logtime = request.match_info['logtime'] + + try: + test=int(logtime) + checklogtime = True + except: + if logtime == "b": + checklogtime = True - response = web.StreamResponse( - status=200, - reason='OK', - headers={'Content-Type': 'application/zip'}, - ) - await response.prepare(request) - with open(file_name, 'rb') as file: - for line in file.readlines(): - await response.write(line) + if checklogtime: + await self.controller.downloadlog(logtime) + filename = "cbpi4_log.zip" + file_name = pathlib.Path(os.path.join(".", filename)) - await response.write_eof() - os.remove(file_name) - return response + response = web.StreamResponse( + status=200, + reason='OK', + headers={'Content-Type': 'application/zip'}, + ) + await response.prepare(request) + with open(file_name, 'rb') as file: + for line in file.readlines(): + await response.write(line) + await response.write_eof() + os.remove(file_name) + return response + else: + return web.Response(status=400, text='Need integer or "b" for logtime.') @request_mapping("/restore", method="POST", name="RestoreConfig", auth_required=False) async def restore(self, request): From 822ed8df1426e435f57e1b4313b3a513ce08f100 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sat, 27 Apr 2024 17:15:06 +0200 Subject: [PATCH 02/31] compatibility test to support compressoractor plugin --- cbpi/__init__.py | 2 +- cbpi/api/actor.py | 1 + cbpi/api/dataclasses.py | 5 +++-- cbpi/controller/actor_controller.py | 10 ++++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 77ce4f9..04681be 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a1" +__version__ = "4.4.1.a2" __codename__ = "Yeast Starter" diff --git a/cbpi/api/actor.py b/cbpi/api/actor.py index ecf4a26..53d8855 100644 --- a/cbpi/api/actor.py +++ b/cbpi/api/actor.py @@ -21,6 +21,7 @@ class CBPiActor(metaclass=ABCMeta): self.state = False self.running = False self.power = 100 + self.timer = 0 def init(self): pass diff --git a/cbpi/api/dataclasses.py b/cbpi/api/dataclasses.py index eb545db..bd9f411 100644 --- a/cbpi/api/dataclasses.py +++ b/cbpi/api/dataclasses.py @@ -56,13 +56,14 @@ class Actor: props: Props = Props() state: bool = False power: int = 100 + timer: int = 0 type: str = None instance: str = None def __str__(self): - return "name={} props={}, state={}, type={}, power={}".format(self.name, self.props, self.state, self.type, self.power) + return "name={} props={}, state={}, type={}, power={}, timer={}".format(self.name, self.props, self.state, self.type, self.power, self.timer) def to_dict(self): - return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state(), power=self.power) + return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state(), power=self.power, timer=self.timer) class DataType(Enum): VALUE="value" diff --git a/cbpi/controller/actor_controller.py b/cbpi/controller/actor_controller.py index ae9ba9d..118d031 100644 --- a/cbpi/controller/actor_controller.py +++ b/cbpi/controller/actor_controller.py @@ -67,6 +67,16 @@ class ActorController(BasicController): except Exception as e: logging.error("Failed to update Actor {} {}".format(id, e)) + async def timeractor_update(self, id, timer): + try: + item = self.find_by_id(id) + item.timer = round(timer) + #await self.push_udpate() + self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data)))) + self.cbpi.push_update("cbpi/actorupdate/{}".format(id), item.to_dict()) + except Exception as e: + logging.error("Failed to update Actor {} {}".format(id, e)) + async def ws_actor_update(self): try: #await self.push_udpate() From a818b471a74eb105e1ad5cdf1b73be010b37e96a Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Mon, 29 Apr 2024 17:32:13 +0200 Subject: [PATCH 03/31] fix for kbh as temps were not imported correctly issue was introduced woth commit: https://github.com/PiBrewing/craftbeerpi4/commit/72ea6ac2d7d47987643ddef75843ade547d96b40 --- cbpi/__init__.py | 2 +- cbpi/controller/upload_controller.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 04681be..875186b 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a2" +__version__ = "4.4.1.a3" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index 46f16e0..f86b6e7 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -185,13 +185,12 @@ class UploadController: row = c.fetchone() try: if self.cbpi.config.get("TEMP_UNIT", "C") == "C": - mashin_temp = str(float(row[0])) + mashin_temp = str(int(row[0])) else: - mashin_temp = str(round(9.0 / 5.0 * float(row[0]) + 32)) + mashin_temp = str(round(9.0 / 5.0 * int(row[0]) + 32)) except: pass - logging.error(mashin_temp) # get the hop addition times c.execute('SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze <> 1 AND SudID = ?', (Recipe_ID,)) hops = c.fetchall() @@ -242,7 +241,7 @@ class UploadController: "AutoMode": self.AutoMode, "Kettle": self.id, "Sensor": self.kettle.sensor, - "Temp": str(float(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * float(row[1]) + 32)), + "Temp": str(int(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * int(row[1]) + 32)), "Timer": "0", "Notification": "Target temperature reached. Please add malt." }, @@ -259,7 +258,7 @@ class UploadController: "AutoMode": self.AutoMode, "Kettle": self.id, "Sensor": self.kettle.sensor, - "Temp": str(float(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * float(row[1]) + 32)), + "Temp": str(int(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * int(row[1]) + 32)), "Timer": str(int(row[2])) }, "status_text": "", @@ -267,7 +266,7 @@ class UploadController: "type": step_type } await self.create_step(step_string) - + # MashOut -> Notification step that sends notification and waits for user input to move to next step (AutoNext=No) if self.mashout == "NotificationStep": step_string = { "name": "Lautering", @@ -800,6 +799,7 @@ class UploadController: else: step_temp = str(round((9.0 / 5.0 * int(step['stepTemp']) + 32))) + logging.error(step_temp) sensor = self.kettle.sensor if MashIn_Flag == True: From 0cac23e73025ebf3808a4fea65ec39b4454c694d Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Mon, 29 Apr 2024 17:34:44 +0200 Subject: [PATCH 04/31] remove test logging --- cbpi/controller/upload_controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cbpi/controller/upload_controller.py b/cbpi/controller/upload_controller.py index f86b6e7..2c54e4b 100644 --- a/cbpi/controller/upload_controller.py +++ b/cbpi/controller/upload_controller.py @@ -266,7 +266,7 @@ class UploadController: "type": step_type } await self.create_step(step_string) - + # MashOut -> Notification step that sends notification and waits for user input to move to next step (AutoNext=No) if self.mashout == "NotificationStep": step_string = { "name": "Lautering", @@ -799,7 +799,6 @@ class UploadController: else: step_temp = str(round((9.0 / 5.0 * int(step['stepTemp']) + 32))) - logging.error(step_temp) sensor = self.kettle.sensor if MashIn_Flag == True: From ffacde41aa899407b728d943f7a4612d8f6a55d9 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 16:50:24 +0200 Subject: [PATCH 05/31] test requirement python-systemd --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fab42ac..fb33bd4 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,8 @@ setup(name='cbpi4', 'importlib_metadata', 'numpy==1.26.4', 'pandas==2.2.2'] + ( - ['rpi-lgpio'] if raspberrypi else [] ), + ['rpi-lgpio'] if raspberrypi else [] ) + ( + ['python-systemd'] if localsystem == "Linux" else [] ), dependency_links=[ 'https://testpypi.python.org/pypi', From fd2a8b2988bb3da16a79abd4b6af9104d76bebf6 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 16:56:05 +0200 Subject: [PATCH 06/31] change requirement --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fb33bd4..c6cafe3 100644 --- a/setup.py +++ b/setup.py @@ -65,7 +65,7 @@ setup(name='cbpi4', 'numpy==1.26.4', 'pandas==2.2.2'] + ( ['rpi-lgpio'] if raspberrypi else [] ) + ( - ['python-systemd'] if localsystem == "Linux" else [] ), + ['systemd-python'] if localsystem == "Linux" else [] ), dependency_links=[ 'https://testpypi.python.org/pypi', From 3f74bd9de55f75d651299c2aba40f1e6a12924f3 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 19:25:56 +0200 Subject: [PATCH 07/31] test on log download --- cbpi/controller/system_controller.py | 79 +++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index ebf6f42..6e890e4 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -15,6 +15,14 @@ import zipfile import socket import importlib from tabulate import tabulate +from datetime import datetime, timedelta + +try: + from systemd import journal + systemd_available=True +except Exception: + logger.warning("Failed to load systemd library. logfile download not available") + systemd_available=False class SystemController: @@ -64,7 +72,7 @@ class SystemController: print(e) return tabulate(result, headers="keys") - async def downloadlog(self, logtime): + async def downloadlog_old(self, logtime): filename = "cbpi4.log" fullname = pathlib.Path(os.path.join(".",filename)) pluginname = "cbpi4_plugins.txt" @@ -123,6 +131,75 @@ class SystemController: logging.info(e) self.cbpi.notify("Error", "Removal of original files failed: {}".format(e), NotificationType.ERROR) + async def downloadlog(self, logtime): + filename = "cbpi4.log" + fullname = pathlib.Path(os.path.join(".",filename)) + pluginname = "cbpi4_plugins.txt" + fullpluginname = pathlib.Path(os.path.join(".",pluginname)) + actorname = "cbpi4_actors.txt" + fullactorname = pathlib.Path(os.path.join(".",actorname)) + sensorname = "cbpi4_sensors.txt" + fullsensorname = pathlib.Path(os.path.join(".",sensorname)) + kettlename = "cbpi4_kettles.txt" + fullkettlename = pathlib.Path(os.path.join(".",kettlename)) + + output_filename="cbpi4_log.zip" + + if logtime == "b": + os.system('journalctl -b -u craftbeerpi.service --output cat > {}'.format(fullname)) + else: + if systemd_available: + result=[] + #os.system('journalctl --since \"{} hours ago\" -u craftbeerpi.service --output cat > {}'.format(logtime, fullname)) + j = journal.Reader() + j.add_match(_SYSTEMD_UNIT="craftbeerpi.service") + since = datetime.now() - timedelta(hours=int(logtime)) + j.seek_realtime(since) + for entry in j: + result.append(entry) + logging.error(result) + + + plugins = await self.plugins_list() + + with open(fullpluginname, 'w') as f: + f.write(plugins) + + #os.system('echo "{}" >> {}'.format(plugins,fullpluginname)) + + try: + actors = self.cbpi.actor.get_state() + json.dump(actors['data'],open(fullactorname,'w'),indent=4, sort_keys=True) + sensors = self.cbpi.sensor.get_state() + json.dump(sensors['data'],open(fullsensorname,'w'),indent=4, sort_keys=True) + kettles = self.cbpi.kettle.get_state() + json.dump(kettles['data'],open(fullkettlename,'w'),indent=4, sort_keys=True) + except Exception as e: + logging.info(e) + self.cbpi.notify("Error", "Creation of files failed: {}".format(e), NotificationType.ERROR) + + try: + zipObj=zipfile.ZipFile(output_filename , 'w', zipfile.ZIP_DEFLATED) + zipObj.write(fullname) + zipObj.write(fullpluginname) + zipObj.write(fullactorname) + zipObj.write(fullsensorname) + zipObj.write(fullkettlename) + zipObj.close() + except Exception as e: + logging.info(e) + self.cbpi.notify("Error", "Zip creation failed: {}".format(e), NotificationType.ERROR) + + try: + os.remove(fullname) + os.remove(fullpluginname) + os.remove(fullactorname) + os.remove(fullsensorname) + os.remove(fullkettlename) + except Exception as e: + logging.info(e) + self.cbpi.notify("Error", "Removal of original files failed: {}".format(e), NotificationType.ERROR) + def allowed_file(self, filename, extension): return '.' in filename and filename.rsplit('.', 1)[1] in set([extension]) From 5d1c1eef2ea99a8360c13e79698e948e6b3b98e2 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 19:31:59 +0200 Subject: [PATCH 08/31] fix --- cbpi/__init__.py | 2 +- cbpi/controller/system_controller.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 875186b..ddc10e3 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a3" +__version__ = "4.4.1.a4" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 6e890e4..47f192e 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -156,7 +156,7 @@ class SystemController: since = datetime.now() - timedelta(hours=int(logtime)) j.seek_realtime(since) for entry in j: - result.append(entry) + result.append(entry['MESSAGE']) logging.error(result) From e12d437ed0df67e5ce86bce0d4ff5558462ab415 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 19:35:36 +0200 Subject: [PATCH 09/31] add write logfile --- cbpi/controller/system_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 47f192e..47de359 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -159,6 +159,8 @@ class SystemController: result.append(entry['MESSAGE']) logging.error(result) + with open(fullname, 'w') as f: + f.write(result) plugins = await self.plugins_list() From d7ff5ca712d187870a65b760c19184cb1df3905b Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 19:37:22 +0200 Subject: [PATCH 10/31] check error --- cbpi/controller/system_controller.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 47de359..70c804a 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -158,9 +158,11 @@ class SystemController: for entry in j: result.append(entry['MESSAGE']) logging.error(result) - - with open(fullname, 'w') as f: - f.write(result) + try: + with open(fullname, 'w') as f: + f.write(result) + except Exception as e: + logging.error(e) plugins = await self.plugins_list() From 00ac853488183a833dd1500032d6786daa02632e Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 19:55:29 +0200 Subject: [PATCH 11/31] write logfile line by line --- cbpi/controller/system_controller.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 70c804a..89387fc 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -157,15 +157,14 @@ class SystemController: j.seek_realtime(since) for entry in j: result.append(entry['MESSAGE']) - logging.error(result) try: with open(fullname, 'w') as f: - f.write(result) + for line in result: + f.write(f"{line}\n") except Exception as e: logging.error(e) plugins = await self.plugins_list() - with open(fullpluginname, 'w') as f: f.write(plugins) From 1f56fa8fe1dd2fe8491c1b96c429556c52afc164 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 1 May 2024 20:36:29 +0200 Subject: [PATCH 12/31] fixas 'last boot log' was always overwritten --- cbpi/controller/system_controller.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 89387fc..c5b4256 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -157,12 +157,12 @@ class SystemController: j.seek_realtime(since) for entry in j: result.append(entry['MESSAGE']) - try: - with open(fullname, 'w') as f: - for line in result: - f.write(f"{line}\n") - except Exception as e: - logging.error(e) + try: + with open(fullname, 'w') as f: + for line in result: + f.write(f"{line}\n") + except Exception as e: + logging.error(e) plugins = await self.plugins_list() with open(fullpluginname, 'w') as f: From 4ca441bba39b6973dc02543bfc80b324b795a9bf Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 07:08:23 +0200 Subject: [PATCH 13/31] test for log since last boot --- cbpi/__init__.py | 2 +- cbpi/controller/system_controller.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index ddc10e3..9c63548 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a4" +__version__ = "4.4.1.a5" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index c5b4256..104ea71 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -146,7 +146,22 @@ class SystemController: output_filename="cbpi4_log.zip" if logtime == "b": - os.system('journalctl -b -u craftbeerpi.service --output cat > {}'.format(fullname)) + if systemd_available: + #os.system('journalctl -b -u craftbeerpi.service --output cat > {}'.format(fullname)) + j = journal.Reader() + j.add_match(_TRANSPORT="kernel") + result=[] + for entry in j: + message=entry['MESSAGE'] + if message.find("Booting") != -1: + result.append(entry['__REALTIME_TIMESTAMP']) + j.add_match(_SYSTEMD_UNIT="craftbeerpi.service") + j.seek_realtime(result[-1]) + for entry in j: + timestamp=entry['__REALTIME_TIMESTAMP'] + message=entry['MESSAGE'] + print(message) + else: if systemd_available: result=[] From f5c242d7c856e8f7be2cac8e8ca4dcb51d779515 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 07:32:38 +0200 Subject: [PATCH 14/31] write log since last boot to file --- cbpi/controller/system_controller.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 104ea71..ed13271 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -148,19 +148,25 @@ class SystemController: if logtime == "b": if systemd_available: #os.system('journalctl -b -u craftbeerpi.service --output cat > {}'.format(fullname)) - j = journal.Reader() - j.add_match(_TRANSPORT="kernel") + b = journal.Reader() + b.add_match(_TRANSPORT="kernel") result=[] - for entry in j: + for entry in b: message=entry['MESSAGE'] if message.find("Booting") != -1: result.append(entry['__REALTIME_TIMESTAMP']) + j = journal.Reader() j.add_match(_SYSTEMD_UNIT="craftbeerpi.service") j.seek_realtime(result[-1]) + result=[] for entry in j: - timestamp=entry['__REALTIME_TIMESTAMP'] - message=entry['MESSAGE'] - print(message) + result.append(entry['MESSAGE']) + try: + with open(fullname, 'w') as f: + for line in result: + f.write(f"{line}\n") + except Exception as e: + logging.error(e) else: if systemd_available: From 858c71856da9ec61e92a730011fc8300fea4cdee Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 12:45:44 +0200 Subject: [PATCH 15/31] simplify log download --- cbpi/__init__.py | 2 +- cbpi/controller/system_controller.py | 56 ++++++++-------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 9c63548..134dd57 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a5" +__version__ = "4.4.1.a6" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index ed13271..7f45365 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -144,53 +144,29 @@ class SystemController: fullkettlename = pathlib.Path(os.path.join(".",kettlename)) output_filename="cbpi4_log.zip" - - if logtime == "b": - if systemd_available: - #os.system('journalctl -b -u craftbeerpi.service --output cat > {}'.format(fullname)) - b = journal.Reader() - b.add_match(_TRANSPORT="kernel") - result=[] - for entry in b: - message=entry['MESSAGE'] - if message.find("Booting") != -1: - result.append(entry['__REALTIME_TIMESTAMP']) - j = journal.Reader() - j.add_match(_SYSTEMD_UNIT="craftbeerpi.service") - j.seek_realtime(result[-1]) - result=[] - for entry in j: - result.append(entry['MESSAGE']) - try: - with open(fullname, 'w') as f: - for line in result: - f.write(f"{line}\n") - except Exception as e: - logging.error(e) - - else: - if systemd_available: - result=[] - #os.system('journalctl --since \"{} hours ago\" -u craftbeerpi.service --output cat > {}'.format(logtime, fullname)) - j = journal.Reader() - j.add_match(_SYSTEMD_UNIT="craftbeerpi.service") + result=[] + if systemd_available: + j = journal.Reader() + if logtime == "b": + j.this_boot() + else: since = datetime.now() - timedelta(hours=int(logtime)) j.seek_realtime(since) - for entry in j: - result.append(entry['MESSAGE']) - try: - with open(fullname, 'w') as f: - for line in result: - f.write(f"{line}\n") - except Exception as e: - logging.error(e) + j.add_match(_SYSTEMD_UNIT="craftbeerpi.service") + + for entry in j: + result.append(entry['MESSAGE']) + try: + with open(fullname, 'w') as f: + for line in result: + f.write(f"{line}\n") + except Exception as e: + logging.error(e) plugins = await self.plugins_list() with open(fullpluginname, 'w') as f: f.write(plugins) - #os.system('echo "{}" >> {}'.format(plugins,fullpluginname)) - try: actors = self.cbpi.actor.get_state() json.dump(actors['data'],open(fullactorname,'w'),indent=4, sort_keys=True) From f5593fdc1f9bbbdc8f989fd004d31930dd4a67f2 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 12:52:22 +0200 Subject: [PATCH 16/31] remove old downloadlog function --- cbpi/controller/system_controller.py | 59 ---------------------------- 1 file changed, 59 deletions(-) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 7f45365..ae8a346 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -72,65 +72,6 @@ class SystemController: print(e) return tabulate(result, headers="keys") - async def downloadlog_old(self, logtime): - filename = "cbpi4.log" - fullname = pathlib.Path(os.path.join(".",filename)) - pluginname = "cbpi4_plugins.txt" - fullpluginname = pathlib.Path(os.path.join(".",pluginname)) - actorname = "cbpi4_actors.txt" - fullactorname = pathlib.Path(os.path.join(".",actorname)) - sensorname = "cbpi4_sensors.txt" - fullsensorname = pathlib.Path(os.path.join(".",sensorname)) - kettlename = "cbpi4_kettles.txt" - fullkettlename = pathlib.Path(os.path.join(".",kettlename)) - - output_filename="cbpi4_log.zip" - - if logtime == "b": - os.system('journalctl -b -u craftbeerpi.service --output cat > {}'.format(fullname)) - else: - os.system('journalctl --since \"{} hours ago\" -u craftbeerpi.service --output cat > {}'.format(logtime, fullname)) - - plugins = await self.plugins_list() - - with open(fullpluginname, 'w') as f: - f.write(plugins) - - #os.system('echo "{}" >> {}'.format(plugins,fullpluginname)) - - try: - actors = self.cbpi.actor.get_state() - json.dump(actors['data'],open(fullactorname,'w'),indent=4, sort_keys=True) - sensors = self.cbpi.sensor.get_state() - json.dump(sensors['data'],open(fullsensorname,'w'),indent=4, sort_keys=True) - kettles = self.cbpi.kettle.get_state() - json.dump(kettles['data'],open(fullkettlename,'w'),indent=4, sort_keys=True) - except Exception as e: - logging.info(e) - self.cbpi.notify("Error", "Creation of files failed: {}".format(e), NotificationType.ERROR) - - try: - zipObj=zipfile.ZipFile(output_filename , 'w', zipfile.ZIP_DEFLATED) - zipObj.write(fullname) - zipObj.write(fullpluginname) - zipObj.write(fullactorname) - zipObj.write(fullsensorname) - zipObj.write(fullkettlename) - zipObj.close() - except Exception as e: - logging.info(e) - self.cbpi.notify("Error", "Zip creation failed: {}".format(e), NotificationType.ERROR) - - try: - os.remove(fullname) - os.remove(fullpluginname) - os.remove(fullactorname) - os.remove(fullsensorname) - os.remove(fullkettlename) - except Exception as e: - logging.info(e) - self.cbpi.notify("Error", "Removal of original files failed: {}".format(e), NotificationType.ERROR) - async def downloadlog(self, logtime): filename = "cbpi4.log" fullname = pathlib.Path(os.path.join(".",filename)) From ed7580b5b0071ab37a6bced0a9f475183f67fd18 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 17:39:43 +0200 Subject: [PATCH 17/31] fix for logging in case systemd is not available --- .devcontainer/Dockerfile | 1 + cbpi/__init__.py | 2 +- cbpi/controller/system_controller.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 364d53d..5930049 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,6 +4,7 @@ RUN apt-get update \ && apt-get upgrade -y RUN apt-get install --no-install-recommends -y \ libatlas-base-dev \ + libsystemd-dev \ libffi-dev \ python3-pip \ && rm -rf /var/lib/apt/lists/* diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 134dd57..99bbddb 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a6" +__version__ = "4.4.1.a7" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index ae8a346..61b7c75 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -21,7 +21,7 @@ try: from systemd import journal systemd_available=True except Exception: - logger.warning("Failed to load systemd library. logfile download not available") + logging.warning("Failed to load systemd library. logfile download not available") systemd_available=False class SystemController: From d20b0af71a4b07664bec1cccc9a017f4869073fe Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 17:41:01 +0200 Subject: [PATCH 18/31] add systemd-python to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 96370cf..46df2da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,3 +27,4 @@ colorama==0.4.6 pytest-aiohttp coverage==6.3.1 inquirer==3.2.4 +systemd-python From daf9495722ed8b86c7a5a1b23bfcdec92c412bcc Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 18:07:57 +0200 Subject: [PATCH 19/31] test workflow --- .github/workflows/build.yml | 5 ++++- Dockerfile | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 976fe56..c880778 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,10 @@ jobs: - name: Setup python environment uses: actions/setup-python@v2 with: - python-version: '3.9' + python-version: '3.11' + + - name: Install packages + run: sudo apt install -y libsystemd-dev - name: Clean run: python setup.py clean --all diff --git a/Dockerfile b/Dockerfile index de45f14..b31af88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ RUN apt-get update \ && apt-get upgrade -y RUN apt-get install --no-install-recommends -y \ libatlas-base-dev \ + libsystemd-dev \ libffi-dev \ python3-pip \ && rm -rf /var/lib/apt/lists/* From 07ffe0dea827615622ed6a7b26fa3525ba1f8c83 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 2 May 2024 19:02:02 +0200 Subject: [PATCH 20/31] changes for devcontainer --- .devcontainer/Dockerfile | 2 +- .vscode/launch.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5930049..4b72b28 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/vscode/devcontainers/python:3.11-bullseye +FROM mcr.microsoft.com/vscode/devcontainers/python:3.11-bookworm RUN apt-get update \ && apt-get upgrade -y diff --git a/.vscode/launch.json b/.vscode/launch.json index 0b0fdc4..334051b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ { "name": "run CraftBeerPi4", - "type": "python", + "type": "debugpy", "request": "launch", "module": "run", "args": [ @@ -20,7 +20,7 @@ { "name": "create CraftBeerPi4 plugin", - "type": "python", + "type": "debugpy", "request": "launch", "module": "run", "args": ["--config-folder-path=./.devcontainer/cbpi-dev-config", "create"] @@ -28,7 +28,7 @@ { "name": "setup CraftBeerPi4: create config folder structure", - "type": "python", + "type": "debugpy", "request": "launch", "module": "run", "args": ["--config-folder-path=./.devcontainer/cbpi-dev-config", "setup"] @@ -36,7 +36,7 @@ { "name": "run tests", - "type": "python", + "type": "debugpy", "request": "launch", "module": "pytest", "args": ["tests"] From 2180cad28551621b1d02aea9fa8d956713d94824 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 9 May 2024 19:18:49 +0200 Subject: [PATCH 21/31] change default values for fermenter hysteresis to values that will work if users don't read the documentation and don't enter values at all... --- cbpi/__init__.py | 2 +- cbpi/extension/FermenterHysteresis/__init__.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 99bbddb..afa5ebb 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a7" +__version__ = "4.4.1.a8" __codename__ = "Yeast Starter" diff --git a/cbpi/extension/FermenterHysteresis/__init__.py b/cbpi/extension/FermenterHysteresis/__init__.py index 48c17c8..9f7d0fe 100644 --- a/cbpi/extension/FermenterHysteresis/__init__.py +++ b/cbpi/extension/FermenterHysteresis/__init__.py @@ -55,9 +55,9 @@ class FermenterHysteresis(CBPiFermenterLogic): async def run(self): try: - self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0)) + self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0.2)) self.heater_offset_max = float(self.props.get("HeaterOffsetOff", 0)) - self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0)) + self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0.2)) self.cooler_offset_max = float(self.props.get("CoolerOffsetOff", 0)) self.heater_max_power = int(self.props.get("HeaterMaxPower", 100)) self.cooler_max_power = int(self.props.get("CoolerMaxPower", 100)) @@ -153,9 +153,9 @@ class FermenterSpundingHysteresis(CBPiFermenterLogic): logging.info("No valve or pressure sensor defined") async def temperature_control(self): - self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0)) + self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0.2)) self.heater_offset_max = float(self.props.get("HeaterOffsetOff", 0)) - self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0)) + self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0.2)) self.cooler_offset_max = float(self.props.get("CoolerOffsetOff", 0)) heater = self.cbpi.actor.find_by_id(self.heater) From 9d928cd58f80392acc15b39e18d87a8298181b1e Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Fri, 10 May 2024 21:29:53 +0200 Subject: [PATCH 22/31] add date prefix to backup download name --- cbpi/__init__.py | 2 +- cbpi/controller/system_controller.py | 10 ++++++++-- cbpi/http_endpoints/http_system.py | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index afa5ebb..3dfba54 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a8" +__version__ = "4.4.1.a9" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 61b7c75..3ba5fc6 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -15,7 +15,7 @@ import zipfile import socket import importlib from tabulate import tabulate -from datetime import datetime, timedelta +from datetime import datetime, timedelta, date try: from systemd import journal @@ -48,9 +48,15 @@ class SystemController: pass async def backupConfig(self): - output_filename = "cbpi4_config" + try: + current_date = date.today() + current_date=str(current_date).replace("-","_") + output_filename = current_date+"_cbpi4_config" + except: + output_filename = "cbpi4_config" dir_name = pathlib.Path(self.cbpi.config_folder.get_file_path('')) shutil.make_archive(output_filename, 'zip', dir_name) + return output_filename+".zip" async def plugins_list(self): result = [] diff --git a/cbpi/http_endpoints/http_system.py b/cbpi/http_endpoints/http_system.py index 39b43b0..1827449 100644 --- a/cbpi/http_endpoints/http_system.py +++ b/cbpi/http_endpoints/http_system.py @@ -140,9 +140,10 @@ class SystemHttpEndpoints: content: # Response body application/zip: # Media type """ - await self.controller.backupConfig() - filename = "cbpi4_config.zip" + filename = await self.controller.backupConfig() + #filename = "cbpi4_config.zip" file_name = pathlib.Path(os.path.join(".", filename)) + logging.error(file_name) response = web.StreamResponse( status=200, From f3a8fdf1a2aabc4f7945f80259ea8389e3a282a8 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Fri, 10 May 2024 22:16:18 +0200 Subject: [PATCH 23/31] remove old backup files locally prior to generation of new backup file --- cbpi/__init__.py | 2 +- cbpi/controller/system_controller.py | 8 ++++++++ cbpi/http_endpoints/http_system.py | 3 +-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 3dfba54..f4f5438 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a9" +__version__ = "4.4.1.a10" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index 3ba5fc6..a56f30b 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -16,6 +16,7 @@ import socket import importlib from tabulate import tabulate from datetime import datetime, timedelta, date +import glob try: from systemd import journal @@ -48,6 +49,13 @@ class SystemController: pass async def backupConfig(self): + files=glob.glob('*cbpi4_config*.zip') + for f in files: + try: + os.remove(f) + except Exception as e: + logging.error("Cannot remove old config backup: {}".format(e)) + try: current_date = date.today() current_date=str(current_date).replace("-","_") diff --git a/cbpi/http_endpoints/http_system.py b/cbpi/http_endpoints/http_system.py index 1827449..906d49e 100644 --- a/cbpi/http_endpoints/http_system.py +++ b/cbpi/http_endpoints/http_system.py @@ -143,8 +143,7 @@ class SystemHttpEndpoints: filename = await self.controller.backupConfig() #filename = "cbpi4_config.zip" file_name = pathlib.Path(os.path.join(".", filename)) - logging.error(file_name) - + response = web.StreamResponse( status=200, reason='OK', From f594ed04a0e5e04275a8d55d738056f3f53f114d Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Tue, 28 May 2024 21:51:17 +0200 Subject: [PATCH 24/31] added rangewarning for testing (different color in dashboard -> dev version of ui required) --- cbpi/__init__.py | 2 +- cbpi/api/dataclasses.py | 4 ++- cbpi/api/sensor.py | 34 +++++++++++++++++++++++--- cbpi/extension/httpsensor/__init__.py | 13 ++++++---- cbpi/extension/mqtt_sensor/__init__.py | 11 ++++++--- cbpi/extension/onewire/__init__.py | 14 +++++++---- cbpi/extension/onewire/config.yaml | 2 +- requirements.txt | 4 +-- setup.py | 4 +-- 9 files changed, 63 insertions(+), 25 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index f4f5438..c772fb0 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a10" +__version__ = "4.4.1.a11" __codename__ = "Yeast Starter" diff --git a/cbpi/api/dataclasses.py b/cbpi/api/dataclasses.py index bd9f411..1c403be 100644 --- a/cbpi/api/dataclasses.py +++ b/cbpi/api/dataclasses.py @@ -79,7 +79,9 @@ class Sensor: type: str = None instance: str = None datatype: DataType = DataType.VALUE - + inrange: bool = True + temp_range: float = 0 + def __str__(self): return "name={} props={}, state={}".format(self.name, self.props, self.state) def to_dict(self): diff --git a/cbpi/api/sensor.py b/cbpi/api/sensor.py index 9874ee8..341c72e 100644 --- a/cbpi/api/sensor.py +++ b/cbpi/api/sensor.py @@ -18,6 +18,10 @@ class CBPiSensor(CBPiBase, metaclass=ABCMeta): self.state = False self.running = False self.datatype=DataType.VALUE + self.inrange = True + self.temprange = 0 + self.kettle = None + self.fermenter = None def init(self): pass @@ -34,12 +38,34 @@ class CBPiSensor(CBPiBase, metaclass=ABCMeta): def get_unit(self): pass - def push_update(self, value, mqtt = True): - + def checkrange(self, value): + # if Kettle and fermenter are selected, range check is deactivated + if self.kettle is not None and self.fermenter is not None: + return True try: - self.cbpi.ws.send(dict(topic="sensorstate", id=self.id, value=value, datatype=self.datatype.value)) + if self.kettle is not None: + target_temp=float(self.kettle.target_temp) + if self.fermenter is not None: + target_temp=float(self.fermenter.target_temp) + + diff=abs(target_temp-value) + if diff>self.temprange: + return False + else: + return True + except Exception as e: + return True + + + def push_update(self, value, mqtt = True): + if self.temprange !=0: + self.inrange = self.checkrange(value) + else: + self.inrange = True + try: + self.cbpi.ws.send(dict(topic="sensorstate", id=self.id, value=value, datatype=self.datatype.value, inrange=self.inrange)) if mqtt: - self.cbpi.push_update("cbpi/sensordata/{}".format(self.id), dict(id=self.id, value=value, datatype=self.datatype.value), retain=True) + self.cbpi.push_update("cbpi/sensordata/{}".format(self.id), dict(id=self.id, value=value, datatype=self.datatype.value, inrange=self.inrange), retain=True) # self.cbpi.push_update("cbpi/sensor/{}/udpate".format(self.id), dict(id=self.id, value=value), retain=True) except: logging.error("Failed to push sensor update for sensor {}".format(self.id)) diff --git a/cbpi/extension/httpsensor/__init__.py b/cbpi/extension/httpsensor/__init__.py index c37c11e..2f50f53 100644 --- a/cbpi/extension/httpsensor/__init__.py +++ b/cbpi/extension/httpsensor/__init__.py @@ -12,10 +12,12 @@ cache = {} @parameters([Property.Text(label="Key", configurable=True, description="Http Key"), Property.Number(label="Timeout", configurable="True",unit="sec",description="Timeout in seconds to send notification (default:60 | deactivated: 0)"), - Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive (only Kettle or Fermenter to be selected)"), - Property.Fermenter(label="Fermenter", description="Reduced logging in seconds if Fermenter is inactive (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 | disabled: 0)")]) - + 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 in seconds 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 | disabled: 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 HTTPSensor(CBPiSensor): def __init__(self, cbpi, id, props): super(HTTPSensor, self).__init__(cbpi, id, props) @@ -35,11 +37,12 @@ class HTTPSensor(CBPiSensor): self.kettleid=self.props.get("Kettle", None) self.fermenterid=self.props.get("Fermenter", None) + self.temprange=float(self.props.get("TempRange", 0)) 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("HTTPSensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) + self.cbpi.notify("HTTPSensor", "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 diff --git a/cbpi/extension/mqtt_sensor/__init__.py b/cbpi/extension/mqtt_sensor/__init__.py index c5b0c0b..68db819 100644 --- a/cbpi/extension/mqtt_sensor/__init__.py +++ b/cbpi/extension/mqtt_sensor/__init__.py @@ -11,11 +11,13 @@ 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 (only Kettle or Fermenter to be selected)"), - Property.Fermenter(label="Fermenter", description="Reduced logging in seconds if Fermenter is inactive (only Kettle or Fermenter to be selected)"), + 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)")]) + 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): @@ -27,6 +29,7 @@ class MQTTSensor(CBPiSensor): 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 @@ -42,7 +45,7 @@ class MQTTSensor(CBPiSensor): 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.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) + 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 diff --git a/cbpi/extension/onewire/__init__.py b/cbpi/extension/onewire/__init__.py index 56f336f..309e410 100644 --- a/cbpi/extension/onewire/__init__.py +++ b/cbpi/extension/onewire/__init__.py @@ -52,10 +52,13 @@ class ReadThread (threading.Thread): @parameters([Property.Select(label="Sensor", options=getSensors()), Property.Number(label="offset",configurable = True, default_value = 0, description="Sensor Offset (Default is 0)"), Property.Select(label="Interval", options=[1,5,10,30,60], description="Interval in Seconds"), - Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive (only Kettle or Fermenter to be selected)"), - Property.Fermenter(label="Fermenter", description="Reduced logging in seconds if Fermenter is inactive (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 | disabled: 0)") - ]) + 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 in seconds 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 | disabled: 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 OneWire(CBPiSensor): def __init__(self, cbpi, id, props): @@ -72,6 +75,7 @@ class OneWire(CBPiSensor): if self.reducedfrequency < 0: self.reducedfrequency = 0 self.lastlog=0 + self.temprange=float(self.props.get("TempRange", 0)) self.sensor=self.get_sensor(self.id) self.kettleid=self.props.get("Kettle", None) self.fermenterid=self.props.get("Fermenter", None) @@ -79,7 +83,7 @@ class OneWire(CBPiSensor): if self.kettleid is not None and self.fermenterid is not None: self.reducedlogging=False - self.cbpi.notify("OneWire Sensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) + self.cbpi.notify("OneWire Sensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging / range warning.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) if (self.reducedfrequency != 0) and (self.interval >= self.reducedfrequency): self.reducedlogging=False self.cbpi.notify("OneWire Sensor", "Sensor '" + str(self.sensor.name) + "' has shorter or equal 'reduced logging' compared to regular interval.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)]) diff --git a/cbpi/extension/onewire/config.yaml b/cbpi/extension/onewire/config.yaml index 7f0aa15..961686c 100644 --- a/cbpi/extension/onewire/config.yaml +++ b/cbpi/extension/onewire/config.yaml @@ -1,3 +1,3 @@ -name: DummySensor +name: OneWireSensor version: 4 active: true \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 46df2da..806ce15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ aiojobs==1.2.1 aiosqlite==0.17.0 cryptography==42.0.5 pyopenssl==24.1.0 -requests==2.31.0 +requests==2.32.2 voluptuous==0.14.2 pyfiglet==1.0.2 pandas==2.2.2 @@ -20,7 +20,7 @@ numpy==1.26.4 cbpi4gui click==8.1.7 importlib_metadata==4.11.1 -aiomqtt==2.0.1 +aiomqtt==2.1.0 psutil==5.9.8 zipp>=0.5 colorama==0.4.6 diff --git a/setup.py b/setup.py index c6cafe3..266bf84 100644 --- a/setup.py +++ b/setup.py @@ -50,13 +50,13 @@ setup(name='cbpi4', "aiosqlite==0.17.0", "cryptography==42.0.5", "pyopenssl==24.1.0", - "requests==2.31.0", + "requests==2.32.2", "voluptuous==0.14.2", "pyfiglet==1.0.2", 'click==8.1.7', 'shortuuid==1.0.13', 'tabulate==0.9.0', - 'aiomqtt==2.0.1', + 'aiomqtt==2.1.0', 'inquirer==3.2.4', 'colorama==0.4.6', 'psutil==5.9.8', From e623aa2e5ebf9dac35b51537801e543c4d366790 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Thu, 30 May 2024 19:01:06 +0200 Subject: [PATCH 25/31] add notifications to ui state variables and functionality to delete notifications --- cbpi/__init__.py | 2 +- cbpi/controller/notification_controller.py | 18 +++++++++++++++++- cbpi/http_endpoints/http_notification.py | 16 +++++++++++++++- cbpi/http_endpoints/http_system.py | 1 + 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index c772fb0..94300f0 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a11" +__version__ = "4.4.1.a12" __codename__ = "Yeast Starter" diff --git a/cbpi/controller/notification_controller.py b/cbpi/controller/notification_controller.py index 1040bf0..5cbd20c 100644 --- a/cbpi/controller/notification_controller.py +++ b/cbpi/controller/notification_controller.py @@ -4,6 +4,7 @@ from cbpi.api.dataclasses import NotificationType from cbpi.api import * import logging import shortuuid +from datetime import datetime class NotificationController: def __init__(self, cbpi): @@ -15,6 +16,9 @@ class NotificationController: logging.root.addFilter(self.notify_log_event) self.callback_cache = {} self.listener = {} + self.notifications = [] + self.update_key="notificationupdate" + self.sorting=False def notify_log_event(self, record): NOTIFY_ON_ERROR = self.cbpi.config.get("NOTIFY_ON_ERROR", "No") @@ -32,6 +36,10 @@ class NotificationController: return True return True + def get_state(self): + result = self.notifications + return result + def add_listener(self, method): listener_id = shortuuid.uuid() self.listener[listener_id] = method @@ -69,8 +77,16 @@ class NotificationController: self.cbpi.ws.send(dict(id=notifcation_id, topic="notifiaction", type=type.value, title=title, message=message, action=actions, timeout=timeout)) data = dict(type=type.value, title=title, message=message, action=actions, timeout=timeout) self.cbpi.push_update(topic="cbpi/notification", data=data) + self.notifications.insert(0,[f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: {title} | {message}']) + if len(self.notifications) > 100: + self.notifications = self.notifications[:100] + self.cbpi.ws.send(dict(topic=self.update_key, data=self.notifications),self.sorting) asyncio.create_task(self._call_listener(title, message, type, action)) + def delete_all_notifications(self): + self.notifications = [] + self.cbpi.ws.send(dict(topic=self.update_key, data=self.notifications),self.sorting) + def notify_callback(self, notification_id, action_id) -> None: try: @@ -79,5 +95,5 @@ class NotificationController: asyncio.create_task(action.method()) del self.callback_cache[notification_id] except Exception as e: - self.logger.error("Failed to call notificatoin callback") + self.logger.error("Failed to call notification callback") \ No newline at end of file diff --git a/cbpi/http_endpoints/http_notification.py b/cbpi/http_endpoints/http_notification.py index b25c4e3..4fd0eff 100644 --- a/cbpi/http_endpoints/http_notification.py +++ b/cbpi/http_endpoints/http_notification.py @@ -37,4 +37,18 @@ class NotificationHttpEndpoints: action_id = request.match_info['action_id'] #print(notification_id, action_id) self.cbpi.notification.notify_callback(notification_id, action_id) - return web.Response(status=204) \ No newline at end of file + return web.Response(status=200) + + @request_mapping("/delete", method="POST", auth_required=False) + async def restart(self, request): + """ + --- + description: DeleteNotifications + tags: + - Notification + responses: + "200": + description: successful operation + """ + self.cbpi.notification.delete_all_notifications() + return web.Response(status=200) \ No newline at end of file diff --git a/cbpi/http_endpoints/http_system.py b/cbpi/http_endpoints/http_system.py index 906d49e..7a556d2 100644 --- a/cbpi/http_endpoints/http_system.py +++ b/cbpi/http_endpoints/http_system.py @@ -42,6 +42,7 @@ class SystemHttpEndpoints: step=self.cbpi.step.get_state(), fermentersteps=self.cbpi.fermenter.get_fermenter_steps(), config=self.cbpi.config.get_state(), + notifications=self.cbpi.notification.get_state(), version=__version__, guiversion=version, codename=__codename__) From cdf70b8eb7bd79108b39d09b18770b1001a3905a Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Fri, 31 May 2024 07:38:15 +0200 Subject: [PATCH 26/31] remove restored config from config folder (bug fix) --- cbpi/__init__.py | 2 +- cbpi/configFolder.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 94300f0..5c5acfd 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a12" +__version__ = "4.4.1.a13" __codename__ = "Yeast Starter" diff --git a/cbpi/configFolder.py b/cbpi/configFolder.py index 54cd4eb..c46d28b 100644 --- a/cbpi/configFolder.py +++ b/cbpi/configFolder.py @@ -74,6 +74,7 @@ class ConfigFolder: print(f"Changing owner and group of config folder recursively to {owner}:{group}") self.recursive_chown(output_path, owner, group) print("Removing backup file") + os.remove(backupfile) print("contents of restored_config.zip file have been restored.") print("in case of a partial backup you will still be prompted to run 'cbpi setup'.") else: From 6cc4d3e19b8f0deb9746293c0dcf5e0b5cb1b031 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Fri, 31 May 2024 15:36:19 +0200 Subject: [PATCH 27/31] show missing content in commandline if restore of config fails --- cbpi/__init__.py | 2 +- cbpi/configFolder.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 5c5acfd..a616c03 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a13" +__version__ = "4.4.1.a14" __codename__ = "Yeast Starter" diff --git a/cbpi/configFolder.py b/cbpi/configFolder.py index c46d28b..bbfc51b 100644 --- a/cbpi/configFolder.py +++ b/cbpi/configFolder.py @@ -46,16 +46,28 @@ class ConfigFolder: if os.path.exists(os.path.join(backupfile)) is True: print("***************************************************") print("Found backup of config. Starting restore") - required_content=['dashboard/', 'recipes/', 'upload/', 'config.json', 'config.yaml'] + required_content=['dashboard/', + 'recipes/', + 'upload/', + 'sensor.json', + 'actor.json', + 'kettle.json', + 'config.json', + 'craftbeerpi.template', + 'chromium.desktop', + 'config.yaml'] zip=zipfile.ZipFile(backupfile) zip_content_list = zip.namelist() zip_content = True + missing_content=[] print("Checking content of zip file") + for content in required_content: try: check = zip_content_list.index(content) except: zip_content = False + missing_content.append(content) if zip_content == True: print("Found correct content. Starting Restore process") @@ -78,7 +90,9 @@ class ConfigFolder: print("contents of restored_config.zip file have been restored.") print("in case of a partial backup you will still be prompted to run 'cbpi setup'.") else: + zip.close() print("Wrong Content in zip file. No restore possible") + print(f'These files are missing {missing_content}') print("renaming zip file so it will be ignored on the next start") try: os.rename(backupfile, os.path.join(self.configFolderPath, "UNRESTORABLE_restored_config.zip")) From e7c7d85731be13a81f1a858a07893e8b711b53da Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Fri, 31 May 2024 16:30:35 +0200 Subject: [PATCH 28/31] show restore errors in notification after startup --- cbpi/__init__.py | 2 +- cbpi/configFolder.py | 18 ++++++++++++++---- cbpi/controller/notification_controller.py | 12 ++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index a616c03..48db3db 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a14" +__version__ = "4.4.1.a15" __codename__ = "Yeast Starter" diff --git a/cbpi/configFolder.py b/cbpi/configFolder.py index bbfc51b..4d354b0 100644 --- a/cbpi/configFolder.py +++ b/cbpi/configFolder.py @@ -87,12 +87,22 @@ class ConfigFolder: self.recursive_chown(output_path, owner, group) print("Removing backup file") os.remove(backupfile) - print("contents of restored_config.zip file have been restored.") - print("in case of a partial backup you will still be prompted to run 'cbpi setup'.") + Line1="Contents of restored_config.zip file have been restored." + Line2="In case of a partial backup you will still be prompted to run 'cbpi setup'." + print(Line1) + print(Line2) else: zip.close() - print("Wrong Content in zip file. No restore possible") - print(f'These files are missing {missing_content}') + Line1="Wrong Content in zip file. No restore possible" + Line2=f'These files are missing {missing_content}' + print(Line1) + print(Line2) + restorelogfile = os.path.join(self.configFolderPath, "restore_error.log") + f = open(restorelogfile, "w") + f.write(Line1+"\n") + f.write(Line2+"\n") + f.close() + print("renaming zip file so it will be ignored on the next start") try: os.rename(backupfile, os.path.join(self.configFolderPath, "UNRESTORABLE_restored_config.zip")) diff --git a/cbpi/controller/notification_controller.py b/cbpi/controller/notification_controller.py index 5cbd20c..212ab0e 100644 --- a/cbpi/controller/notification_controller.py +++ b/cbpi/controller/notification_controller.py @@ -5,6 +5,7 @@ from cbpi.api import * import logging import shortuuid from datetime import datetime +import os class NotificationController: def __init__(self, cbpi): @@ -19,6 +20,17 @@ class NotificationController: self.notifications = [] self.update_key="notificationupdate" self.sorting=False + self.check_startup_message() + + def check_startup_message(self): + self.restore_error = self.cbpi.config_folder.get_file_path("restore_error.log") + try: + with open(self.restore_error) as f: + for line in f: + self.notifications.insert(0,[f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: Restore Error | {line}']) + os.remove(self.restore_error) + except Exception as e: + pass def notify_log_event(self, record): NOTIFY_ON_ERROR = self.cbpi.config.get("NOTIFY_ON_ERROR", "No") From 14e0190bb90f0d8c6730be712f42d52258affbe3 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Sat, 1 Jun 2024 19:14:56 +0200 Subject: [PATCH 29/31] add timestamp to log files in log file folder and allow to download current logfile under windows --- cbpi/__init__.py | 2 +- cbpi/cli.py | 4 +++- cbpi/controller/system_controller.py | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 48db3db..61bc3b4 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a15" +__version__ = "4.4.1.a16" __codename__ = "Yeast Starter" diff --git a/cbpi/cli.py b/cbpi/cli.py index b8275ff..c4e21fa 100644 --- a/cbpi/cli.py +++ b/cbpi/cli.py @@ -287,7 +287,9 @@ def main(context, config_folder_path, logs_folder_path, debug_log_level): logger.info(f"logs folder '{logs_folder_path}' doesnt exist and we are trying to create it") pathlib.Path(logs_folder_path).mkdir(parents=True, exist_ok=True) logger.info(f"logs folder '{logs_folder_path}' successfully created") - logger.addHandler(logging.handlers.RotatingFileHandler(os.path.join(logs_folder_path, f"cbpi.log"), maxBytes=1000000, backupCount=3)) + handler=logging.handlers.RotatingFileHandler(os.path.join(logs_folder_path, f"cbpi.log"), maxBytes=1000000, backupCount=3) + logger.addHandler(handler) + handler.setFormatter(formatter) except Exception as e: logger.warning("log folder or log file could not be created or accessed. check folder and file permissions or create the logs folder somewhere you have access with a start option like '--log-folder-path=./logs'") logging.critical(e, exc_info=True) diff --git a/cbpi/controller/system_controller.py b/cbpi/controller/system_controller.py index a56f30b..139ea31 100644 --- a/cbpi/controller/system_controller.py +++ b/cbpi/controller/system_controller.py @@ -31,6 +31,7 @@ class SystemController: self.cbpi = cbpi self.service = cbpi.actor self.logger = logging.getLogger(__name__) + self.logsFolderPath = self.cbpi.config_folder.logsFolderPath self.cbpi.app.on_startup.append(self.check_for_update) @@ -111,6 +112,15 @@ class SystemController: for entry in j: result.append(entry['MESSAGE']) + else: + try: + logfilename=pathlib.Path(self.logsFolderPath+"/"+"cbpi.log") + with open(logfilename) as f: + for line in f: + result.append(line.rstrip('\n')) + except: + pass + try: with open(fullname, 'w') as f: for line in result: From 59be57fd75961ca8556cd9dd8f193160441d26ac Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Tue, 4 Jun 2024 21:27:59 +0200 Subject: [PATCH 30/31] changes for UI 0.3.14.a8 (CustomSVG properties will be changed automatically) --- cbpi/__init__.py | 2 +- cbpi/extension/ConfigUpdate/__init__.py | 33 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 61bc3b4..1d83bc6 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a16" +__version__ = "4.4.1.a17" __codename__ = "Yeast Starter" diff --git a/cbpi/extension/ConfigUpdate/__init__.py b/cbpi/extension/ConfigUpdate/__init__.py index cecb22f..19c3434 100644 --- a/cbpi/extension/ConfigUpdate/__init__.py +++ b/cbpi/extension/ConfigUpdate/__init__.py @@ -8,6 +8,7 @@ import json from cbpi.api import * from cbpi.api.config import ConfigType from cbpi.api.base import CBPiBase +import glob from cbpi import __version__ logger = logging.getLogger(__name__) @@ -502,7 +503,37 @@ class ConfigUpdate(CBPiExtension): else: if CONFIG_STATUS is None or CONFIG_STATUS != self.version: await self.cbpi.config.add("current_grid", current_grid, type=ConfigType.NUMBER, description="Dashboard Grid Width",source="hidden") - + + # Check if CustomSVG props are correct for latest functionality (Widget dependent on acxtor state -> UI >= 0.3.14.a8) + try: + dashboard_files=glob.glob(self.cbpi.config_folder.get_dashboard_path('cbpi_dashboard*.json')) + write=False + + for dashboard_file in dashboard_files: + with open(dashboard_file, 'r') as file: + data = json.load(file) + + for id in data['elements']: + if id['type'] == 'CustomSVG': + try: + testoff = (id['props']['WidgetOff']) + except: + id['props']['WidgetOff'] = id['props']['name'] + write=True + try: + teston = (id['props']['WidgetOn']) + except: + try: + id['props']['WidgetOn'] = id['props']['nameon'] + write=True + except: + pass + + if write: + with open(dashboard_file, 'w') as outfile: + json.dump(data, outfile, indent=4, sort_keys=True) + except Exception as e: + logging.error(e) ## Check if influxdbname is in config From 049f50608938a9c634b1a0a9662efb15772f6659 Mon Sep 17 00:00:00 2001 From: avollkopf <43980694+avollkopf@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:37:27 +0200 Subject: [PATCH 31/31] change version to release candidate --- cbpi/__init__.py | 2 +- cbpi/extension/ConfigUpdate/__init__.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 1d83bc6..f095c11 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.1.a17" +__version__ = "4.4.1.rc0" __codename__ = "Yeast Starter" diff --git a/cbpi/extension/ConfigUpdate/__init__.py b/cbpi/extension/ConfigUpdate/__init__.py index 19c3434..eba146a 100644 --- a/cbpi/extension/ConfigUpdate/__init__.py +++ b/cbpi/extension/ConfigUpdate/__init__.py @@ -505,6 +505,13 @@ class ConfigUpdate(CBPiExtension): await self.cbpi.config.add("current_grid", current_grid, type=ConfigType.NUMBER, description="Dashboard Grid Width",source="hidden") # Check if CustomSVG props are correct for latest functionality (Widget dependent on acxtor state -> UI >= 0.3.14.a8) + plugin_list = await self.cbpi.plugin.load_plugin_list("cbpi4gui") + try: + version= plugin_list[0].get("Version", "not detected") + except: + version="not detected" + + logging.info(f'GUI Version: {version}') try: dashboard_files=glob.glob(self.cbpi.config_folder.get_dashboard_path('cbpi_dashboard*.json')) write=False