Merge pull request #120 from PiBrewing/development

merge from Development to allow for installation with pipx under bookworm
This commit is contained in:
Alexander Vollkopf 2023-11-19 15:05:21 +01:00 committed by GitHub
commit 42d8a6c65d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 190 additions and 84 deletions

View file

@ -1,3 +1,3 @@
__version__ = "4.1.11" __version__ = "4.2.0"
__codename__ = "Groundhog Day" __codename__ = "Indian Summer"

View file

@ -1,6 +1,8 @@
import logging import logging
import sys
from pathlib import Path from pathlib import Path
import requests import requests
from cbpi import __version__, __codename__
from cbpi.configFolder import ConfigFolder from cbpi.configFolder import ConfigFolder
from cbpi.utils.utils import load_config from cbpi.utils.utils import load_config
from zipfile import ZipFile from zipfile import ZipFile
@ -38,14 +40,27 @@ class CraftBeerPiCli():
def setup_one_wire(self): def setup_one_wire(self):
print("Setting up 1Wire") print("Setting up 1Wire")
with open('/boot/config.txt', 'w') as f: with open('/boot/config.txt', 'r') as f:
f.write("dtoverlay=w1-gpio,gpiopin=4,pullup=on") lines=f.readlines()
lines.append("dtoverlay=w1-gpio,gpiopin=4,pullup=on")
configtempfile=os.path.join(self.config.get_file_path(""),"config.txt")
with open(configtempfile, 'w') as f:
for line in lines:
f.write(line)
destfile="/boot/config.txt"
#copy and remove afterwards as mv will work, but raise an error message due to different file owners
shutil.os.system('sudo cp "{}" "{}"'.format(configtempfile,destfile))
shutil.os.system('rm -rf "{}"'.format(configtempfile))
print("/boot/config.txt created") print("/boot/config.txt created")
def list_one_wire(self): def list_one_wire(self):
print("List 1Wire") print("List 1Wire")
call(["modprobe", "w1-gpio"]) call(["sudo","modprobe", "w1-gpio"])
call(["modprobe", "w1-therm"]) call(["sudo","modprobe", "w1-therm"])
try: try:
for dirname in os.listdir('/sys/bus/w1/devices'): for dirname in os.listdir('/sys/bus/w1/devices'):
if (dirname.startswith("28") or dirname.startswith("10")): if (dirname.startswith("28") or dirname.startswith("10")):
@ -150,16 +165,34 @@ class CraftBeerPiCli():
else: else:
print("CraftBeerPi Autostart is {}OFF{}".format(Fore.RED,Style.RESET_ALL)) print("CraftBeerPi Autostart is {}OFF{}".format(Fore.RED,Style.RESET_ALL))
elif(name == "on"): elif(name == "on"):
user=os.getlogin()
path="/usr/local/bin/cbpi"
if os.path.exists("/home/"+user+"/.local/bin/cbpi") is True:
path="/home/"+user+"/.local/bin/cbpi"
print("Add craftbeerpi.service to systemd") print("Add craftbeerpi.service to systemd")
try: try:
if os.path.exists(os.path.join("/etc/systemd/system","craftbeerpi.service")) is False: if os.path.exists(os.path.join("/etc/systemd/system","craftbeerpi.service")) is False:
templatefile=self.config.get_file_path("craftbeerpi.template")
shutil.os.system('cp "{}" "{}"'.format(templatefile,self.config.get_file_path("craftbeerpi.service")))
srcfile = self.config.get_file_path("craftbeerpi.service") srcfile = self.config.get_file_path("craftbeerpi.service")
import jinja2
templateLoader = jinja2.FileSystemLoader(searchpath=os.path.join(self.config.get_file_path("")))
templateEnv = jinja2.Environment(loader=templateLoader)
operatingsystem = str(platform.system()).lower()
if operatingsystem.startswith("win"):
srcfile=str(srcfile).replace('\\','/')
template = templateEnv.get_template("craftbeerpi.service")
outputText = template.render(user=user, path=path)
with open(srcfile, "w") as fh:
fh.write(outputText)
destfile = os.path.join("/etc/systemd/system") destfile = os.path.join("/etc/systemd/system")
shutil.copy(srcfile, destfile) shutil.os.system('sudo mv "{}" "{}"'.format(srcfile,destfile))
print("Copied craftbeerpi.service to /etc/systemd/system") print("Copied craftbeerpi.service to /etc/systemd/system")
os.system('systemctl enable craftbeerpi.service') shutil.os.system('sudo systemctl enable craftbeerpi.service')
print('Enabled craftbeerpi service') print('Enabled craftbeerpi service')
os.system('systemctl start craftbeerpi.service') shutil.os.system('sudo systemctl start craftbeerpi.service')
print('Started craftbeerpi.service') print('Started craftbeerpi.service')
else: else:
print("craftbeerpi.service is already located in /etc/systemd/system") print("craftbeerpi.service is already located in /etc/systemd/system")
@ -167,20 +200,20 @@ class CraftBeerPiCli():
print(e) print(e)
return return
return return
elif(name == "off"): elif(name == "off"):
print("Remove craftbeerpi.service from systemd") print("Remove craftbeerpi.service from systemd")
try: try:
status = os.popen('systemctl list-units --type=service --state=running | grep craftbeerpi.service').read() status = os.popen('systemctl list-units --type=service --state=running | grep craftbeerpi.service').read()
if status.find("craftbeerpi.service") != -1: if status.find("craftbeerpi.service") != -1:
os.system('systemctl stop craftbeerpi.service') shutil.os.system('sudo systemctl stop craftbeerpi.service')
print('Stopped craftbeerpi service') print('Stopped craftbeerpi service')
os.system('systemctl disable craftbeerpi.service') shutil.os.system('sudo systemctl disable craftbeerpi.service')
print('Removed craftbeerpi.service as service') print('Removed craftbeerpi.service as service')
else: else:
print('craftbeerpi.service service is not running') print('craftbeerpi.service service is not running')
if os.path.exists(os.path.join("/etc/systemd/system","craftbeerpi.service")) is True: if os.path.exists(os.path.join("/etc/systemd/system","craftbeerpi.service")) is True:
os.remove(os.path.join("/etc/systemd/system","craftbeerpi.service")) shutil.os.system('sudo rm -rf "{}"'.format(os.path.join("/etc/systemd/system","craftbeerpi.service")))
print("Deleted craftbeerpi.service from /etc/systemd/system") print("Deleted craftbeerpi.service from /etc/systemd/system")
else: else:
print("craftbeerpi.service is not located in /etc/systemd/system") print("craftbeerpi.service is not located in /etc/systemd/system")
@ -188,8 +221,6 @@ class CraftBeerPiCli():
print(e) print(e)
return return
return return
def chromium(self, name): def chromium(self, name):
'''Enable or disable autostart''' '''Enable or disable autostart'''
if(name == "status"): if(name == "status"):
@ -203,7 +234,7 @@ class CraftBeerPiCli():
if os.path.exists(os.path.join("/etc/xdg/autostart/","chromium.desktop")) is False: if os.path.exists(os.path.join("/etc/xdg/autostart/","chromium.desktop")) is False:
srcfile = self.config.get_file_path("chromium.desktop") srcfile = self.config.get_file_path("chromium.desktop")
destfile = os.path.join("/etc/xdg/autostart/") destfile = os.path.join("/etc/xdg/autostart/")
shutil.copy(srcfile, destfile) shutil.os.system('sudo cp "{}" "{}"'.format(srcfile,destfile))
print("Copied chromium.desktop to /etc/xdg/autostart/") print("Copied chromium.desktop to /etc/xdg/autostart/")
else: else:
print("chromium.desktop is already located in /etc/xdg/autostart/") print("chromium.desktop is already located in /etc/xdg/autostart/")
@ -215,7 +246,7 @@ class CraftBeerPiCli():
print("Remove chromium.desktop from /etc/xdg/autostart/") print("Remove chromium.desktop from /etc/xdg/autostart/")
try: try:
if os.path.exists(os.path.join("/etc/xdg/autostart/","chromium.desktop")) is True: if os.path.exists(os.path.join("/etc/xdg/autostart/","chromium.desktop")) is True:
os.remove(os.path.join("/etc/xdg/autostart/","chromium.desktop")) shutil.os.system('sudo rm -rf "{}"'.format(os.path.join("/etc/xdg/autostart/","chromium.desktop")))
print("Deleted chromium.desktop from /etc/xdg/autostart/") print("Deleted chromium.desktop from /etc/xdg/autostart/")
else: else:
print("chromium.desktop is not located in /etc/xdg/autostart/") print("chromium.desktop is not located in /etc/xdg/autostart/")
@ -229,16 +260,27 @@ class CraftBeerPiCli():
@click.pass_context @click.pass_context
@click.option('--config-folder-path', '-c', default="./config", type=click.Path(), help="Specify where the config folder is located. Defaults to './config'.") @click.option('--config-folder-path', '-c', default="./config", type=click.Path(), help="Specify where the config folder is located. Defaults to './config'.")
@click.option('--logs-folder-path', '-l', default="", type=click.Path(), help="Specify where the log folder is located. Defaults to '../logs' relative from the config folder.") @click.option('--logs-folder-path', '-l', default="", type=click.Path(), help="Specify where the log folder is located. Defaults to '../logs' relative from the config folder.")
@click.option('--debug-log-level', '-d', default="30", type=int, help="Specify the log level you want to write to all logs. 0=ALL, 10=DEBUG, 20=INFO 30(default)=WARNING, 40=ERROR, 50=CRITICAL") @click.option('--debug-log-level', '-d', default="99", type=int, help="Specify the log level you want to write to all logs. 0=ALL, 10=DEBUG, 20=INFO 30(default)=WARNING, 40=ERROR, 50=CRITICAL. Can be also set in config.yaml (debug-log-level: INT)")
def main(context, config_folder_path, logs_folder_path, debug_log_level): def main(context, config_folder_path, logs_folder_path, debug_log_level):
print("---------------------") print("--------------------------")
print("Welcome to CBPi") print("Welcome to CBPi "+__version__)
print("---------------------") print("--------------------------")
if logs_folder_path == "": if logs_folder_path == "":
logs_folder_path = os.path.join(Path(config_folder_path).absolute().parent, 'logs') logs_folder_path = os.path.join(Path(config_folder_path).absolute().parent, 'logs')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
config=ConfigFolder(config_folder_path, logs_folder_path)
static_config = load_config(config.get_file_path("config.yaml"))
try:
if debug_log_level == 99:
debug_log_level=static_config['debug-log-level']
except:
debug_log_level=30
logging.basicConfig(format=formatter, stream=logging.StreamHandler()) logging.basicConfig(format=formatter, stream=logging.StreamHandler())
logger = logging.getLogger() logger = logging.getLogger()
print("*******************************")
print("Debug-log-level is {}".format(debug_log_level))
print("*******************************")
logger.setLevel(debug_log_level) logger.setLevel(debug_log_level)
try: try:
if not os.path.isdir(logs_folder_path): if not os.path.isdir(logs_folder_path):
@ -249,7 +291,7 @@ def main(context, config_folder_path, logs_folder_path, debug_log_level):
except Exception as e: 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'") 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) logging.critical(e, exc_info=True)
cbpi_cli = CraftBeerPiCli(ConfigFolder(config_folder_path, logs_folder_path)) cbpi_cli = CraftBeerPiCli(config)
context.obj = cbpi_cli context.obj = cbpi_cli
@main.command() @main.command()
@ -263,11 +305,15 @@ def setup(context):
@click.option('--list', is_flag=True, help="List all 1Wire Devices") @click.option('--list', is_flag=True, help="List all 1Wire Devices")
@click.option('--setup', is_flag=True, help="Setup 1Wire on Raspberry Pi") @click.option('--setup', is_flag=True, help="Setup 1Wire on Raspberry Pi")
def onewire(context, list, setup): def onewire(context, list, setup):
'''Setup 1wire on Raspberry Pi''' '''(--setup | --list) Setup 1wire on Raspberry Pi or list sensors'''
if setup is True: operationsystem= sys.platform
context.obj.setup_one_wire() if not operationsystem.startswith('win'):
if list is True: if setup is True:
context.obj.list_one_wire() context.obj.setup_one_wire()
if list is True:
context.obj.list_one_wire()
else:
print("Onewire options NOT available under Windows")
@main.command() @main.command()
@click.pass_context @click.pass_context
@ -297,7 +343,11 @@ def create(context, pluginname=[]):
@click.argument('name') @click.argument('name')
def autostart(context, name): def autostart(context, name):
'''(on|off|status) Enable or disable autostart''' '''(on|off|status) Enable or disable autostart'''
context.obj.autostart(name) operationsystem= sys.platform
if not operationsystem.startswith('win'):
context.obj.autostart(name)
else:
print("Autostart option NOT available under Windows")
@main.command() @main.command()
@ -305,4 +355,8 @@ def autostart(context, name):
@click.argument('name') @click.argument('name')
def chromium(context, name): def chromium(context, name):
'''(on|off|status) Enable or disable Kiosk mode''' '''(on|off|status) Enable or disable Kiosk mode'''
context.obj.chromium(name) operationsystem= sys.platform
if not operationsystem.startswith('win'):
context.obj.chromium(name)
else:
print("Chromium option NOT available under Windows")

