Merge pull request #129 from PiBrewing/development

Merge from Development
This commit is contained in:
Alexander Vollkopf 2024-02-23 15:36:41 +01:00 committed by GitHub
commit 1f645a835c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 134 additions and 31 deletions

View file

@ -1,3 +1,3 @@
__version__ = "4.3.1"
__version__ = "4.3.2"
__codename__ = "Winter Storm"

View file

@ -107,16 +107,24 @@ class ConfigFolder:
for checking in required_config_content:
if self.inform_missing_content(self.check_for_file_or_folder(os.path.join(self.configFolderPath, checking[0]), checking[1])):
# since there is no complete config we now check if the config folder may be completely empty to show hints:
if len(os.listdir(os.path.join(self.configFolderPath))) == 0 :
print("***************************************************")
print(f"the config folder '{self.configFolderPath}' seems to be completely empty")
print("you might want to run 'cbpi setup'.print")
print("but you could also place your zipped config backup named")
print("'restored_config.zip' inside the mentioned config folder for")
print("cbpi4 to automatically unpack it")
print("of course you can also place your config files manually")
print("***************************************************")
return False
try:
if len(os.listdir(os.path.join(self.configFolderPath))) == 0 :
print("***************************************************")
print(f"the config folder '{self.configFolderPath}' seems to be completely empty")
print("you might want to run 'cbpi setup'.print")
print("but you could also place your zipped config backup named")
print("'restored_config.zip' inside the mentioned config folder for")
print("cbpi4 to automatically unpack it")
print("of course you can also place your config files manually")
print("***************************************************")
return False
except:
print("***************************************************")
print("Cannot find config folder!")
print("Please navigate to path where you did run 'cbpi setup'.")
print("Or run 'cbpi setup' before starting the server.")
print("***************************************************")
return False
# if cbpi_dashboard_1.json does'nt exist at the new location (configFolderPath/dashboard)
# we move every cbpi_dashboard_n.json file from the old location (configFolderPath) there.
@ -145,8 +153,11 @@ class ConfigFolder:
# Starting with cbpi 4.2.0, the craftbeerpi.service file will be created dynamically from the template file based on the user id.
# Therefore, the service file is replaced with a template file in the config folder
if whatsmissing.find("craftbeerpi.template"):
self.copyDefaultFileIfNotExists("craftbeerpi.template")
return False
try:
self.copyDefaultFileIfNotExists("craftbeerpi.template")
return False
except:
pass
print("***************************************************")
print(f"CraftBeerPi config content not found: {whatsmissing}")
print("Please run 'cbpi setup' before starting the server ")

View file

@ -525,13 +525,19 @@ class FermentationController:
except Exception as e:
self.logger.error(e)
def remove_key(self,d, key):
r = dict(d)
del r[key]
return r
def push_update(self, key="fermenterupdate"):
if key == self.update_key:
self.cbpi.ws.send(dict(topic=key, data=list(map(lambda item: item.to_dict(), self.data))))
for item in self.data:
self.cbpi.push_update("cbpi/{}/{}".format(self.update_key,item.id), item.to_dict())
fermenters=self.remove_key(item.to_dict(),"steps")
self.cbpi.push_update("cbpi/{}/{}".format(self.update_key,item.id), fermenters)
pass
else:
fermentersteps=self.get_fermenter_steps()
@ -539,9 +545,20 @@ class FermentationController:
# send mqtt update for active femrentersteps
for fermenter in fermentersteps:
active = False
for step in fermenter['steps']:
if step['status'] == 'A':
self.cbpi.push_update("cbpi/{}/{}/{}".format(key,fermenter['id'],step['id']), step)
active=True
active_step=step
# self.cbpi.push_update("cbpi/{}/{}/{}".format(key,fermenter['id'],step['id']), step)
#else:
# self.cbpi.push_update("cbpi/{}/{}".format(key,fermenter['id']), "")
if active:
self.cbpi.push_update("cbpi/{}/{}".format(key,fermenter['id']), active_step)
else:
self.cbpi.push_update("cbpi/{}/{}".format(key,fermenter['id']), "")
async def call_action(self, id, action, parameter) -> None:
logging.info("FermenterStep Controller - call Action {} {}".format(id, action))

View file

