mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-12-04 04:28:26 +01:00
commit
8b42600781
11 changed files with 296 additions and 50 deletions
|
@ -1,3 +1,3 @@
|
||||||
__version__ = "4.4.1.rc1"
|
__version__ = "4.4.3"
|
||||||
__codename__ = "Yeast Starter"
|
__codename__ = "Yeast Starter"
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,11 @@ from cbpi.utils.utils import load_config
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
from cbpi.craftbeerpi import CraftBeerPi
|
from cbpi.craftbeerpi import CraftBeerPi
|
||||||
import os
|
import os
|
||||||
|
try:
|
||||||
|
import pwd
|
||||||
|
module_pwd=True
|
||||||
|
except:
|
||||||
|
module_pwd=False
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import shutil
|
import shutil
|
||||||
import click
|
import click
|
||||||
|
@ -165,7 +170,8 @@ 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()
|
#user=os.getlogin()
|
||||||
|
user=pwd.getpwuid(os.getuid()).pw_name
|
||||||
path="/usr/local/bin/cbpi"
|
path="/usr/local/bin/cbpi"
|
||||||
if os.path.exists("/home/"+user+"/.local/bin/cbpi") is True:
|
if os.path.exists("/home/"+user+"/.local/bin/cbpi") is True:
|
||||||
path="/home/"+user+"/.local/bin/cbpi"
|
path="/home/"+user+"/.local/bin/cbpi"
|
||||||
|
|
|
@ -11,6 +11,7 @@ mqtt_host: localhost
|
||||||
mqtt_port: 1883
|
mqtt_port: 1883
|
||||||
mqtt_username: ""
|
mqtt_username: ""
|
||||||
mqtt_password: ""
|
mqtt_password: ""
|
||||||
|
mqtt_offset: false
|
||||||
|
|
||||||
username: cbpi
|
username: cbpi
|
||||||
password: 123
|
password: 123
|
||||||
|
|
|
@ -17,13 +17,12 @@ import os.path
|
||||||
from os import listdir
|
from os import listdir
|
||||||
from os.path import isfile, join
|
from os.path import isfile, join
|
||||||
import json
|
import json
|
||||||
import shortuuid
|
import math
|
||||||
import yaml
|
import yaml
|
||||||
from ..api.step import StepMove, StepResult, StepState
|
from ..api.step import StepMove, StepResult, StepState
|
||||||
import re
|
import re
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
|
||||||
class UploadController:
|
class UploadController:
|
||||||
|
|
||||||
def __init__(self, cbpi):
|
def __init__(self, cbpi):
|
||||||
|
@ -70,9 +69,12 @@ class UploadController:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def get_brewfather_recipes(self,offset=0):
|
async def get_brewfather_recipes(self,offset=0):
|
||||||
|
limit = 50
|
||||||
|
length = self.cbpi.config.get('brewfather_list_length',50)
|
||||||
|
repeat = True
|
||||||
brewfather = True
|
brewfather = True
|
||||||
result=[]
|
result=[]
|
||||||
self.url="https://api.brewfather.app/v1/recipes"
|
self.url="https://api.brewfather.app/v2/recipes"
|
||||||
brewfather_user_id = self.cbpi.config.get("brewfather_user_id", None)
|
brewfather_user_id = self.cbpi.config.get("brewfather_user_id", None)
|
||||||
if brewfather_user_id == "" or brewfather_user_id is None:
|
if brewfather_user_id == "" or brewfather_user_id is None:
|
||||||
brewfather = False
|
brewfather = False
|
||||||
|
@ -84,25 +86,63 @@ class UploadController:
|
||||||
if brewfather == True:
|
if brewfather == True:
|
||||||
encodedData = base64.b64encode(bytes(f"{brewfather_user_id}:{brewfather_api_key}", "ISO-8859-1")).decode("ascii")
|
encodedData = base64.b64encode(bytes(f"{brewfather_user_id}:{brewfather_api_key}", "ISO-8859-1")).decode("ascii")
|
||||||
headers={"Authorization": "Basic %s" % encodedData}
|
headers={"Authorization": "Basic %s" % encodedData}
|
||||||
parameters={"limit": 50, 'offset': offset}
|
parameters={"limit": limit}
|
||||||
async with aiohttp.ClientSession(headers=headers) as bf_session:
|
while repeat == True:
|
||||||
async with bf_session.get(self.url, params=parameters) as r:
|
try:
|
||||||
bf_recipe_list = await r.json()
|
async with aiohttp.ClientSession(headers=headers) as bf_session:
|
||||||
await bf_session.close()
|
async with bf_session.get(self.url, params=parameters) as r:
|
||||||
|
if r.status == 429:
|
||||||
if bf_recipe_list:
|
try:
|
||||||
for row in bf_recipe_list:
|
seconds=int(r.headers['Retry-After'])
|
||||||
recipe_id = row['_id']
|
minutes=round(seconds/60)
|
||||||
name = row['name']
|
except:
|
||||||
element = {'value': recipe_id, 'label': name}
|
seconds=None
|
||||||
result.append(element)
|
if not seconds:
|
||||||
return result
|
logging.error("Too many requests to BF api. Try again later")
|
||||||
else:
|
self.cbpi.notify("Error", "Too many requests to BF api. Try again later", NotificationType.ERROR)
|
||||||
return []
|
else:
|
||||||
|
logging.error(f"Too many requests to BF api. Try in {minutes} minutes again.")
|
||||||
else:
|
self.cbpi.notify("Error", f"Too many requests to BF api. Try in {minutes} minutes again.", NotificationType.ERROR)
|
||||||
return []
|
repeat = False
|
||||||
|
logging.error(r.headers['Retry-After'])
|
||||||
|
else:
|
||||||
|
bf_recipe_list = await r.json()
|
||||||
|
await bf_session.close()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
repeat = False
|
||||||
|
try:
|
||||||
|
if bf_recipe_list:
|
||||||
|
for row in bf_recipe_list:
|
||||||
|
recipe_id = row['_id']
|
||||||
|
name = row['name']
|
||||||
|
element = {'value': recipe_id, 'label': name}
|
||||||
|
result.append(element)
|
||||||
|
else:
|
||||||
|
repeat = False
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
try:
|
||||||
|
if len(bf_recipe_list) != limit:
|
||||||
|
repeat = False
|
||||||
|
else:
|
||||||
|
parameters={"limit": limit, 'start_after': recipe_id}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
newlist = sorted(result, key=lambda d: d['label'])
|
||||||
|
listlength=len(newlist)
|
||||||
|
max=math.floor(listlength/length)
|
||||||
|
sortlist=[]
|
||||||
|
for i in range(0 , max+1):
|
||||||
|
sortlist.append({ 'value': i*length, 'label': i*length })
|
||||||
|
return newlist, sortlist, length
|
||||||
|
except:
|
||||||
|
logging.error("Return empty BF recipe list")
|
||||||
|
sortlist=[{ 'value': 0, 'label': '0' }]
|
||||||
|
return result, sortlist, length
|
||||||
|
|
||||||
|
|
||||||
def get_creation_path(self):
|
def get_creation_path(self):
|
||||||
creation_path = self.cbpi.config.get("RECIPE_CREATION_PATH", "upload")
|
creation_path = self.cbpi.config.get("RECIPE_CREATION_PATH", "upload")
|
||||||
|
@ -738,7 +778,7 @@ class UploadController:
|
||||||
|
|
||||||
brewfather = True
|
brewfather = True
|
||||||
result=[]
|
result=[]
|
||||||
self.bf_url="https://api.brewfather.app/v1/recipes/" + Recipe_ID
|
self.bf_url="https://api.brewfather.app/v2/recipes/" + Recipe_ID
|
||||||
brewfather_user_id = self.cbpi.config.get("brewfather_user_id", None)
|
brewfather_user_id = self.cbpi.config.get("brewfather_user_id", None)
|
||||||
if brewfather_user_id == "" or brewfather_user_id is None:
|
if brewfather_user_id == "" or brewfather_user_id is None:
|
||||||
brewfather = False
|
brewfather = False
|
||||||
|
@ -775,6 +815,21 @@ class UploadController:
|
||||||
except:
|
except:
|
||||||
miscs = None
|
miscs = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
fermentation_steps=bf_recipe['fermentation']['steps']
|
||||||
|
except:
|
||||||
|
fermentation_steps=None
|
||||||
|
|
||||||
|
if fermentation_steps is not None:
|
||||||
|
try:
|
||||||
|
step=fermentation_steps[0]
|
||||||
|
self.fermentation_step_temp=int(step['stepTemp'])
|
||||||
|
except:
|
||||||
|
self.fermentation_step_temp=None
|
||||||
|
|
||||||
|
if self.fermentation_step_temp is not None and self.TEMP_UNIT != "C":
|
||||||
|
self.fermentation_step_temp = round((9.0 / 5.0 * float(self.fermentation_step_temp)+ 32))
|
||||||
|
|
||||||
FirstWort = self.getFirstWort(hops, "bf")
|
FirstWort = self.getFirstWort(hops, "bf")
|
||||||
|
|
||||||
await self.create_recipe(RecipeName)
|
await self.create_recipe(RecipeName)
|
||||||
|
@ -1012,8 +1067,9 @@ class UploadController:
|
||||||
cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None)
|
cooldown_sensor = self.cbpi.config.get("steps_cooldown_sensor", None)
|
||||||
if cooldown_sensor is None or cooldown_sensor == '':
|
if cooldown_sensor is None or cooldown_sensor == '':
|
||||||
cooldown_sensor = self.boilkettle.sensor # fall back to boilkettle sensor if no other sensor is specified
|
cooldown_sensor = self.boilkettle.sensor # fall back to boilkettle sensor if no other sensor is specified
|
||||||
step_timer = ""
|
step_timer = ""
|
||||||
step_temp = int(self.CoolDownTemp)
|
|
||||||
|
step_temp = int(self.CoolDownTemp) if (self.fermentation_step_temp is None or self.fermentation_step_temp <= int(self.CoolDownTemp)) else self.fermentation_step_temp
|
||||||
step_string = { "name": "Cooldown",
|
step_string = { "name": "Cooldown",
|
||||||
"props": {
|
"props": {
|
||||||
"Kettle": self.boilid,
|
"Kettle": self.boilid,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import json
|
||||||
from cbpi.api import *
|
from cbpi.api import *
|
||||||
from cbpi.api.config import ConfigType
|
from cbpi.api.config import ConfigType
|
||||||
from cbpi.api.base import CBPiBase
|
from cbpi.api.base import CBPiBase
|
||||||
import glob
|
import glob, yaml
|
||||||
from cbpi import __version__
|
from cbpi import __version__
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -19,6 +19,12 @@ class ConfigUpdate(CBPiExtension):
|
||||||
self.cbpi = cbpi
|
self.cbpi = cbpi
|
||||||
self._task = asyncio.create_task(self.run())
|
self._task = asyncio.create_task(self.run())
|
||||||
|
|
||||||
|
def append_to_yaml(self, file_path, data_to_append):
|
||||||
|
|
||||||
|
with open(file_path[0], 'a+') as file:
|
||||||
|
file.seek(0)
|
||||||
|
yaml.dump(data_to_append, file, default_flow_style=False)
|
||||||
|
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
logging.info("Check Config for required changes")
|
logging.info("Check Config for required changes")
|
||||||
|
@ -61,12 +67,13 @@ class ConfigUpdate(CBPiExtension):
|
||||||
AddMashIn = self.cbpi.config.get("AddMashInStep", None)
|
AddMashIn = self.cbpi.config.get("AddMashInStep", None)
|
||||||
bfuserid = self.cbpi.config.get("brewfather_user_id", None)
|
bfuserid = self.cbpi.config.get("brewfather_user_id", None)
|
||||||
bfapikey = self.cbpi.config.get("brewfather_api_key", None)
|
bfapikey = self.cbpi.config.get("brewfather_api_key", None)
|
||||||
|
bflistlength = self.cbpi.config.get("brewfather_list_length", None)
|
||||||
RecipeCreationPath = self.cbpi.config.get("RECIPE_CREATION_PATH", None)
|
RecipeCreationPath = self.cbpi.config.get("RECIPE_CREATION_PATH", None)
|
||||||
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)
|
current_grid = self.cbpi.config.get("current_grid", None)
|
||||||
|
mqtt_offset=self.cbpi.static_config.get("mqtt_offset", None)
|
||||||
|
|
||||||
if boil_temp is None:
|
if boil_temp is None:
|
||||||
logger.info("INIT Boil Temp Setting")
|
logger.info("INIT Boil Temp Setting")
|
||||||
|
@ -244,6 +251,21 @@ class ConfigUpdate(CBPiExtension):
|
||||||
await self.cbpi.config.add("brewfather_api_key", "", type=ConfigType.STRING, description="Brewfather API Key", source="craftbeerpi")
|
await self.cbpi.config.add("brewfather_api_key", "", type=ConfigType.STRING, description="Brewfather API Key", source="craftbeerpi")
|
||||||
except:
|
except:
|
||||||
logger.warning('Unable to update config')
|
logger.warning('Unable to update config')
|
||||||
|
|
||||||
|
## Check if Brewfather API Key is in config
|
||||||
|
|
||||||
|
if bflistlength is None:
|
||||||
|
logger.info("INIT Brewfather Recipe List Length")
|
||||||
|
try:
|
||||||
|
await self.cbpi.config.add("brewfather_list_length", 50, type=ConfigType.SELECT, description="Brewfather Recipe List length",
|
||||||
|
source="craftbeerpi",
|
||||||
|
options= [{"label": "5", "value": 5},
|
||||||
|
{"label": "10", "value": 10},
|
||||||
|
{"label": "25", "value": 25},
|
||||||
|
{"label": "50", "value": 50},
|
||||||
|
{"label": "100", "value": 100}])
|
||||||
|
except:
|
||||||
|
logger.warning('Unable to update config')
|
||||||
|
|
||||||
## Check if Brewfather API Key is in config
|
## Check if Brewfather API Key is in config
|
||||||
|
|
||||||
|
@ -542,6 +564,16 @@ class ConfigUpdate(CBPiExtension):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
|
|
||||||
|
if mqtt_offset is None:
|
||||||
|
logging.info("INIT MQTT Offset in static config")
|
||||||
|
try:
|
||||||
|
static_config_file=glob.glob(self.cbpi.config_folder.get_file_path('config.yaml'))
|
||||||
|
data_to_append = {'mqtt_offset': False}
|
||||||
|
self.append_to_yaml(static_config_file, data_to_append)
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
logging.warning('Unable to update database')
|
||||||
|
|
||||||
## Check if influxdbname is in config
|
## Check if influxdbname is in config
|
||||||
if CONFIG_STATUS is None or CONFIG_STATUS != self.version:
|
if CONFIG_STATUS is None or CONFIG_STATUS != self.version:
|
||||||
|
|
|
@ -536,7 +536,7 @@ class CooldownStep(CBPiStep):
|
||||||
if time.time() >= self.next_check:
|
if time.time() >= self.next_check:
|
||||||
self.next_check = time.time() + (self.Interval * 60)
|
self.next_check = time.time() + (self.Interval * 60)
|
||||||
|
|
||||||
cooldown_model = np.poly1d(np.polyfit(self.temp_array, self.time_array, 2))
|
cooldown_model = np.polynomial.polynomial.Polynomial.fit(self.temp_array, self.time_array, 2)
|
||||||
target_time=cooldown_model(self.target_temp)
|
target_time=cooldown_model(self.target_temp)
|
||||||
target_timestring= datetime.fromtimestamp(target_time)
|
target_timestring= datetime.fromtimestamp(target_time)
|
||||||
self.summary="ECT: {}".format(target_timestring.strftime("%H:%M"))
|
self.summary="ECT: {}".format(target_timestring.strftime("%H:%M"))
|
||||||
|
|
|
@ -8,16 +8,17 @@ import json
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
@parameters([Property.Text(label="Topic", configurable=True, description="MQTT Topic"),
|
@parameters([Property.Text(label="Topic", configurable=True, description="MQTT Topic"),
|
||||||
Property.Text(label="PayloadDictionary", configurable=True, default_value="",
|
Property.Text(label="PayloadDictionary", configurable=True, default_value="",
|
||||||
description="Where to find msg in payload, leave blank for raw payload"),
|
description="Where to find msg in payload, leave blank for raw payload"),
|
||||||
Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"),
|
Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"),
|
||||||
Property.Fermenter(label="Fermenter", description="Reduced logging if Fermenter is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"),
|
Property.Fermenter(label="Fermenter", description="Reduced logging if Fermenter is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"),
|
||||||
Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"),
|
Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"),
|
||||||
Property.Number(label="Timeout", configurable=True, unit="sec",
|
Property.Number(label="Timeout", configurable=True, unit="sec",
|
||||||
description="Timeout in seconds to send notification (default:60 | deactivated: 0)"),
|
description="Timeout in seconds to send notification (default:60 | deactivated: 0)"),
|
||||||
Property.Number(label="TempRange", configurable=True, unit="degree",
|
Property.Number(label="TempRange", configurable=True, unit="degree",
|
||||||
description="Temp range in degree between reading and target temp of fermenter/kettle. Larger difference shows different color in dashboard (default:0 | deactivated: 0)")])
|
description="Temp range in degree between reading and target temp of fermenter/kettle. Larger difference shows different color in dashboard (default:0 | deactivated: 0)")])
|
||||||
class MQTTSensor(CBPiSensor):
|
class MQTTSensor(CBPiSensor):
|
||||||
|
|
||||||
def __init__(self, cbpi, id, props):
|
def __init__(self, cbpi, id, props):
|
||||||
|
@ -135,6 +136,135 @@ class MQTTSensor(CBPiSensor):
|
||||||
async def on_stop(self):
|
async def on_stop(self):
|
||||||
self.subscribed = self.cbpi.satellite.unsubscribe(self.Topic, self.on_message)
|
self.subscribed = self.cbpi.satellite.unsubscribe(self.Topic, self.on_message)
|
||||||
|
|
||||||
|
@parameters([Property.Text(label="Topic", configurable=True, description="MQTT Topic"),
|
||||||
|
Property.Text(label="PayloadDictionary", configurable=True, default_value="",
|
||||||
|
description="Where to find msg in payload, leave blank for raw payload"),
|
||||||
|
Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"),
|
||||||
|
Property.Fermenter(label="Fermenter", description="Reduced logging if Fermenter is inactive / range warning in dashboard (only Kettle or Fermenter to be selected)"),
|
||||||
|
Property.Number(label="Offset", configurable=True, description="Offset for MQTT Sensor (default is 0). !!! Use this only with caution as offset for MQTT sensor should be defined on Sensor side !!!"),
|
||||||
|
Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"),
|
||||||
|
Property.Number(label="Timeout", configurable=True, unit="sec",
|
||||||
|
description="Timeout in seconds to send notification (default:60 | deactivated: 0)"),
|
||||||
|
Property.Number(label="TempRange", configurable=True, unit="degree",
|
||||||
|
description="Temp range in degree between reading and target temp of fermenter/kettle. Larger difference shows different color in dashboard (default:0 | deactivated: 0)")])
|
||||||
|
class MQTTSensorOffset(CBPiSensor):
|
||||||
|
|
||||||
|
def __init__(self, cbpi, id, props):
|
||||||
|
super(MQTTSensorOffset, self).__init__(cbpi, id, props)
|
||||||
|
self.Topic = self.props.get("Topic", None)
|
||||||
|
self.offset = float(self.props.get("Offset", 0))
|
||||||
|
self.payload_text = self.props.get("PayloadDictionary", None)
|
||||||
|
if self.payload_text != None:
|
||||||
|
self.payload_text = self.payload_text.split('.')
|
||||||
|
self.subscribed = self.cbpi.satellite.subscribe(self.Topic, self.on_message)
|
||||||
|
self.value: float = 999
|
||||||
|
self.timeout=int(self.props.get("Timeout", 60))
|
||||||
|
self.temprange=float(self.props.get("TempRange", 0))
|
||||||
|
self.starttime = time.time()
|
||||||
|
self.notificationsend = False
|
||||||
|
self.nextchecktime=self.starttime+self.timeout
|
||||||
|
self.lastdata=time.time()
|
||||||
|
self.lastlog=0
|
||||||
|
self.sensor=self.get_sensor(self.id)
|
||||||
|
self.reducedfrequency=int(self.props.get("ReducedLogging", 60))
|
||||||
|
if self.reducedfrequency < 0:
|
||||||
|
self.reducedfrequency = 0
|
||||||
|
self.kettleid=self.props.get("Kettle", None)
|
||||||
|
self.fermenterid=self.props.get("Fermenter", None)
|
||||||
|
self.reducedlogging = True if self.kettleid or self.fermenterid else False
|
||||||
|
|
||||||
|
if self.kettleid is not None and self.fermenterid is not None:
|
||||||
|
self.reducedlogging=False
|
||||||
|
self.cbpi.notify("MQTTSensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging / range warning.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)])
|
||||||
|
|
||||||
|
async def Confirm(self, **kwargs):
|
||||||
|
self.nextchecktime = time.time() + self.timeout
|
||||||
|
self.notificationsend = False
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def message(self):
|
||||||
|
target_timestring= datetime.fromtimestamp(self.lastdata)
|
||||||
|
self.cbpi.notify("MQTTSensor Timeout", "Sensor '" + str(self.sensor.name) + "' did not respond. Last data received: "+target_timestring.strftime("%D %H:%M"), NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)])
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def on_message(self, message):
|
||||||
|
val = json.loads(message.payload.decode())
|
||||||
|
try:
|
||||||
|
if self.payload_text is not None:
|
||||||
|
for key in self.payload_text:
|
||||||
|
val = val.get(key, None)
|
||||||
|
|
||||||
|
if isinstance(val, (int, float, str)):
|
||||||
|
self.value = float(val)+self.offset
|
||||||
|
self.push_update(self.value)
|
||||||
|
if self.reducedlogging == True:
|
||||||
|
await self.logvalue()
|
||||||
|
else:
|
||||||
|
logging.info("MQTTSensor {} regular logging".format(self.sensor.name))
|
||||||
|
self.log_data(self.value)
|
||||||
|
self.lastlog = time.time()
|
||||||
|
|
||||||
|
if self.timeout !=0:
|
||||||
|
self.nextchecktime = time.time() + self.timeout
|
||||||
|
self.notificationsend = False
|
||||||
|
self.lastdata=time.time()
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("MQTT Sensor Error {}".format(e))
|
||||||
|
|
||||||
|
async def logvalue(self):
|
||||||
|
self.kettle = self.get_kettle(self.kettleid) if self.kettleid is not None else None
|
||||||
|
self.fermenter = self.get_fermenter(self.fermenterid) if self.fermenterid is not None else None
|
||||||
|
now=time.time()
|
||||||
|
if self.kettle is not None:
|
||||||
|
try:
|
||||||
|
kettlestatus=self.kettle.instance.state
|
||||||
|
except:
|
||||||
|
kettlestatus=False
|
||||||
|
if kettlestatus:
|
||||||
|
self.log_data(self.value)
|
||||||
|
logging.info("MQTTSensor {} Kettle Active".format(self.sensor.name))
|
||||||
|
self.lastlog = time.time()
|
||||||
|
else:
|
||||||
|
logging.info("MQTTSensor {} Kettle Inactive".format(self.sensor.name))
|
||||||
|
if self.reducedfrequency != 0:
|
||||||
|
if now >= self.lastlog + self.reducedfrequency:
|
||||||
|
self.log_data(self.value)
|
||||||
|
self.lastlog = time.time()
|
||||||
|
logging.info("Logged with reduced freqency")
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.fermenter is not None:
|
||||||
|
try:
|
||||||
|
fermenterstatus=self.fermenter.instance.state
|
||||||
|
except:
|
||||||
|
fermenterstatus=False
|
||||||
|
if fermenterstatus:
|
||||||
|
self.log_data(self.value)
|
||||||
|
logging.info("MQTTSensor {} Fermenter Active".format(self.sensor.name))
|
||||||
|
self.lastlog = time.time()
|
||||||
|
else:
|
||||||
|
logging.info("MQTTSensor {} Fermenter Inactive".format(self.sensor.name))
|
||||||
|
if self.reducedfrequency != 0:
|
||||||
|
if now >= self.lastlog + self.reducedfrequency:
|
||||||
|
self.log_data(self.value)
|
||||||
|
self.lastlog = time.time()
|
||||||
|
logging.info("Logged with reduced freqency")
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
while self.running:
|
||||||
|
if self.timeout !=0:
|
||||||
|
if time.time() > self.nextchecktime and self.notificationsend == False:
|
||||||
|
await self.message()
|
||||||
|
self.notificationsend=True
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
return dict(value=self.value)
|
||||||
|
|
||||||
|
async def on_stop(self):
|
||||||
|
self.subscribed = self.cbpi.satellite.unsubscribe(self.Topic, self.on_message)
|
||||||
|
|
||||||
|
|
||||||
def setup(cbpi):
|
def setup(cbpi):
|
||||||
'''
|
'''
|
||||||
|
@ -145,4 +275,7 @@ def setup(cbpi):
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
if str(cbpi.static_config.get("mqtt", False)).lower() == "true":
|
if str(cbpi.static_config.get("mqtt", False)).lower() == "true":
|
||||||
cbpi.plugin.register("MQTTSensor", MQTTSensor)
|
if str(cbpi.static_config.get("mqtt_offset", False)).lower() == "false":
|
||||||
|
cbpi.plugin.register("MQTTSensor", MQTTSensor)
|
||||||
|
else:
|
||||||
|
cbpi.plugin.register("MQTTSensor", MQTTSensorOffset)
|
||||||
|
|
|
@ -43,6 +43,7 @@ class SystemHttpEndpoints:
|
||||||
fermentersteps=self.cbpi.fermenter.get_fermenter_steps(),
|
fermentersteps=self.cbpi.fermenter.get_fermenter_steps(),
|
||||||
config=self.cbpi.config.get_state(),
|
config=self.cbpi.config.get_state(),
|
||||||
notifications=self.cbpi.notification.get_state(),
|
notifications=self.cbpi.notification.get_state(),
|
||||||
|
bf_recipes=await self.cbpi.upload.get_brewfather_recipes(0),
|
||||||
version=__version__,
|
version=__version__,
|
||||||
guiversion=version,
|
guiversion=version,
|
||||||
codename=__codename__)
|
codename=__codename__)
|
||||||
|
|
|
@ -159,6 +159,23 @@ class UploadHttpEndpoints():
|
||||||
|
|
||||||
return web.json_response(bf_list)
|
return web.json_response(bf_list)
|
||||||
|
|
||||||
|
@request_mapping(path='/bfupdate/', method="GET", auth_required=False)
|
||||||
|
async def get_bf_update(self, request):
|
||||||
|
"""
|
||||||
|
|
||||||
|
---
|
||||||
|
description: Get recipe list update from Brewfather App
|
||||||
|
tags:
|
||||||
|
- Upload
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
#offset = request.match_info['offset']
|
||||||
|
bf_list = await self.controller.get_brewfather_recipes()
|
||||||
|
self.cbpi.ws.send(dict(topic="bfupdate", data=bf_list))
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
@request_mapping(path='/bf', method="POST", auth_required=False)
|
@request_mapping(path='/bf', method="POST", auth_required=False)
|
||||||
async def create_bf_recipe(self, request):
|
async def create_bf_recipe(self, request):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
typing-extensions>=4
|
typing-extensions>=4
|
||||||
aiohttp==3.9.4
|
aiohttp==3.9.5
|
||||||
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.5.0
|
aiohttp-security==0.5.0
|
||||||
|
@ -8,7 +8,7 @@ aiohttp-swagger==1.0.16
|
||||||
async-timeout==4.0.3
|
async-timeout==4.0.3
|
||||||
aiojobs==1.2.1
|
aiojobs==1.2.1
|
||||||
aiosqlite==0.17.0
|
aiosqlite==0.17.0
|
||||||
cryptography==42.0.5
|
cryptography==42.0.8
|
||||||
pyopenssl==24.1.0
|
pyopenssl==24.1.0
|
||||||
requests==2.32.2
|
requests==2.32.2
|
||||||
voluptuous==0.14.2
|
voluptuous==0.14.2
|
||||||
|
@ -16,12 +16,12 @@ pyfiglet==1.0.2
|
||||||
pandas==2.2.2
|
pandas==2.2.2
|
||||||
shortuuid==1.0.13
|
shortuuid==1.0.13
|
||||||
tabulate==0.9.0
|
tabulate==0.9.0
|
||||||
numpy==1.26.4
|
numpy==2.0.0
|
||||||
cbpi4gui
|
cbpi4gui
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
importlib_metadata==4.11.1
|
importlib_metadata==4.11.1
|
||||||
aiomqtt==2.1.0
|
aiomqtt==2.2.0
|
||||||
psutil==5.9.8
|
psutil==6.0.0
|
||||||
zipp>=0.5
|
zipp>=0.5
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
pytest-aiohttp
|
pytest-aiohttp
|
||||||
|
|
10
setup.py
10
setup.py
|
@ -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.9.4",
|
"aiohttp==3.9.5",
|
||||||
"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.5.0",
|
"aiohttp-security==0.5.0",
|
||||||
|
@ -48,7 +48,7 @@ setup(name='cbpi4',
|
||||||
"async-timeout==4.0.3",
|
"async-timeout==4.0.3",
|
||||||
"aiojobs==1.2.1 ",
|
"aiojobs==1.2.1 ",
|
||||||
"aiosqlite==0.17.0",
|
"aiosqlite==0.17.0",
|
||||||
"cryptography==42.0.5",
|
"cryptography==42.0.8",
|
||||||
"pyopenssl==24.1.0",
|
"pyopenssl==24.1.0",
|
||||||
"requests==2.32.2",
|
"requests==2.32.2",
|
||||||
"voluptuous==0.14.2",
|
"voluptuous==0.14.2",
|
||||||
|
@ -56,13 +56,13 @@ setup(name='cbpi4',
|
||||||
'click==8.1.7',
|
'click==8.1.7',
|
||||||
'shortuuid==1.0.13',
|
'shortuuid==1.0.13',
|
||||||
'tabulate==0.9.0',
|
'tabulate==0.9.0',
|
||||||
'aiomqtt==2.1.0',
|
'aiomqtt==2.2.0',
|
||||||
'inquirer==3.2.4',
|
'inquirer==3.2.4',
|
||||||
'colorama==0.4.6',
|
'colorama==0.4.6',
|
||||||
'psutil==5.9.8',
|
'psutil==6.0.0',
|
||||||
'cbpi4gui',
|
'cbpi4gui',
|
||||||
'importlib_metadata',
|
'importlib_metadata',
|
||||||
'numpy==1.26.4',
|
'numpy==2.0.0',
|
||||||
'pandas==2.2.2'] + (
|
'pandas==2.2.2'] + (
|
||||||
['rpi-lgpio'] if raspberrypi else [] ) + (
|
['rpi-lgpio'] if raspberrypi else [] ) + (
|
||||||
['systemd-python'] if localsystem == "Linux" else [] ),
|
['systemd-python'] if localsystem == "Linux" else [] ),
|
||||||
|
|
Loading…
Reference in a new issue