View file

@ -5,7 +5,7 @@ version: 4.0.8
index_url: /cbpi_ui/static/index.html index_url: /cbpi_ui/static/index.html
port: 8000 port: 8000
debug-log-level: 30
mqtt: false mqtt: false
mqtt_host: localhost mqtt_host: localhost
mqtt_port: 1883 mqtt_port: 1883

View file

@ -2,8 +2,8 @@
Description=Craftbeer Pi Description=Craftbeer Pi
[Service] [Service]
WorkingDirectory=/home/pi WorkingDirectory=/home/{{ user }}
ExecStart=/usr/local/bin/cbpi start ExecStart={{ path }} start
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -94,7 +94,7 @@ class ConfigFolder:
#['fermenter_data.json', 'file'], created by fermentation_controller @ start if not available #['fermenter_data.json', 'file'], created by fermentation_controller @ start if not available
#['step_data.json', 'file'], created by step_controller @ start if not available #['step_data.json', 'file'], created by step_controller @ start if not available
['config.json', 'file'], ['config.json', 'file'],
['craftbeerpi.service', 'file'], ['craftbeerpi.template', 'file'],
['chromium.desktop', 'file'], ['chromium.desktop', 'file'],
['dashboard', 'folder'], ['dashboard', 'folder'],
['dashboard/widgets', 'folder'], ['dashboard/widgets', 'folder'],
@ -106,7 +106,7 @@ class ConfigFolder:
] ]
for checking in required_config_content: 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])): 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 folde rmay be completely empty to show hints: # 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 : if len(os.listdir(os.path.join(self.configFolderPath))) == 0 :
print("***************************************************") print("***************************************************")
print(f"the config folder '{self.configFolderPath}' seems to be completely empty") print(f"the config folder '{self.configFolderPath}' seems to be completely empty")
@ -118,7 +118,7 @@ class ConfigFolder:
print("***************************************************") print("***************************************************")
return False return False
# if cbpi_dashboard_1.json doesnt exist at the new location (configFolderPath/dashboard) # 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. # we move every cbpi_dashboard_n.json file from the old location (configFolderPath) there.
# this could be a config zip file restore from version 4.0.7.a4 or prior. # this could be a config zip file restore from version 4.0.7.a4 or prior.
dashboard_1_path = os.path.join(self.configFolderPath, 'dashboard', 'cbpi_dashboard_1.json') dashboard_1_path = os.path.join(self.configFolderPath, 'dashboard', 'cbpi_dashboard_1.json')
@ -132,7 +132,7 @@ class ConfigFolder:
try: try:
with open(dashboard_1_path, 'r') as f: with open(dashboard_1_path, 'r') as f:
data = json.load(f) data = json.load(f)
if (len(data['elements']) == 0): # there may exist some pathes but pathes without elements in dashboard is not very likely if (len(data['elements']) == 0): # there may exist some paths but paths without elements in dashboard is not very likely
return True return True
else: else:
return False return False
@ -142,6 +142,11 @@ class ConfigFolder:
def inform_missing_content(self, whatsmissing : str): def inform_missing_content(self, whatsmissing : str):
if whatsmissing == "": if whatsmissing == "":
return False return False
# 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
print("***************************************************") print("***************************************************")
print(f"CraftBeerPi config content not found: {whatsmissing}") print(f"CraftBeerPi config content not found: {whatsmissing}")
print("Please run 'cbpi setup' before starting the server ") print("Please run 'cbpi setup' before starting the server ")
@ -181,7 +186,7 @@ class ConfigFolder:
self.copyDefaultFileIfNotExists("fermenter_data.json") self.copyDefaultFileIfNotExists("fermenter_data.json")
self.copyDefaultFileIfNotExists("step_data.json") self.copyDefaultFileIfNotExists("step_data.json")
self.copyDefaultFileIfNotExists("config.json") self.copyDefaultFileIfNotExists("config.json")
self.copyDefaultFileIfNotExists("craftbeerpi.service") self.copyDefaultFileIfNotExists("craftbeerpi.template")
self.copyDefaultFileIfNotExists("chromium.desktop") self.copyDefaultFileIfNotExists("chromium.desktop")
print("Config Folder created") print("Config Folder created")
@ -206,5 +211,5 @@ class ConfigFolder:
shutil.chown(os.path.join(dirpath, filename), owner, group) shutil.chown(os.path.join(dirpath, filename), owner, group)
except: except:
print("problems assigning file or folder permissions") print("problems assigning file or folder permissions")
print("if this happend on windows its fine") print("if this happened on windows its fine")
print("if this happend in the dev container running inside windows its also fine but you might have to rebuild the container if you run into further problems") print("if this happened in the dev container running inside windows its also fine but you might have to rebuild the container if you run into further problems")