@ -35,6 +35,11 @@ class SatelliteController:
]
self.tasks = set()
def remove_key(self,d, key):
r = dict(d)
del r[key]
return r
async def init(self):
#not sure if required like done in the old routine
@ -132,7 +137,8 @@ class SatelliteController:
try:
self.fermenter=self.fermentercontroller.get_state()
for item in self.fermenter['data']:
self.cbpi.push_update("cbpi/{}/{}".format("fermenterupdate",item['id']), item)
item_new=self.remove_key(item,"steps")
self.cbpi.push_update("cbpi/{}/{}".format("fermenterupdate",item['id']), item_new)
except Exception as e:
self.logger.warning("Failed to send fermenterupdate via mqtt: {}".format(e))

View file

@ -9,7 +9,7 @@ from os import listdir
import os
from os.path import isfile, join
import shortuuid
from cbpi.api.dataclasses import NotificationAction, Props, Step
from cbpi.api.dataclasses import NotificationAction, NotificationType, Props, Step
from tabulate import tabulate
from ..api.step import StepMove, StepResult, StepState
@ -156,14 +156,14 @@ class StepController:
logging.info("BREWING COMPLETE")
async def previous(self):
logging.info("Trigger Next")
logging.info("Trigger Previous")
async def next(self):
logging.info("Trigger Next")
print("\n\n\n\n")
print(self.profile)
print("\n\n\n\n")
#print("\n\n\n\n")
#print(self.profile)
#print("\n\n\n\n")
step = self.find_by_status(StepState.ACTIVE)
if step is not None:
if step.instance is not None:
@ -299,6 +299,7 @@ class StepController:
await step.instance.start()
step.status = StepState.ACTIVE
except Exception as e:
self.cbpi.notify("Error", "Can't start step. Please check step in Mash Profile", NotificationType.ERROR)
logging.error("Failed to start step %s" % step)
async def save_basic(self, data):

View file

@ -1,6 +1,7 @@
import logging
import os
import shutil
import pkgutil
import psutil
import pathlib
import json
@ -12,6 +13,8 @@ from cbpi.api.config import ConfigType
from cbpi.api import *
import zipfile
import socket
import importlib
from tabulate import tabulate
class SystemController:
@ -41,6 +44,26 @@ class SystemController:
dir_name = pathlib.Path(self.cbpi.config_folder.get_file_path(''))
shutil.make_archive(output_filename, 'zip', dir_name)
async def plugins_list(self):
result = []
discovered_plugins = {
name: importlib.import_module(name)
for finder, name, ispkg
in pkgutil.iter_modules()
if name.startswith('cbpi') and len(name) > 4
}
for key, module in discovered_plugins.items():
from importlib.metadata import version
try:
from importlib.metadata import (distribution, metadata,
version)
meta = metadata(key)
result.append(dict(Name=meta["Name"], Version=meta["Version"], Author=meta["Author"], Homepage=meta["Home-page"], Summary=meta["Summary"]))
except Exception as e:
print(e)
return tabulate(result, headers="keys")
async def downloadlog(self, logtime):
filename = "cbpi4.log"
fullname = pathlib.Path(os.path.join(".",filename))
@ -60,7 +83,12 @@ class SystemController:
else:
os.system('journalctl --since \"{} hours ago\" -u craftbeerpi.service --output cat > {}'.format(logtime, fullname))
os.system('cbpi plugins > {}'.format(fullpluginname))
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()

View file

@ -1,6 +1,7 @@
import asyncio
import sys
import socket
try:
from asyncio import set_event_loop_policy, WindowsSelectorEventLoopPolicy
except ImportError:
@ -277,6 +278,22 @@ class CraftBeerPi:
self.app.add_routes([web.get('/', http_index),
web.static('/static', os.path.join(os.path.dirname(__file__), "static"), show_index=True)])
def testport(self, port=8000):
HOST = "localhost"
# Creates a new socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Try to connect to the given host and port
if sock.connect_ex((HOST, port)) == 0:
#print("Port " + str(port) + " is open") # Connected successfully
isrunning = True
else:
#print("Port " + str(port) + " is closed") # Failed to connect because port is in use (or bad host)
isrunning = False
# Close the connection
sock.close()
return isrunning
async def init_serivces(self):
self._print_logo()
@ -304,4 +321,9 @@ class CraftBeerPi:
return self.app
def start(self):
web.run_app(self.init_serivces(), port=self.static_config.get("port", 2202))
port=self.static_config.get("port",8000)
if not self.testport(port):
web.run_app(self.init_serivces(), port=port)
else:
logging.error("Port {} is already in use! Please check, if server is already running (e.g. in automode)".format(port))
exit(1)

