2021-05-26 21:00:35 +02:00
import os
import pathlib
import aiohttp
from aiohttp import web
import asyncio
from cbpi . api import *
import xml . etree . ElementTree
import sqlite3
from voluptuous . schema_builder import message
2021-12-27 18:11:40 +01:00
from cbpi . api . dataclasses import NotificationAction , NotificationType , Actor , Sensor , Kettle
2021-05-26 21:00:35 +02:00
from cbpi . controller . kettle_controller import KettleController
from cbpi . api . base import CBPiBase
from cbpi . api . config import ConfigType
import webbrowser
2021-06-06 13:00:55 +02:00
import logging
import os . path
from os import listdir
from os . path import isfile , join
import json
2024-06-23 14:53:03 +02:00
import math
2021-06-06 13:00:55 +02:00
import yaml
from . . api . step import StepMove , StepResult , StepState
import re
2021-06-13 14:38:18 +02:00
import base64
2021-06-06 13:00:55 +02:00
class UploadController :
2021-05-26 21:00:35 +02:00
def __init__ ( self , cbpi ) :
self . cbpi = cbpi
2021-06-06 13:00:55 +02:00
self . logger = logging . getLogger ( __name__ )
async def get_kbh_recipes ( self ) :
try :
2022-02-19 12:33:11 +01:00
path = self . cbpi . config_folder . get_upload_file ( " kbh.db " )
2021-06-06 13:00:55 +02:00
conn = sqlite3 . connect ( path )
c = conn . cursor ( )
c . execute ( ' SELECT ID, Sudname, Status FROM Sud ' )
data = c . fetchall ( )
result = [ ]
for row in data :
element = { ' value ' : str ( row [ 0 ] ) , ' label ' : str ( row [ 1 ] ) }
result . append ( element )
return result
except :
return [ ]
2021-05-26 21:00:35 +02:00
2021-06-06 13:00:55 +02:00
async def get_xml_recipes ( self ) :
try :
2022-02-19 12:33:11 +01:00
path = self . cbpi . config_folder . get_upload_file ( " beer.xml " )
2021-06-06 13:00:55 +02:00
e = xml . etree . ElementTree . parse ( path ) . getroot ( )
result = [ ]
counter = 1
for idx , val in enumerate ( e . findall ( ' RECIPE ' ) ) :
element = { ' value ' : str ( counter ) , ' label ' : val . find ( " NAME " ) . text }
result . append ( element )
counter + = 1
return result
except :
return [ ]
2021-05-26 21:00:35 +02:00
2022-01-20 17:44:44 +01:00
async def get_json_recipes ( self ) :
try :
2022-02-19 12:33:11 +01:00
path = self . cbpi . config_folder . get_upload_file ( " mmum.json " )
2022-01-20 17:44:44 +01:00
e = json . load ( open ( path ) )
result = [ ]
result . append ( { ' value ' : str ( 1 ) , ' label ' : e [ ' Name ' ] } )
return result
except :
return [ ]
2021-07-04 17:42:37 +02:00
async def get_brewfather_recipes ( self , offset = 0 ) :
2024-06-17 19:40:38 +02:00
limit = 50
2024-06-23 14:53:03 +02:00
length = self . cbpi . config . get ( ' brewfather_list_length ' , 50 )
2024-06-17 19:40:38 +02:00
repeat = True
2021-06-13 14:38:18 +02:00
brewfather = True
result = [ ]
2024-06-17 19:40:38 +02:00
self . url = " https://api.brewfather.app/v2/recipes "
2021-06-13 14:38:18 +02:00
brewfather_user_id = self . cbpi . config . get ( " brewfather_user_id " , None )
if brewfather_user_id == " " or brewfather_user_id is None :
brewfather = False
brewfather_api_key = self . cbpi . config . get ( " brewfather_api_key " , None )
if brewfather_api_key == " " or brewfather_api_key is None :
brewfather = False
if brewfather == True :
encodedData = base64 . b64encode ( bytes ( f " { brewfather_user_id } : { brewfather_api_key } " , " ISO-8859-1 " ) ) . decode ( " ascii " )
headers = { " Authorization " : " Basic %s " % encodedData }
2024-06-17 19:40:38 +02:00
parameters = { " limit " : limit }
while repeat == True :
try :
async with aiohttp . ClientSession ( headers = headers ) as bf_session :
async with bf_session . get ( self . url , params = parameters ) as r :
2024-06-23 14:53:03 +02:00
if r . status == 429 :
2024-07-01 22:52:00 +02:00
try :
seconds = int ( r . headers [ ' Retry-After ' ] )
minutes = round ( seconds / 60 )
except :
seconds = None
if not seconds :
logging . error ( " Too many requests to BF api. Try again later " )
self . cbpi . notify ( " Error " , " Too many requests to BF api. Try again later " , NotificationType . ERROR )
else :
logging . error ( f " Too many requests to BF api. Try in { minutes } minutes again. " )
self . cbpi . notify ( " Error " , f " Too many requests to BF api. Try in { minutes } minutes again. " , NotificationType . ERROR )
2024-06-23 14:53:03 +02:00
repeat = False
2024-07-01 22:52:00 +02:00
logging . error ( r . headers [ ' Retry-After ' ] )
2024-06-23 14:53:03 +02:00
else :
bf_recipe_list = await r . json ( )
2024-06-17 19:40:38 +02:00
await bf_session . close ( )
except Exception as e :
logging . error ( e )
2024-06-23 14:53:03 +02:00
repeat = False
2024-06-30 12:13:46 +02:00
try :
if bf_recipe_list :
for row in bf_recipe_list :
recipe_id = row [ ' _id ' ]
name = row [ ' name ' ]
element = { ' value ' : recipe_id , ' label ' : name }
result . append ( element )
else :
repeat = False
except Exception as e :
logging . error ( e )
try :
if len ( bf_recipe_list ) != limit :
repeat = False
else :
parameters = { " limit " : limit , ' start_after ' : recipe_id }
except Exception as e :
logging . error ( e )
2024-06-17 19:40:38 +02:00
try :
newlist = sorted ( result , key = lambda d : d [ ' label ' ] )
2024-06-23 14:53:03 +02:00
listlength = len ( newlist )
max = math . floor ( listlength / length )
sortlist = [ ]
for i in range ( 0 , max + 1 ) :
sortlist . append ( { ' value ' : i * length , ' label ' : i * length } )
return newlist , sortlist , length
2024-06-17 19:40:38 +02:00
except :
2024-06-30 12:13:46 +02:00
logging . error ( " Return empty BF recipe list " )
2024-06-23 14:53:03 +02:00
sortlist = [ { ' value ' : 0 , ' label ' : ' 0 ' } ]
return result , sortlist , length
2024-06-17 19:40:38 +02:00
2021-06-13 14:38:18 +02:00
2021-05-30 11:58:18 +02:00
def get_creation_path ( self ) :
creation_path = self . cbpi . config . get ( " RECIPE_CREATION_PATH " , " upload " )
path = { ' path ' : ' upload ' } if creation_path == ' ' else { ' path ' : creation_path }
return path
2021-06-06 13:00:55 +02:00
def allowed_file ( self , filename , extension ) :
return ' . ' in filename and filename . rsplit ( ' . ' , 1 ) [ 1 ] in set ( [ extension ] )
2021-05-30 11:58:18 +02:00
2021-06-06 13:00:55 +02:00
async def FileUpload ( self , data ) :
2021-05-26 21:00:35 +02:00
fileData = data [ ' File ' ]
2021-06-06 13:00:55 +02:00
filename = fileData . filename
recipe_file = fileData . file
content_type = fileData . content_type
2021-05-26 21:00:35 +02:00
2021-06-06 13:00:55 +02:00
if content_type == ' text/xml ' :
2021-05-26 21:00:35 +02:00
try :
2021-10-28 18:35:00 +02:00
beer_xml = recipe_file . read ( ) . decode ( ' utf-8 ' , ' replace ' )
2021-06-06 13:00:55 +02:00
if recipe_file and self . allowed_file ( filename , ' xml ' ) :
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " beer.xml " )
2021-05-26 21:00:35 +02:00
f = open ( self . path , " w " )
2021-06-06 13:00:55 +02:00
f . write ( beer_xml )
2021-05-26 21:00:35 +02:00
f . close ( )
2021-06-06 13:00:55 +02:00
self . cbpi . notify ( " Success " , " XML Recipe {} has been uploaded " . format ( filename ) , NotificationType . SUCCESS )
2021-10-28 18:35:00 +02:00
except Exception as e :
self . cbpi . notify ( " Error " " XML Recipe upload failed: {} " . format ( e ) , NotificationType . ERROR )
2021-05-26 21:00:35 +02:00
pass
2022-01-20 17:44:44 +01:00
elif content_type == ' application/json ' :
try :
mmum_json = recipe_file . read ( ) . decode ( ' utf-8 ' , ' replace ' )
if recipe_file and self . allowed_file ( filename , ' json ' ) :
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " mmum.json " )
2022-01-20 17:44:44 +01:00
f = open ( self . path , " w " )
f . write ( mmum_json )
f . close ( )
self . cbpi . notify ( " Success " , " JSON Recipe {} has been uploaded " . format ( filename ) , NotificationType . SUCCESS )
except Exception as e :
self . cbpi . notify ( " Error " " JSON Recipe upload failed: {} " . format ( e ) , NotificationType . ERROR )
pass
2021-06-06 13:00:55 +02:00
elif content_type == ' application/octet-stream ' :
2021-05-26 21:00:35 +02:00
try :
2021-06-06 13:00:55 +02:00
content = recipe_file . read ( )
if recipe_file and self . allowed_file ( filename , ' sqlite ' ) :
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " kbh.db " )
2021-05-26 21:00:35 +02:00
f = open ( self . path , " wb " )
f . write ( content )
f . close ( )
2021-06-06 13:00:55 +02:00
self . cbpi . notify ( " Success " , " Kleiner Brauhelfer database has been uploaded " , NotificationType . SUCCESS )
2021-05-26 21:00:35 +02:00
except :
self . cbpi . notify ( " Error " , " Kleiner Brauhelfer database upload failed " , NotificationType . ERROR )
pass
else :
self . cbpi . notify ( " Error " , " Wrong content type. Upload failed " , NotificationType . ERROR )
async def kbh_recipe_creation ( self , Recipe_ID ) :
2021-06-15 21:03:56 +02:00
config = self . get_config_values ( )
if self . kettle is not None :
2021-05-26 21:00:35 +02:00
# load beerxml file located in upload folder
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " kbh.db " )
2021-05-26 21:00:35 +02:00
if os . path . exists ( self . path ) is False :
2023-09-08 18:06:13 +02:00
self . cbpi . notify ( " File Not Found " , " Please upload a kbh V2 database file " , NotificationType . ERROR )
2021-05-26 21:00:35 +02:00
try :
2021-06-15 21:03:56 +02:00
# Get Recipe Nmae
2021-05-26 21:00:35 +02:00
conn = sqlite3 . connect ( self . path )
c = conn . cursor ( )
c . execute ( ' SELECT Sudname FROM Sud WHERE ID = ? ' , ( Recipe_ID , ) )
row = c . fetchone ( )
name = row [ 0 ]
2021-06-15 21:03:56 +02:00
# get MashIn Temp
mashin_temp = None
2023-09-08 18:06:13 +02:00
c . execute ( ' SELECT TempWasser FROM Maischplan WHERE Typ = 0 AND SudID = ? ' , ( Recipe_ID , ) )
2021-05-26 21:00:35 +02:00
row = c . fetchone ( )
2021-06-15 21:03:56 +02:00
try :
if self . cbpi . config . get ( " TEMP_UNIT " , " C " ) == " C " :
2024-04-29 17:32:13 +02:00
mashin_temp = str ( int ( row [ 0 ] ) )
2021-06-15 21:03:56 +02:00
else :
2024-04-29 17:32:13 +02:00
mashin_temp = str ( round ( 9.0 / 5.0 * int ( row [ 0 ] ) + 32 ) )
2021-06-15 21:03:56 +02:00
except :
pass
2023-09-08 18:06:13 +02:00
2021-06-15 21:03:56 +02:00
# get the hop addition times
2022-10-03 16:28:42 +02:00
c . execute ( ' SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze <> 1 AND SudID = ? ' , ( Recipe_ID , ) )
2021-06-15 21:03:56 +02:00
hops = c . fetchall ( )
2022-09-02 18:17:08 +02:00
whirlpool = [ ]
for hop in hops :
if hop [ 0 ] < 0 :
whirlpool . append ( hop )
for whirl in whirlpool :
hops . remove ( whirl )
2021-06-15 21:03:56 +02:00
# get the misc addition times
2022-11-05 12:57:44 +01:00
c . execute ( ' SELECT Zugabedauer, Name FROM WeitereZutatenGaben WHERE Zeitpunkt = 1 AND SudID = ? ' , ( Recipe_ID , ) )
2021-06-15 21:03:56 +02:00
miscs = c . fetchall ( )
try :
2022-11-05 12:57:44 +01:00
c . execute ( ' SELECT Zeit, Name FROM Hopfengaben WHERE Vorderwuerze = 1 AND SudID = ? ' , ( Recipe_ID , ) )
2021-06-15 21:03:56 +02:00
FW_Hops = c . fetchall ( )
FirstWort = self . getFirstWort ( FW_Hops , " kbh " )
except :
FirstWort = " No "
2021-06-13 14:38:18 +02:00
2021-05-26 21:00:35 +02:00
c . execute ( ' SELECT Kochdauer FROM Sud WHERE ID = ? ' , ( Recipe_ID , ) )
row = c . fetchone ( )
2021-06-15 21:03:56 +02:00
BoilTime = str ( int ( row [ 0 ] ) )
2023-09-08 18:06:13 +02:00
2021-06-15 21:03:56 +02:00
await self . create_recipe ( name )
2021-06-13 14:38:18 +02:00
2021-06-15 21:03:56 +02:00
if mashin_temp is not None :
step_type = self . mashin if self . mashin != " " else " MashInStep "
step_string = { " name " : " MashIn " ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-06-15 21:03:56 +02:00
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : mashin_temp ,
" Timer " : " 0 " ,
" Notification " : " Target temperature reached. Please add malt. "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
2021-05-26 21:00:35 +02:00
2023-09-08 18:06:13 +02:00
for row in c . execute ( ' SELECT Name, TempRast, DauerRast FROM Maischplan WHERE Typ <> 0 AND SudID = ? ' , ( Recipe_ID , ) ) :
2021-07-15 07:16:26 +02:00
if mashin_temp is None and self . addmashin == " Yes " :
step_type = self . mashin if self . mashin != " " else " MashInStep "
step_string = { " name " : " MashIn " ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
2024-04-29 17:32:13 +02:00
" Temp " : str ( int ( row [ 1 ] ) ) if self . TEMP_UNIT == " C " else str ( round ( 9.0 / 5.0 * int ( row [ 1 ] ) + 32 ) ) ,
2021-07-15 07:16:26 +02:00
" Timer " : " 0 " ,
" Notification " : " Target temperature reached. Please add malt. "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
2021-06-15 21:03:56 +02:00
step_type = self . mash if self . mash != " " else " MashStep "
step_string = { " name " : str ( row [ 0 ] ) ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-06-15 21:03:56 +02:00
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
2024-04-29 17:32:13 +02:00
" Temp " : str ( int ( row [ 1 ] ) ) if self . TEMP_UNIT == " C " else str ( round ( 9.0 / 5.0 * int ( row [ 1 ] ) + 32 ) ) ,
2021-06-15 21:03:56 +02:00
" Timer " : str ( int ( row [ 2 ] ) )
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
2024-04-29 17:34:44 +02:00
2021-06-15 21:03:56 +02:00
# 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 " ,
" props " : {
" AutoNext " : " No " ,
" Kettle " : self . id ,
" Notification " : " Mash Process completed. Please start lautering and press next to start boil. "
2021-05-26 21:00:35 +02:00
2021-06-15 21:03:56 +02:00
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : self . mashout
}
await self . create_step ( step_string )
Hops = self . getBoilAlerts ( hops , miscs , " kbh " )
step_type = self . boil if self . boil != " " else " BoilStep "
step_string = { " name " : " Boil Step " ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-09-21 18:06:23 +02:00
" Kettle " : self . boilid ,
2022-01-03 17:53:22 +01:00
" Sensor " : self . boilkettle . sensor ,
2021-06-15 21:03:56 +02:00
" Temp " : int ( self . BoilTemp ) ,
" Timer " : BoilTime ,
2022-11-05 12:57:44 +01:00
" First_Wort " : FirstWort [ 0 ] ,
" First_Wort_text " : FirstWort [ 1 ] ,
2021-06-15 21:03:56 +02:00
" LidAlert " : " Yes " ,
2022-11-05 12:57:44 +01:00
" Hop_1 " : Hops [ 0 ] [ 0 ] ,
" Hop_1_text " : Hops [ 0 ] [ 1 ] ,
" Hop_2 " : Hops [ 1 ] [ 0 ] ,
" Hop_2_text " : Hops [ 1 ] [ 1 ] ,
" Hop_3 " : Hops [ 2 ] [ 0 ] ,
" Hop_3_text " : Hops [ 2 ] [ 1 ] ,
" Hop_4 " : Hops [ 3 ] [ 0 ] ,
" Hop_4_text " : Hops [ 3 ] [ 1 ] ,
" Hop_5 " : Hops [ 4 ] [ 0 ] ,
" Hop_5_text " : Hops [ 4 ] [ 1 ] ,
" Hop_6 " : Hops [ 5 ] [ 0 ] ,
" Hop_6_text " : Hops [ 5 ] [ 1 ]
2021-06-15 21:03:56 +02:00
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
2021-06-13 14:38:18 +02:00
2021-06-15 21:03:56 +02:00
await self . create_step ( step_string )
2021-06-13 14:38:18 +02:00
2022-09-14 15:08:59 +02:00
if not whirlpool :
await self . create_Whirlpool_Cooldown ( )
else :
await self . create_Whirlpool_Cooldown ( str ( abs ( whirlpool [ 0 ] [ 0 ] ) ) ) # from kbh this value comes as negative but must be positive
2021-06-15 21:03:56 +02:00
self . cbpi . notify ( ' KBH Recipe created ' , name , NotificationType . INFO )
2021-05-26 21:00:35 +02:00
except :
2021-06-15 21:03:56 +02:00
self . cbpi . notify ( ' KBH Recipe creation failure ' , name , NotificationType . ERROR )
2021-05-26 21:00:35 +02:00
pass
2021-06-15 21:03:56 +02:00
else :
self . cbpi . notify ( ' Recipe Upload ' , ' No default Kettle defined. Please specify default Kettle in settings ' , NotificationType . ERROR )
2021-06-10 20:57:22 +02:00
2022-01-20 17:44:44 +01:00
def findMax ( self , string ) :
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " mmum.json " )
2022-01-20 17:44:44 +01:00
e = json . load ( open ( self . path ) )
for idx in range ( 1 , 20 ) :
search_string = string . replace ( " %% " , str ( idx ) )
i = idx
if search_string not in e :
break
return i
def getJsonMashin ( self , id ) :
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " mmum.json " )
2022-01-20 17:44:44 +01:00
e = json . load ( open ( self . path ) )
return float ( e [ ' Infusion_Einmaischtemperatur ' ] )
async def json_recipe_creation ( self , Recipe_ID ) :
config = self . get_config_values ( )
if self . kettle is not None :
# load mmum-json file located in upload folder
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " mmum.json " )
2022-01-20 17:44:44 +01:00
if os . path . exists ( self . path ) is False :
self . cbpi . notify ( " File Not Found " , " Please upload a MMuM-JSON File " , NotificationType . ERROR )
e = json . load ( open ( self . path ) )
name = e [ ' Name ' ]
boil_time = float ( e [ ' Kochzeit_Wuerze ' ] )
await self . create_recipe ( name )
hops = [ ]
for idx in range ( 1 , self . findMax ( " Hopfen_ %% _Kochzeit " ) ) :
hops_name = " %s g %s %s %% alpha " % ( e [ " Hopfen_ {} _Menge " . format ( idx ) ] , e [ " Hopfen_ {} _Sorte " . format ( idx ) ] , e [ " Hopfen_ {} _alpha " . format ( idx ) ] )
if e [ " Hopfen_ {} _Kochzeit " . format ( idx ) ] . isnumeric ( ) :
if boil_time is not e [ " Hopfen_ {} _Kochzeit " . format ( idx ) ] . isnumeric ( ) :
alert = float ( e [ " Hopfen_ {} _Kochzeit " . format ( idx ) ] )
2022-11-05 12:57:44 +01:00
elif e [ " Hopfen_ {} _Kochzeit " . format ( idx ) ] == " Whirlpool " or float ( e [ " Hopfen_ {} _Kochzeit " . format ( idx ) ] ) < 0 :
alert = float ( 0 )
hops_name = hops_name + ' whirlpool '
2022-01-20 17:44:44 +01:00
else :
2022-11-05 12:57:44 +01:00
self . cbpi . notify ( " No Number at Hoptime " , " Please change json-File at Hopfen_ {} _Kochzeit " . format ( idx ) , NotificationType . ERROR )
alert = float ( 0 )
2022-01-20 17:44:44 +01:00
hops . append ( { " name " : hops_name , " time " : alert } )
firstHops = [ ]
for idx in range ( 1 , self . findMax ( " Hopfen_VWH_ %% _Sorte " ) ) :
firstHops_name = " %s g %s %s %% alpha " % ( e [ " Hopfen_VWH_ {} _Menge " . format ( idx ) ] , e [ " Hopfen_VWH_ {} _Sorte " . format ( idx ) ] , e [ " Hopfen_VWH_ {} _alpha " . format ( idx ) ] )
firstHops . append ( { " name " : firstHops_name } )
FirstWort = self . getFirstWort ( firstHops , " json " )
miscs = [ ]
for idx in range ( 1 , self . findMax ( " WeitereZutat_Wuerze_ %% _Kochzeit " ) ) :
miscs_name = " %s %s %s " % ( e [ " WeitereZutat_Wuerze_ {} _Menge " . format ( idx ) ] , e [ " WeitereZutat_Wuerze_ {} _Einheit " . format ( idx ) ] , e [ " WeitereZutat_Wuerze_ {} _Name " . format ( idx ) ] )
if e [ " WeitereZutat_Wuerze_ {} _Kochzeit " . format ( idx ) ] . isnumeric ( ) :
alert = float ( e [ " WeitereZutat_Wuerze_ {} _Kochzeit " . format ( idx ) ] )
2022-11-05 12:57:44 +01:00
elif e [ " WeitereZutat_Wuerze_ {} _Kochzeit " . format ( idx ) ] == " Whirlpool " or float ( e [ " WeitereZutat_Wuerze_ {} _Kochzeit " . format ( idx ) ] ) < 0 :
alert = float ( 0 )
miscs_name = miscs_name + ' whirlpool '
2022-01-20 17:44:44 +01:00
else :
self . api . notify ( headline = " No Number at Hoptime " , message = " Please change json-File at WeitereZutat_Wuerze_ {} _Kochzeit " . format ( idx ) , type = " danger " )
2022-11-05 12:57:44 +01:00
alert = float ( 0 )
2022-01-20 17:44:44 +01:00
miscs . append ( { " name " : miscs_name , " time " : alert } )
# Mash Steps -> first step is different as it heats up to defined temp and stops with notification to add malt
# AutoMode is yes to start and stop automatic mode or each step
MashIn_Flag = True
step_kettle = self . id
2022-01-25 18:34:11 +01:00
last_step_temp = 0
2022-01-20 17:44:44 +01:00
logging . info ( step_kettle ) ###################################################
for row in self . getSteps ( Recipe_ID , " json " ) :
step_name = str ( row . get ( " name " ) )
step_timer = str ( int ( row . get ( " timer " ) ) )
step_temp = str ( int ( row . get ( " temp " ) ) )
2022-01-25 18:34:11 +01:00
last_step_temp = step_temp
2022-01-20 17:44:44 +01:00
sensor = self . kettle . sensor
if MashIn_Flag == True :
if row . get ( " timer " ) == 0 :
step_type = self . mashin if self . mashin != " " else " MashInStep "
Notification = " Target temperature reached. Please add malt. "
MashIn_Flag = False
if step_name is None or step_name == " " :
step_name = " MashIn "
elif self . addmashin == " Yes " :
step_type = self . mashin if self . mashin != " " else " MashInStep "
Notification = " Target temperature reached. Please add malt. "
MashIn_Flag = False
step_string = { " name " : " MashIn " ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : self . getJsonMashin ( Recipe_ID ) ,
" Timer " : 0 ,
" Notification " : Notification
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
logging . info ( step_kettle ) ###################################################
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
else :
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
else :
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
step_string = { " name " : step_name ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : step_temp ,
" Timer " : step_timer ,
" Notification " : Notification
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
2022-01-25 18:16:04 +01:00
# MashOut -> mashStep to reach mashout-temp for 1 min
2022-01-25 18:34:11 +01:00
if last_step_temp != e [ " Abmaischtemperatur " ] :
step_string = { " name " : " MashOut " ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : e [ " Abmaischtemperatur " ] ,
" Timer " : 1 ,
" Notification " : " "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : " MashStep "
}
2022-01-20 17:44:44 +01:00
2022-01-25 18:34:11 +01:00
await self . create_step ( step_string )
2022-01-25 18:16:04 +01:00
# Lautering -> Simple step that sends notification and waits for user input to move to next step (AutoNext=No)
2022-01-20 17:44:44 +01:00
if self . mashout == " NotificationStep " :
step_string = { " name " : " Lautering " ,
" props " : {
" AutoNext " : " No " ,
" Kettle " : self . id ,
" Notification " : " Mash Process completed. Please start lautering and press next to start boil. "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : self . mashout
}
await self . create_step ( step_string )
# Measure Original Gravity -> Simple step that sends notification
step_string = { " name " : " Measure Original Gravity " ,
" props " : {
" AutoNext " : " No " ,
" Kettle " : self . id ,
" Notification " : " What is the original gravity of the beer wort? "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : " NotificationStep "
}
await self . create_step ( step_string )
# Boil step including hop alarms and alarm for first wort hops -> Automode is set tu yes
Hops = self . getBoilAlerts ( hops , miscs , " json " )
step_kettle = self . boilid
step_type = self . boil if self . boil != " " else " BoilStep "
step_time = str ( int ( boil_time ) )
step_temp = self . BoilTemp
sensor = self . boilkettle . sensor
LidAlert = " Yes "
logging . info ( step_temp ) ###################################################
step_string = { " name " : " Boil Step " ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : step_kettle ,
" Sensor " : sensor ,
" Temp " : step_temp ,
" Timer " : step_time ,
2022-11-05 12:57:44 +01:00
" First_Wort " : FirstWort [ 0 ] ,
" First_Wort_text " : FirstWort [ 1 ] ,
2022-01-20 17:44:44 +01:00
" LidAlert " : LidAlert ,
2022-11-05 12:57:44 +01:00
" Hop_1 " : Hops [ 0 ] [ 0 ] ,
" Hop_1_text " : Hops [ 0 ] [ 1 ] ,
" Hop_2 " : Hops [ 1 ] [ 0 ] ,
" Hop_2_text " : Hops [ 1 ] [ 1 ] ,
" Hop_3 " : Hops [ 2 ] [ 0 ] ,
" Hop_3_text " : Hops [ 2 ] [ 1 ] ,
" Hop_4 " : Hops [ 3 ] [ 0 ] ,
" Hop_4_text " : Hops [ 3 ] [ 1 ] ,
" Hop_5 " : Hops [ 4 ] [ 0 ] ,
" Hop_5_text " : Hops [ 4 ] [ 1 ] ,
" Hop_6 " : Hops [ 5 ] [ 0 ] ,
" Hop_6_text " : Hops [ 5 ] [ 1 ]
2022-01-20 17:44:44 +01:00
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
# Measure Original Gravity -> Simple step that sends notification
step_string = { " name " : " Measure Original Gravity " ,
" props " : {
" AutoNext " : " No " ,
" Kettle " : self . id ,
" Notification " : " What is the original gravity of the beer wort? "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : " NotificationStep "
}
await self . create_step ( step_string )
await self . create_Whirlpool_Cooldown ( )
self . cbpi . notify ( ' MMuM-JSON Recipe created ' , name , NotificationType . INFO )
else :
self . cbpi . notify ( ' Recipe Upload ' , ' No default Kettle defined. Please specify default Kettle in settings ' , NotificationType . ERROR )
2021-05-26 21:00:35 +02:00
async def xml_recipe_creation ( self , Recipe_ID ) :
2021-06-15 21:03:56 +02:00
config = self . get_config_values ( )
2021-05-26 21:00:35 +02:00
2021-06-15 21:03:56 +02:00
if self . kettle is not None :
2021-05-26 21:00:35 +02:00
# load beerxml file located in upload folder
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " beer.xml " )
2021-05-26 21:00:35 +02:00
if os . path . exists ( self . path ) is False :
self . cbpi . notify ( " File Not Found " , " Please upload a Beer.xml File " , NotificationType . ERROR )
2021-06-15 21:03:56 +02:00
e = xml . etree . ElementTree . parse ( self . path ) . getroot ( )
recipe = e . find ( ' ./RECIPE[ %s ] ' % ( str ( Recipe_ID ) ) )
hops = recipe . findall ( ' ./HOPS/HOP ' )
miscs = recipe . findall ( ' MISCS/MISC[USE= " Boil " ] ' )
name = e . find ( ' ./RECIPE[ %s ]/NAME ' % ( str ( Recipe_ID ) ) ) . text
boil_time = float ( e . find ( ' ./RECIPE[ %s ]/BOIL_TIME ' % ( str ( Recipe_ID ) ) ) . text )
FirstWort = self . getFirstWort ( hops , " xml " )
await self . create_recipe ( name )
2021-05-26 21:00:35 +02:00
# Mash Steps -> first step is different as it heats up to defined temp and stops with notification to add malt
# AutoMode is yes to start and stop automatic mode or each step
MashIn_Flag = True
step_kettle = self . id
2022-01-20 17:44:44 +01:00
for row in self . getSteps ( Recipe_ID , " xml " ) :
2021-05-26 21:00:35 +02:00
step_name = str ( row . get ( " name " ) )
step_timer = str ( int ( row . get ( " timer " ) ) )
step_temp = str ( int ( row . get ( " temp " ) ) )
sensor = self . kettle . sensor
2021-07-15 07:16:26 +02:00
if MashIn_Flag == True :
if row . get ( " timer " ) == 0 :
step_type = self . mashin if self . mashin != " " else " MashInStep "
Notification = " Target temperature reached. Please add malt. "
MashIn_Flag = False
if step_name is None or step_name == " " :
step_name = " MashIn "
elif self . addmashin == " Yes " :
step_type = self . mashin if self . mashin != " " else " MashInStep "
Notification = " Target temperature reached. Please add malt. "
MashIn_Flag = False
step_string = { " name " : " MashIn " ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : step_temp ,
" Timer " : 0 ,
" Notification " : Notification
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
else :
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
2021-05-26 21:00:35 +02:00
else :
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
2021-06-15 21:03:56 +02:00
step_string = { " name " : step_name ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-06-15 21:03:56 +02:00
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : step_temp ,
" Timer " : step_timer ,
" Notification " : Notification
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
2021-05-26 21:00:35 +02:00
# MashOut -> Simple step that sends notification and waits for user input to move to next step (AutoNext=No)
if self . mashout == " NotificationStep " :
2021-06-15 21:03:56 +02:00
step_string = { " name " : " Lautering " ,
" props " : {
" AutoNext " : " No " ,
" Kettle " : self . id ,
" Notification " : " Mash Process completed. Please start lautering and press next to start boil. "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : self . mashout
}
await self . create_step ( step_string )
2021-05-26 21:00:35 +02:00
# Boil step including hop alarms and alarm for first wort hops -> Automode is set tu yes
2021-06-15 21:03:56 +02:00
Hops = self . getBoilAlerts ( hops , miscs , " xml " )
2021-09-21 18:06:23 +02:00
step_kettle = self . boilid
2021-05-26 21:00:35 +02:00
step_type = self . boil if self . boil != " " else " BoilStep "
2021-06-15 21:03:56 +02:00
step_time = str ( int ( boil_time ) )
2021-05-26 21:00:35 +02:00
step_temp = self . BoilTemp
2022-01-03 17:53:22 +01:00
sensor = self . boilkettle . sensor
2021-05-26 21:00:35 +02:00
LidAlert = " Yes "
2021-06-15 21:03:56 +02:00
step_string = { " name " : " Boil Step " ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-06-15 21:03:56 +02:00
" Kettle " : step_kettle ,
" Sensor " : sensor ,
" Temp " : step_temp ,
" Timer " : step_time ,
2022-11-05 12:57:44 +01:00
" First_Wort " : FirstWort [ 0 ] ,
" First_Wort_text " : FirstWort [ 1 ] ,
2021-06-15 21:03:56 +02:00
" LidAlert " : LidAlert ,
2022-11-05 12:57:44 +01:00
" Hop_1 " : Hops [ 0 ] [ 0 ] ,
" Hop_1_text " : Hops [ 0 ] [ 1 ] ,
" Hop_2 " : Hops [ 1 ] [ 0 ] ,
" Hop_2_text " : Hops [ 1 ] [ 1 ] ,
" Hop_3 " : Hops [ 2 ] [ 0 ] ,
" Hop_3_text " : Hops [ 2 ] [ 1 ] ,
" Hop_4 " : Hops [ 3 ] [ 0 ] ,
" Hop_4_text " : Hops [ 3 ] [ 1 ] ,
" Hop_5 " : Hops [ 4 ] [ 0 ] ,
" Hop_5_text " : Hops [ 4 ] [ 1 ] ,
" Hop_6 " : Hops [ 5 ] [ 0 ] ,
" Hop_6_text " : Hops [ 5 ] [ 1 ]
2021-06-15 21:03:56 +02:00
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
2021-05-26 21:00:35 +02:00
2021-06-15 21:03:56 +02:00
await self . create_step ( step_string )
2021-05-26 21:00:35 +02:00
2021-06-15 21:03:56 +02:00
await self . create_Whirlpool_Cooldown ( )
2021-05-26 21:00:35 +02:00
2021-06-15 21:03:56 +02:00
self . cbpi . notify ( ' BeerXML Recipe created ' , name , NotificationType . INFO )
else :
self . cbpi . notify ( ' Recipe Upload ' , ' No default Kettle defined. Please specify default Kettle in settings ' , NotificationType . ERROR )
2021-05-26 21:00:35 +02:00
2021-06-15 21:03:56 +02:00
# XML functions to retrieve xml repice parameters (if multiple recipes are stored in one xml file, id could be used)
2021-05-26 21:00:35 +02:00
2022-01-20 17:44:44 +01:00
def getSteps ( self , id , recipe_type ) :
2021-05-26 21:00:35 +02:00
steps = [ ]
2022-01-20 17:44:44 +01:00
if recipe_type == " xml " :
e = xml . etree . ElementTree . parse ( self . path ) . getroot ( )
for e in e . findall ( ' ./RECIPE[ %s ]/MASH/MASH_STEPS/MASH_STEP ' % ( str ( id ) ) ) :
if self . cbpi . config . get ( " TEMP_UNIT " , " C " ) == " C " :
temp = float ( e . find ( " STEP_TEMP " ) . text )
else :
temp = round ( 9.0 / 5.0 * float ( e . find ( " STEP_TEMP " ) . text ) + 32 , 2 )
steps . append ( { " name " : e . find ( " NAME " ) . text , " temp " : temp , " timer " : float ( e . find ( " STEP_TIME " ) . text ) } )
elif recipe_type == " json " :
2022-02-19 12:33:11 +01:00
self . path = self . cbpi . config_folder . get_upload_file ( " mmum.json " )
2022-01-20 17:44:44 +01:00
e = json . load ( open ( self . path ) )
for idx in range ( 1 , self . findMax ( " Infusion_Rastzeit %% " ) ) :
if self . cbpi . config . get ( " TEMP_UNIT " , " C " ) == " C " :
temp = float ( e [ " Infusion_Rasttemperatur {} " . format ( idx ) ] )
else :
temp = round ( 9.0 / 5.0 * float ( e [ " Infusion_Rasttemperatur {} " . format ( idx ) ] ) + 32 , 2 )
steps . append ( { " name " : " Rast {} " . format ( idx ) , " temp " : temp , " timer " : float ( e [ " Infusion_Rastzeit {} " . format ( idx ) ] ) } )
2021-05-26 21:00:35 +02:00
return steps
2021-06-13 14:38:18 +02:00
async def bf_recipe_creation ( self , Recipe_ID ) :
2021-06-15 21:03:56 +02:00
config = self . get_config_values ( )
2021-06-13 14:38:18 +02:00
2021-06-15 21:03:56 +02:00
if self . kettle is not None :
2021-06-13 14:38:18 +02:00
brewfather = True
result = [ ]
2024-06-17 19:40:38 +02:00
self . bf_url = " https://api.brewfather.app/v2/recipes/ " + Recipe_ID
2021-06-13 14:38:18 +02:00
brewfather_user_id = self . cbpi . config . get ( " brewfather_user_id " , None )
if brewfather_user_id == " " or brewfather_user_id is None :
brewfather = False
brewfather_api_key = self . cbpi . config . get ( " brewfather_api_key " , None )
if brewfather_api_key == " " or brewfather_api_key is None :
brewfather = False
if brewfather == True :
encodedData = base64 . b64encode ( bytes ( f " { brewfather_user_id } : { brewfather_api_key } " , " ISO-8859-1 " ) ) . decode ( " ascii " )
headers = { " Authorization " : " Basic %s " % encodedData }
bf_recipe = " "
async with aiohttp . ClientSession ( headers = headers ) as bf_session :
async with bf_session . get ( self . bf_url ) as r :
bf_recipe = await r . json ( )
await bf_session . close ( )
2021-11-03 09:30:16 +01:00
2021-06-13 14:38:18 +02:00
if bf_recipe != " " :
2021-11-03 09:30:16 +01:00
try :
StrikeTemp = bf_recipe [ ' data ' ] [ ' strikeTemp ' ]
except :
2021-11-03 12:53:34 +01:00
StrikeTemp = None
# BF is sending all Temeprature values in °C. If system is running in F, values need to be converted
if StrikeTemp is not None and self . TEMP_UNIT != " C " :
StrikeTemp = round ( ( 9.0 / 5.0 * float ( StrikeTemp ) + 32 ) )
2021-06-13 14:38:18 +02:00
RecipeName = bf_recipe [ ' name ' ]
BoilTime = bf_recipe [ ' boilTime ' ]
mash_steps = bf_recipe [ ' mash ' ] [ ' steps ' ]
hops = bf_recipe [ ' hops ' ]
try :
miscs = bf_recipe [ ' miscs ' ]
except :
miscs = None
2024-07-12 15:37:31 +02:00
try :
fermentation_steps = bf_recipe [ ' fermentation ' ] [ ' steps ' ]
except :
fermentation_steps = None
if fermentation_steps is not None :
try :
step = fermentation_steps [ 0 ]
self . fermentation_step_temp = int ( step [ ' stepTemp ' ] )
except :
self . fermentation_step_temp = None
if self . fermentation_step_temp is not None and self . TEMP_UNIT != " C " :
self . fermentation_step_temp = round ( ( 9.0 / 5.0 * float ( self . fermentation_step_temp ) + 32 ) )
2021-06-15 21:03:56 +02:00
FirstWort = self . getFirstWort ( hops , " bf " )
2021-06-13 14:38:18 +02:00
2021-06-15 21:03:56 +02:00
await self . create_recipe ( RecipeName )
2021-06-13 14:38:18 +02:00
# Mash Steps -> first step is different as it heats up to defined temp and stops with notification to add malt
# AutoMode is yes to start and stop automatic mode or each step
MashIn_Flag = True
step_kettle = self . id
for step in mash_steps :
2021-07-13 07:07:55 +02:00
try :
step_name = step [ ' name ' ]
if step_name == " " :
2021-07-15 07:16:26 +02:00
step_name = " MashStep "
2021-07-13 07:07:55 +02:00
except :
step_name = " MashStep "
2021-07-15 07:16:26 +02:00
2021-06-13 14:38:18 +02:00
step_timer = str ( int ( step [ ' stepTime ' ] ) )
if self . TEMP_UNIT == " C " :
step_temp = str ( int ( step [ ' stepTemp ' ] ) )
else :
step_temp = str ( round ( ( 9.0 / 5.0 * int ( step [ ' stepTemp ' ] ) + 32 ) ) )
sensor = self . kettle . sensor
2021-07-15 07:16:26 +02:00
if MashIn_Flag == True :
if int ( step_timer ) == 0 :
step_type = self . mashin if self . mashin != " " else " MashInStep "
Notification = " Target temperature reached. Please add malt. "
MashIn_Flag = False
elif self . addmashin == " Yes " :
2021-11-03 09:30:16 +01:00
mashin_temp = str ( round ( StrikeTemp ) ) if StrikeTemp is not None else step_temp
2021-07-15 07:16:26 +02:00
step_type = self . mashin if self . mashin != " " else " MashInStep "
Notification = " Target temperature reached. Please add malt. "
MashIn_Flag = False
step_string = { " name " : " MashIn " ,
" props " : {
" AutoMode " : self . AutoMode ,
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
2021-11-03 09:30:16 +01:00
" Temp " : mashin_temp ,
2021-07-15 07:16:26 +02:00
" Timer " : 0 ,
" Notification " : Notification
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
else :
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
2021-06-13 14:38:18 +02:00
else :
step_type = self . mash if self . mash != " " else " MashStep "
Notification = " "
2021-06-15 21:03:56 +02:00
step_string = { " name " : step_name ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-06-15 21:03:56 +02:00
" Kettle " : self . id ,
" Sensor " : self . kettle . sensor ,
" Temp " : step_temp ,
" Timer " : step_timer ,
" Notification " : Notification
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
2021-06-13 14:38:18 +02:00
# MashOut -> Simple step that sends notification and waits for user input to move to next step (AutoNext=No)
2021-06-15 21:03:56 +02:00
2021-06-13 14:38:18 +02:00
if self . mashout == " NotificationStep " :
2021-06-15 21:03:56 +02:00
step_string = { " name " : " Lautering " ,
" props " : {
" AutoNext " : " No " ,
" Kettle " : self . id ,
" Notification " : " Mash Process completed. Please start lautering and press next to start boil. "
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : self . mashout
}
await self . create_step ( step_string )
2021-06-13 14:38:18 +02:00
# Boil step including hop alarms and alarm for first wort hops -> Automode is set tu yes
2021-06-15 21:03:56 +02:00
Hops = self . getBoilAlerts ( hops , miscs , " bf " )
2021-06-13 14:38:18 +02:00
2021-09-21 18:06:23 +02:00
step_kettle = self . boilid
2021-06-13 14:38:18 +02:00
step_time = str ( int ( BoilTime ) )
step_type = self . boil if self . boil != " " else " BoilStep "
step_temp = self . BoilTemp
2022-01-03 17:53:22 +01:00
sensor = self . boilkettle . sensor
2021-06-13 14:38:18 +02:00
LidAlert = " Yes "
2021-06-15 21:03:56 +02:00
step_string = { " name " : " Boil Step " ,
" props " : {
2021-06-21 06:51:58 +02:00
" AutoMode " : self . AutoMode ,
2021-06-15 21:03:56 +02:00
" Kettle " : step_kettle ,
" Sensor " : sensor ,
" Temp " : step_temp ,
" Timer " : step_time ,
2022-11-05 12:57:44 +01:00
" First_Wort " : FirstWort [ 0 ] ,
" First_Wort_text " : FirstWort [ 1 ] ,
2021-06-15 21:03:56 +02:00
" LidAlert " : LidAlert ,
2022-11-05 12:57:44 +01:00
" Hop_1 " : Hops [ 0 ] [ 0 ] ,
" Hop_1_text " : Hops [ 0 ] [ 1 ] ,
" Hop_2 " : Hops [ 1 ] [ 0 ] ,
" Hop_2_text " : Hops [ 1 ] [ 1 ] ,
" Hop_3 " : Hops [ 2 ] [ 0 ] ,
" Hop_3_text " : Hops [ 2 ] [ 1 ] ,
" Hop_4 " : Hops [ 3 ] [ 0 ] ,
" Hop_4_text " : Hops [ 3 ] [ 1 ] ,
" Hop_5 " : Hops [ 4 ] [ 0 ] ,
" Hop_5_text " : Hops [ 4 ] [ 1 ] ,
" Hop_6 " : Hops [ 5 ] [ 0 ] ,
" Hop_6_text " : Hops [ 5 ] [ 1 ]
2021-06-15 21:03:56 +02:00
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
await self . create_Whirlpool_Cooldown ( )
2021-06-13 14:38:18 +02:00
2021-06-15 21:03:56 +02:00
self . cbpi . notify ( ' Brewfather App Recipe created: ' , RecipeName , NotificationType . INFO )
else :
self . cbpi . notify ( ' Recipe Upload ' , ' No default Kettle defined. Please specify default Kettle in settings ' , NotificationType . ERROR )
2021-06-13 14:38:18 +02:00
2021-06-15 21:03:56 +02:00
def getBoilAlerts ( self , hops , miscs , recipe_type ) :
2021-06-13 14:38:18 +02:00
alerts = [ ]
for hop in hops :
2021-06-15 21:03:56 +02:00
if recipe_type == " xml " :
use = hop . find ( ' USE ' ) . text
## Hops which are not used in the boil step should not cause alerts
2021-06-13 14:38:18 +02:00
if use != ' Aroma ' and use != ' Boil ' :
continue
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( hop . find ( ' TIME ' ) . text ) , hop . find ( ' NAME ' ) . text ] )
2021-06-15 21:03:56 +02:00
elif recipe_type == " bf " :
use = hop [ ' use ' ]
if use != ' Aroma ' and use != ' Boil ' :
continue
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( hop [ ' time ' ] ) , hop [ ' name ' ] ] ) ## TODO: Testing
2021-06-15 21:03:56 +02:00
elif recipe_type == " kbh " :
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( hop [ 0 ] ) , hop [ 1 ] ] )
2022-01-20 17:44:44 +01:00
elif recipe_type == " json " :
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( hop [ ' time ' ] ) , hop [ ' name ' ] ] )
2022-01-20 17:44:44 +01:00
2021-06-15 21:03:56 +02:00
## There might also be miscelaneous additions during boild time
if miscs is not None :
for misc in miscs :
if recipe_type == " xml " :
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( misc . find ( ' TIME ' ) . text ) , misc . find ( ' NAME ' ) . text ] )
2021-06-15 21:03:56 +02:00
elif recipe_type == " bf " :
use = misc [ ' use ' ]
if use != ' Aroma ' and use != ' Boil ' :
continue
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( misc [ ' time ' ] ) , misc [ ' name ' ] ] ) ## TODO: Testing
2021-06-15 21:03:56 +02:00
elif recipe_type == " kbh " :
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( misc [ 0 ] ) , misc [ 1 ] ] )
2022-01-20 17:44:44 +01:00
elif recipe_type == " json " :
2022-11-05 12:57:44 +01:00
alerts . append ( [ float ( misc [ ' time ' ] ) , misc [ ' name ' ] ] )
## Dedupe and order the additions by their time
2021-06-13 14:38:18 +02:00
## CBP should have these additions in reverse
2022-11-05 12:57:44 +01:00
alerts = sorted ( alerts , key = lambda x : x [ 0 ] , reverse = True )
hop_alerts = [ [ None , None ] , [ None , None ] , [ None , None ] , [ None , None ] , [ None , None ] , [ None , None ] ]
2021-06-15 21:03:56 +02:00
for i in range ( 0 , 6 ) :
try :
2022-11-05 12:57:44 +01:00
if float ( alerts [ i ] [ 0 ] ) > - 1 :
hop_alerts [ i ] = alerts [ i ]
2021-06-15 21:03:56 +02:00
except :
2022-11-05 12:57:44 +01:00
pass
2021-06-15 21:03:56 +02:00
return hop_alerts
def getFirstWort ( self , hops , recipe_type ) :
alert = " No "
2022-11-05 12:57:44 +01:00
names = [ ]
2021-06-15 21:03:56 +02:00
if recipe_type == " kbh " :
if len ( hops ) != 0 :
alert = " Yes "
2022-11-05 12:57:44 +01:00
for hop in hops :
names . append ( hop [ 1 ] )
2021-06-15 21:03:56 +02:00
elif recipe_type == " xml " :
for hop in hops :
use = hop . find ( ' USE ' ) . text
## Hops which are not used in the boil step should not cause alerts
if use != ' First Wort ' :
continue
alert = " Yes "
2022-11-05 12:57:44 +01:00
names . append ( hop . find ( ' NAME ' ) . text )
2021-06-15 21:03:56 +02:00
elif recipe_type == " bf " :
for hop in hops :
if hop [ ' use ' ] == " First Wort " :
alert = " Yes "
2022-11-05 12:57:44 +01:00
names . append ( hop [ ' name ' ] ) ## TODO: Testing
2022-01-20 17:44:44 +01:00
elif recipe_type == " json " :
2022-11-05 12:57:44 +01:00
if len ( hops ) != 0 :
alert = " Yes "
for hop in hops :
names . append ( hop [ ' name ' ] )
return [ alert , " and " . join ( names ) ]
2021-06-13 14:38:18 +02:00
2022-09-02 18:17:08 +02:00
async def create_Whirlpool_Cooldown ( self , time : str = " 15 " ) :
2021-06-15 21:03:56 +02:00
# Add Waitstep as Whirlpool
if self . cooldown != " WaiStep " and self . cooldown != " " :
step_string = { " name " : " Whirlpool " ,
2021-05-26 21:00:35 +02:00
" props " : {
2021-09-21 18:06:23 +02:00
" Kettle " : self . boilid ,
2022-09-02 18:17:08 +02:00
" Timer " : time
2021-05-26 21:00:35 +02:00
} ,
" status_text " : " " ,
" status " : " I " ,
2021-06-15 21:03:56 +02:00
" type " : " WaitStep "
2021-05-26 21:00:35 +02:00
}
2021-06-15 21:03:56 +02:00
await self . create_step ( step_string )
# CoolDown step is sending a notification when cooldowntemp is reached
step_type = self . cooldown if self . cooldown != " " else " WaitStep "
step_name = " CoolDown "
cooldown_sensor = " "
step_temp = " "
2022-09-02 18:17:08 +02:00
step_timer = time
2021-06-15 21:03:56 +02:00
if step_type == " CooldownStep " :
cooldown_sensor = self . cbpi . config . get ( " steps_cooldown_sensor " , None )
if cooldown_sensor is None or cooldown_sensor == ' ' :
2022-01-03 17:53:22 +01:00
cooldown_sensor = self . boilkettle . sensor # fall back to boilkettle sensor if no other sensor is specified
2024-07-12 15:37:31 +02:00
step_timer = " "
step_temp = int ( self . CoolDownTemp ) if ( self . fermentation_step_temp is None or self . fermentation_step_temp < = int ( self . CoolDownTemp ) ) else self . fermentation_step_temp
2021-06-15 21:03:56 +02:00
step_string = { " name " : " Cooldown " ,
" props " : {
2021-09-21 18:06:23 +02:00
" Kettle " : self . boilid ,
2021-06-15 21:03:56 +02:00
" Timer " : step_timer ,
" Temp " : step_temp ,
2022-01-28 07:56:24 +01:00
" Sensor " : cooldown_sensor ,
" Actor " : self . CoolDownActor
2021-06-15 21:03:56 +02:00
} ,
" status_text " : " " ,
" status " : " I " ,
" type " : step_type
}
await self . create_step ( step_string )
def get_config_values ( self ) :
self . kettle = None
2021-09-21 18:06:23 +02:00
self . boilkettle = None
2021-06-15 21:03:56 +02:00
#Define MashSteps
self . TEMP_UNIT = self . cbpi . config . get ( " TEMP_UNIT " , " C " )
2021-06-21 06:51:58 +02:00
self . AutoMode = self . cbpi . config . get ( " AutoMode " , " Yes " )
2021-06-15 21:03:56 +02:00
self . mashin = self . cbpi . config . get ( " steps_mashin " , " MashInStep " )
self . mash = self . cbpi . config . get ( " steps_mash " , " MashStep " )
self . mashout = self . cbpi . config . get ( " steps_mashout " , None ) # Currently used only for the Braumeister
self . boil = self . cbpi . config . get ( " steps_boil " , " BoilStep " )
self . whirlpool = " Waitstep "
self . cooldown = self . cbpi . config . get ( " steps_cooldown " , " WaitStep " )
#get default boil temp from settings
self . BoilTemp = self . cbpi . config . get ( " steps_boil_temp " , 98 )
#get default cooldown temp alarm setting
self . CoolDownTemp = self . cbpi . config . get ( " steps_cooldown_temp " , 25 )
2022-01-28 07:56:24 +01:00
self . CoolDownActor = self . cbpi . config . get ( " steps_cooldown_actor " , None )
2021-06-15 21:03:56 +02:00
# get default Kettle from Settings
self . id = self . cbpi . config . get ( ' MASH_TUN ' , None )
2021-09-21 18:06:23 +02:00
self . boilid = self . cbpi . config . get ( ' BoilKettle ' , None )
if self . boilid is None :
self . boilid = self . id
2021-07-15 07:16:26 +02:00
# If next parameter is Yes, MashIn Ste will be added before first mash step if not included in recipe
self . addmashin = self . cbpi . config . get ( ' AddMashInStep ' , " Yes " )
2021-06-15 21:03:56 +02:00
try :
self . kettle = self . cbpi . kettle . find_by_id ( self . id )
except :
self . cbpi . notify ( ' Recipe Upload ' , ' No default Kettle defined. Please specify default Kettle in settings ' , NotificationType . ERROR )
2021-09-21 18:06:23 +02:00
try :
self . boilkettle = self . cbpi . kettle . find_by_id ( self . boilid )
except :
2022-01-03 17:53:22 +01:00
self . boilkettle = self . kettle
2021-09-21 18:06:23 +02:00
2021-06-15 21:03:56 +02:00
config_values = { " kettle " : self . kettle ,
" kettle_id " : str ( self . id ) ,
2021-09-21 18:06:23 +02:00
" boilkettle " : self . boilkettle ,
" boilkettle_id " : str ( self . boilid ) ,
2021-06-15 21:03:56 +02:00
" mashin " : str ( self . mashin ) ,
" mash " : str ( self . mash ) ,
" mashout " : str ( self . mashout ) ,
" boil " : str ( self . boil ) ,
" whirlpool " : str ( self . whirlpool ) ,
" cooldown " : str ( self . cooldown ) ,
" boiltemp " : str ( self . BoilTemp ) ,
" cooldowntemp " : str ( self . CoolDownTemp ) ,
2022-01-28 07:56:24 +01:00
" cooldownactor " : self . CoolDownActor ,
2021-06-21 06:51:58 +02:00
" temp_unit " : str ( self . TEMP_UNIT ) ,
" AutoMode " : str ( self . AutoMode )
2021-06-15 21:03:56 +02:00
}
2021-09-21 18:06:23 +02:00
logging . info ( config_values )
2021-06-15 21:03:56 +02:00
return config_values
async def create_recipe ( self , name ) :
# Create recipe in recipe Book with name of first recipe in xml file
self . recipeID = await self . cbpi . recipe . create ( name )
# send recipe to mash profile
await self . cbpi . recipe . brew ( self . recipeID )
# remove empty recipe from recipe book
await self . cbpi . recipe . remove ( self . recipeID )
# function to create json to be send to api to add a step to the current mash profile. Currently all properties are send to each step which does not cuase an issue
async def create_step ( self , step_string ) :
#get server port from settings and define url for api calls -> adding steps
self . port = str ( self . cbpi . static_config . get ( ' port ' , 8000 ) )
self . url = " http://127.0.0.1: " + self . port + " /step2/ "
2021-05-26 21:00:35 +02:00
# convert step:string to json required for api call.
step = json . dumps ( step_string )
headers = { ' Content-Type ' : ' application/json ' , ' Accept ' : ' application/json ' }
async with aiohttp . ClientSession ( headers = headers ) as session :
async with session . post ( self . url , data = step ) as response :
return await response . text ( )
2021-06-13 14:38:18 +02:00
await self . push_update ( )