View file

@ -60,11 +60,19 @@ class DashboardController:
async def get_current_dashboard(self): async def get_current_dashboard(self):
current_dashboard_number = self.cbpi.config.get("current_dashboard_number", 1) current_dashboard_number = self.cbpi.config.get("current_dashboard_number", 1)
return current_dashboard_number return current_dashboard_number
async def set_current_dashboard(self, dashboard_id=1): async def set_current_dashboard(self, dashboard_id=1):
await self.cbpi.config.set("current_dashboard_number", dashboard_id) await self.cbpi.config.set("current_dashboard_number", dashboard_id)
return {"status": "OK"} return {"status": "OK"}
async def get_current_grid(self):
current_grid = self.cbpi.config.get("current_grid", 5)
return current_grid
async def set_current_grid(self, grid_width=5):
await self.cbpi.config.set("current_grid", grid_width)
return {"status": "OK"}
async def get_slow_pipe_animation(self): async def get_slow_pipe_animation(self):
slow_pipe_animation = self.cbpi.config.get("slow_pipe_animation", "Yes") slow_pipe_animation = self.cbpi.config.get("slow_pipe_animation", "Yes")
return slow_pipe_animation return slow_pipe_animation

View file

@ -219,12 +219,12 @@ class SystemController:
for nic, addrs in ethernet.items(): for nic, addrs in ethernet.items():
if nic == "eth0": if nic == "eth0":
for addr in addrs: for addr in addrs:
if str(addr.family) == "AddressFamily.AF_INET": if str(addr.family) == "AddressFamily.AF_INET" or str(addr.family) == "2":
if addr.address: if addr.address:
eth0IP = addr.address eth0IP = addr.address
if nic == "wlan0": if nic == "wlan0":
for addr in addrs: for addr in addrs:
if str(addr.family) == "AddressFamily.AF_INET": if str(addr.family) == "AddressFamily.AF_INET" or str(addr.family) == "2":
if addr.address: if addr.address:
wlan0IP = addr.address wlan0IP = addr.address
info = psutil.net_if_stats() info = psutil.net_if_stats()