View file

@ -46,6 +46,8 @@ class FermenterAutostart(CBPiExtension):
Property.Number(label="HeaterOffsetOff", configurable=True, description="Offset as decimal number when the heater is switched off. Should be smaller then 'HeaterOffsetOn'. For example a value of 1 switches off the heater if the current temperature is 1 degree below the target temperature"),
Property.Number(label="CoolerOffsetOn", configurable=True, description="Offset as decimal number when the cooler is switched on. Should be greater then 'CoolerOffsetOff'. For example a value of 2 switches on the cooler if the current temperature is 2 degrees above the target temperature"),
Property.Number(label="CoolerOffsetOff", configurable=True, description="Offset as decimal number when the cooler is switched off. Should be smaller then 'CoolerOffsetOn'. For example a value of 1 switches off the cooler if the current temperature is 1 degree above the target temperature"),
Property.Number(label="HeaterMaxPower", configurable=True,description="Max Power [%] for Heater (default: 100)"),
Property.Number(label="CoolerMaxPower", configurable=True ,description="Max Power [%] for Cooler (default: 100)"),
Property.Select(label="AutoStart", options=["Yes","No"],description="Autostart Fermenter on cbpi start"),
Property.Sensor(label="sensor2",description="Optional Sensor for LCDisplay(e.g. iSpindle)")])
@ -57,6 +59,8 @@ class FermenterHysteresis(CBPiFermenterLogic):
self.heater_offset_max = float(self.props.get("HeaterOffsetOff", 0))
self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0))
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))
self.fermenter = self.get_fermenter(self.id)
self.heater = self.fermenter.heater
@ -81,7 +85,7 @@ class FermenterHysteresis(CBPiFermenterLogic):
if sensor_value + self.heater_offset_min <= target_temp:
if self.heater and (heater_state == False):
await self.actor_on(self.heater)
await self.actor_on(self.heater, self.heater_max_power)
if sensor_value + self.heater_offset_max >= target_temp:
if self.heater and (heater_state == True):
@ -89,7 +93,7 @@ class FermenterHysteresis(CBPiFermenterLogic):
if sensor_value >= self.cooler_offset_min + target_temp:
if self.cooler and (cooler_state == False):
await self.actor_on(self.cooler)
await self.actor_on(self.cooler, self.cooler_max_power)
if sensor_value <= self.cooler_offset_max + target_temp:
if self.cooler and (cooler_state == True):

View file

@ -37,6 +37,11 @@ class MQTTUtil(CBPiExtension):
self.push_update()
await asyncio.sleep(self.mqttupdate)
def remove_key(self,d, key):
r = dict(d)
del r[key]
return r
def push_update(self):
# try:
# self.actor=self.actorcontroller.get_state()
@ -62,7 +67,8 @@ class MQTTUtil(CBPiExtension):
try:
self.fermenter=self.fermentationcontroller.get_state()
for item in self.fermenter['data']:
self.cbpi.push_update("cbpi/{}/{}".format("fermenterupdate",item['id']), item)
item_new=self.remove_key(item,"steps")
self.cbpi.push_update("cbpi/{}/{}".format("fermenterupdate",item['id']), item_new)
except Exception as e:
logging.error(e)
pass

View file

@ -1,5 +1,5 @@
typing-extensions>=4
aiohttp==3.9.1
aiohttp==3.9.3
aiohttp-auth==0.1.1
aiohttp-route-decorator==0.1.4
aiohttp-security==0.5.0

View file

@ -39,7 +39,7 @@ setup(name='cbpi4',
long_description_content_type='text/markdown',
install_requires=[
"typing-extensions>=4",
"aiohttp==3.9.1",
"aiohttp==3.9.3",
"aiohttp-auth==0.1.1",
"aiohttp-route-decorator==0.1.4",
"aiohttp-security==0.5.0",

View file

@ -80,7 +80,7 @@
"options": null,
"source": "hidden",
"type": "string",
"value": "4.2.0.a6"
"value": "4.3.2.a6"
},
"CSVLOGFILES": {
"description": "Write sensor data to csv logfiles (enabling requires restart)",
@ -326,6 +326,14 @@
"type": "number",
"value": 1
},
"current_grid": {
"description": "Dashboard Grid Width",
"name": "current_grid",
"options": null,
"source": "hidden",
"type": "number",
"value": 5
},
"max_dashboard_number": {
"description": "Max Number of Dashboards",
"name": "max_dashboard_number",