View file

@ -170,7 +170,7 @@ class UploadController:
# load beerxml file located in upload folder # load beerxml file located in upload folder
self.path = self.cbpi.config_folder.get_upload_file("kbh.db") self.path = self.cbpi.config_folder.get_upload_file("kbh.db")
if os.path.exists(self.path) is False: if os.path.exists(self.path) is False:
self.cbpi.notify("File Not Found", "Please upload a kbh V2 databsel file", NotificationType.ERROR) self.cbpi.notify("File Not Found", "Please upload a kbh V2 database file", NotificationType.ERROR)
try: try:
# Get Recipe Nmae # Get Recipe Nmae
@ -179,19 +179,19 @@ class UploadController:
c.execute('SELECT Sudname FROM Sud WHERE ID = ?', (Recipe_ID,)) c.execute('SELECT Sudname FROM Sud WHERE ID = ?', (Recipe_ID,))
row = c.fetchone() row = c.fetchone()
name = row[0] name = row[0]
# get MashIn Temp # get MashIn Temp
mashin_temp = None mashin_temp = None
c.execute('SELECT Temp FROM Rasten WHERE Typ = 0 AND SudID = ?', (Recipe_ID,)) c.execute('SELECT TempWasser FROM Maischplan WHERE Typ = 0 AND SudID = ?', (Recipe_ID,))
row = c.fetchone() row = c.fetchone()
try: try:
if self.cbpi.config.get("TEMP_UNIT", "C") == "C": if self.cbpi.config.get("TEMP_UNIT", "C") == "C":
mashin_temp = str(int(row[0])) mashin_temp = str(float(row[0]))
else: else:
mashin_temp = str(round(9.0 / 5.0 * int(row[0]) + 32)) mashin_temp = str(round(9.0 / 5.0 * float(row[0]) + 32))
except: except:
pass pass
logging.error(mashin_temp)
# get the hop addition times # get the hop addition times
c.execute('SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze <> 1 AND SudID = ?', (Recipe_ID,)) c.execute('SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze <> 1 AND SudID = ?', (Recipe_ID,))
hops = c.fetchall() hops = c.fetchall()
@ -201,11 +201,9 @@ class UploadController:
whirlpool.append(hop) whirlpool.append(hop)
for whirl in whirlpool: for whirl in whirlpool:
hops.remove(whirl) hops.remove(whirl)
# get the misc addition times # get the misc addition times
c.execute('SELECT Zugabedauer, Name FROM WeitereZutatenGaben WHERE Zeitpunkt = 1 AND SudID = ?', (Recipe_ID,)) c.execute('SELECT Zugabedauer, Name FROM WeitereZutatenGaben WHERE Zeitpunkt = 1 AND SudID = ?', (Recipe_ID,))
miscs = c.fetchall() miscs = c.fetchall()
try: try:
c.execute('SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze = 1 AND SudID = ?', (Recipe_ID,)) c.execute('SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze = 1 AND SudID = ?', (Recipe_ID,))
FW_Hops = c.fetchall() FW_Hops = c.fetchall()
@ -216,9 +214,7 @@ class UploadController:
c.execute('SELECT Kochdauer FROM Sud WHERE ID = ?', (Recipe_ID,)) c.execute('SELECT Kochdauer FROM Sud WHERE ID = ?', (Recipe_ID,))
row = c.fetchone() row = c.fetchone()
BoilTime = str(int(row[0])) BoilTime = str(int(row[0]))
await self.create_recipe(name) await self.create_recipe(name)
if mashin_temp is not None: if mashin_temp is not None:
@ -238,7 +234,7 @@ class UploadController:
} }
await self.create_step(step_string) await self.create_step(step_string)
for row in c.execute('SELECT Name, Temp, Dauer FROM Rasten WHERE Typ <> 0 AND SudID = ?', (Recipe_ID,)): for row in c.execute('SELECT Name, TempRast, DauerRast FROM Maischplan WHERE Typ <> 0 AND SudID = ?', (Recipe_ID,)):
if mashin_temp is None and self.addmashin == "Yes": if mashin_temp is None and self.addmashin == "Yes":
step_type = self.mashin if self.mashin != "" else "MashInStep" step_type = self.mashin if self.mashin != "" else "MashInStep"
step_string = { "name": "MashIn", step_string = { "name": "MashIn",
@ -246,7 +242,7 @@ class UploadController:
"AutoMode": self.AutoMode, "AutoMode": self.AutoMode,
"Kettle": self.id, "Kettle": self.id,
"Sensor": self.kettle.sensor, "Sensor": self.kettle.sensor,
"Temp": str(int(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * int(row[1]) + 32)), "Temp": str(float(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * float(row[1]) + 32)),
"Timer": "0", "Timer": "0",
"Notification": "Target temperature reached. Please add malt." "Notification": "Target temperature reached. Please add malt."
}, },
@ -263,7 +259,7 @@ class UploadController:
"AutoMode": self.AutoMode, "AutoMode": self.AutoMode,
"Kettle": self.id, "Kettle": self.id,
"Sensor": self.kettle.sensor, "Sensor": self.kettle.sensor,
"Temp": str(int(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * int(row[1]) + 32)), "Temp": str(float(row[1])) if self.TEMP_UNIT == "C" else str(round(9.0 / 5.0 * float(row[1]) + 32)),
"Timer": str(int(row[2])) "Timer": str(int(row[2]))
}, },
"status_text": "", "status_text": "",

View file

@ -260,9 +260,9 @@ class CraftBeerPi:
def _print_logo(self): def _print_logo(self):
from pyfiglet import Figlet from pyfiglet import Figlet
f = Figlet(font='big') f = Figlet(font='big')
logger.info("\n%s" % f.renderText("CraftBeerPi %s " % self.version)) logger.warning("\n%s" % f.renderText("CraftBeerPi %s " % self.version))
logger.info("www.CraftBeerPi.com") logger.warning("www.CraftBeerPi.com")
logger.info("(c) 2021/2022 Manuel Fritsch / Alexander Vollkopf") logger.warning("(c) 2021/2022/2023 Manuel Fritsch / Alexander Vollkopf")
def _setup_http_index(self): def _setup_http_index(self):
async def http_index(request): async def http_index(request):

View file

@ -64,6 +64,7 @@ class ConfigUpdate(CBPiExtension):
BoilKettle = self.cbpi.config.get("BoilKettle", None) BoilKettle = self.cbpi.config.get("BoilKettle", None)
CONFIG_STATUS = self.cbpi.config.get("CONFIG_STATUS", None) CONFIG_STATUS = self.cbpi.config.get("CONFIG_STATUS", None)
self.version=__version__ self.version=__version__
current_grid = self.cbpi.config.get("current_grid", None)
if boil_temp is None: if boil_temp is None:
@ -491,6 +492,16 @@ class ConfigUpdate(CBPiExtension):
source="steps", source="steps",
options=[{"label": "Yes", "value": "Yes"}, options=[{"label": "Yes", "value": "Yes"},
{"label": "No", "value": "No"}]) {"label": "No", "value": "No"}])
if current_grid is None:
logger.info("INIT Current Dashboard Number")
try:
await self.cbpi.config.add("current_grid", 5, type=ConfigType.NUMBER, description="Dashboard Grid Width",source="hidden")
except:
logger.warning('Unable to update database')
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")
@ -503,6 +514,8 @@ class ConfigUpdate(CBPiExtension):
logger.warning('Unable to update config') logger.warning('Unable to update config')
def setup(cbpi): def setup(cbpi):
cbpi.plugin.register("ConfigUpdate", ConfigUpdate) cbpi.plugin.register("ConfigUpdate", ConfigUpdate)
pass pass

View file

@ -170,7 +170,7 @@ def setup(cbpi):
cbpi.plugin.register("OneWire", OneWire) cbpi.plugin.register("OneWire", OneWire)
try: try:
# Global Init # Global Init
call(["modprobe", "w1-gpio"]) call(["sudo","modprobe", "w1-gpio"])
call(["modprobe", "w1-therm"]) call(["sudo","modprobe", "w1-therm"])
except Exception as e: except Exception as e:
pass pass

View file

@ -156,6 +156,40 @@ class DashBoardHttpEndpoints:
""" """
dashboard_id = int(request.match_info['id']) dashboard_id = int(request.match_info['id'])
return web.json_response(await self.cbpi.dashboard.set_current_dashboard(dashboard_id)) return web.json_response(await self.cbpi.dashboard.set_current_dashboard(dashboard_id))
@request_mapping(path="/currentgrid", method="GET", auth_required=False)
async def get_current_grid(self, request):
"""
---
description: Get Dashboard Numbers
tags:
- Dashboard
responses:
"200":
description: successful operation
"""
return web.json_response(await self.cbpi.dashboard.get_current_grid(), dumps=json_dumps)
@request_mapping(path="/{width}/currentgrid", method="POST", auth_required=False)
async def set_current_grid(self, request):
"""
---
description: Set Current Grid Width
tags:
- Dashboard
parameters:
- name: "width"
in: "path"
description: "Grid Width"
required: true
type: "integer"
format: "int64"
responses:
"200":
description: successful operation
"""
grid_width = int(request.match_info['width'])
return web.json_response(await self.cbpi.dashboard.set_current_grid(grid_width))
@request_mapping(path="/slowPipeAnimation", method="GET", auth_required=False) @request_mapping(path="/slowPipeAnimation", method="GET", auth_required=False)
async def get_slow_pipe_animation(self, request): async def get_slow_pipe_animation(self, request):

View file

@ -1,5 +1,5 @@
typing-extensions>=4 typing-extensions>=4
aiohttp==3.8.5 aiohttp==3.8.6
aiohttp-auth==0.1.1 aiohttp-auth==0.1.1
aiohttp-route-decorator==0.1.4 aiohttp-route-decorator==0.1.4
aiohttp-security==0.4.0 aiohttp-security==0.4.0
@ -7,22 +7,22 @@ aiohttp-session==2.12.0
aiohttp-swagger==1.0.16 aiohttp-swagger==1.0.16
aiojobs==1.1.0 aiojobs==1.1.0
aiosqlite==0.17.0 aiosqlite==0.17.0
cryptography==41.0.2 cryptography==41.0.5
pyopenssl==23.2.0 pyopenssl==23.3.0
requests==2.31.0 requests==2.31.0
voluptuous==0.13.1 voluptuous==0.13.1
pyfiglet==0.8.post1 pyfiglet==1.0.2
pandas==1.5.3 pandas==1.5.3
shortuuid==1.0.11 shortuuid==1.0.11
tabulate==0.9.0 tabulate==0.9.0
numpy==1.24.1 numpy==1.24.1
cbpi4gui cbpi4gui
click==8.1.3 click==8.1.7
importlib_metadata==4.11.1 importlib_metadata==4.11.1
aiomqtt==1.0.0 aiomqtt==1.2.1
psutil==5.9.4 psutil==5.9.6
zipp>=0.5 zipp>=0.5
colorama==0.4.6 colorama==0.4.6
pytest-aiohttp pytest-aiohttp
coverage==6.3.1 coverage==6.3.1
inquirer==3.1.1 inquirer==3.1.3

View file

@ -39,7 +39,7 @@ setup(name='cbpi4',
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
install_requires=[ install_requires=[
"typing-extensions>=4", "typing-extensions>=4",
"aiohttp==3.8.5", "aiohttp==3.8.6",
"aiohttp-auth==0.1.1", "aiohttp-auth==0.1.1",
"aiohttp-route-decorator==0.1.4", "aiohttp-route-decorator==0.1.4",
"aiohttp-security==0.4.0", "aiohttp-security==0.4.0",
@ -47,18 +47,18 @@ setup(name='cbpi4',
"aiohttp-swagger==1.0.16", "aiohttp-swagger==1.0.16",
"aiojobs==1.1.0 ", "aiojobs==1.1.0 ",
"aiosqlite==0.17.0", "aiosqlite==0.17.0",
"cryptography==41.0.2", "cryptography==41.0.5",
"pyopenssl==23.2.0", "pyopenssl==23.3.0",
"requests==2.31.0", "requests==2.31.0",
"voluptuous==0.13.1", "voluptuous==0.13.1",
"pyfiglet==0.8.post1", "pyfiglet==1.0.2",
'click==8.1.3', 'click==8.1.7',
'shortuuid==1.0.11', 'shortuuid==1.0.11',
'tabulate==0.9.0', 'tabulate==0.9.0',
'aiomqtt==1.0.0', 'aiomqtt==1.2.1',
'inquirer==3.1.1', 'inquirer==3.1.3',
'colorama==0.4.6', 'colorama==0.4.6',
'psutil==5.9.4', 'psutil==5.9.6',
'cbpi4gui', 'cbpi4gui',
'importlib_metadata', 'importlib_metadata',
'numpy==1.24.1', 'numpy==1.24.1',

View file

@ -80,10 +80,10 @@
"options": null, "options": null,
"source": "hidden", "source": "hidden",
"type": "string", "type": "string",
"value": "4.1.10.a1" "value": "4.2.0.a6"
}, },
"CSVLOGFILES": { "CSVLOGFILES": {
"description": "Write sensor data to csv logfiles", "description": "Write sensor data to csv logfiles (enabling requires restart)",
"name": "CSVLOGFILES", "name": "CSVLOGFILES",
"options": [ "options": [
{ {
@ -100,7 +100,7 @@
"value": "Yes" "value": "Yes"
}, },
"INFLUXDB": { "INFLUXDB": {
"description": "Write sensor data to influxdb", "description": "Write sensor data to influxdb (enabling requires restart)",
"name": "INFLUXDB", "name": "INFLUXDB",
"options": [ "options": [
{ {

View file

@ -4,7 +4,6 @@ from tests.cbpi_config_fixture import CraftBeerPiTestCase
class SensorTestCase(CraftBeerPiTestCase): class SensorTestCase(CraftBeerPiTestCase):
@unittest_run_loop
async def test_crud(self): async def test_crud(self):
data = { data = {

View file

@ -4,7 +4,6 @@ from tests.cbpi_config_fixture import CraftBeerPiTestCase
class StepTestCase(CraftBeerPiTestCase): class StepTestCase(CraftBeerPiTestCase):
@unittest_run_loop
async def test_get(self): async def test_get(self):
resp = await self.client.request("GET", "/step2") resp = await self.client.request("GET", "/step2")
@ -12,7 +11,6 @@ class StepTestCase(CraftBeerPiTestCase):
assert resp.status == 200 assert resp.status == 200
@unittest_run_loop
async def test_crud(self): async def test_crud(self):
data = { data = {
"name": "Test", "name": "Test",

View file

@ -4,7 +4,6 @@ from tests.cbpi_config_fixture import CraftBeerPiTestCase
class IndexTestCase(CraftBeerPiTestCase): class IndexTestCase(CraftBeerPiTestCase):
@unittest_run_loop
async def test_endpoints(self): async def test_endpoints(self):
# Test Index Page # Test Index Page
resp = await self.client.post(path="/system/restart") resp = await self.client.post(path="/system/restart")