Merge pull request #106 from PiBrewing/development

Development
This commit is contained in:
Alexander Vollkopf 2023-03-30 16:11:25 +02:00 committed by GitHub
commit da72d61612
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 263 additions and 360 deletions

3
.gitignore vendored
View file

@ -4,8 +4,7 @@ build
dist dist
.idea .idea
*.log *.log
cbpi.egg-info *egg-info/
cbpi4.egg-info
log log
venv venv
cbpi/extension/ui cbpi/extension/ui

View file

@ -1,3 +1,3 @@
__version__ = "4.1.6" __version__ = "4.1.7"
__codename__ = "Groundhog Day" __codename__ = "Groundhog Day"

View file

@ -7,7 +7,7 @@ from time import strftime, localtime
import pandas as pd import pandas as pd
import zipfile import zipfile
import base64 import base64
import urllib3 from urllib3 import Timeout, PoolManager
from pathlib import Path from pathlib import Path
from cbpi.api import * from cbpi.api import *
from cbpi.api.config import ConfigType from cbpi.api.config import ConfigType
@ -46,43 +46,44 @@ class LogController:
formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime()) formatted_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
self.datalogger[name].info("%s,%s" % (formatted_time, str(value))) self.datalogger[name].info("%s,%s" % (formatted_time, str(value)))
if self.influxdb == "Yes": if self.influxdb == "Yes":
## Write to influxdb in an asyncio task
self._task = asyncio.create_task(self.log_influx(name,value))
async def log_influx(self, name:str, value:str):
self.influxdbcloud = self.cbpi.config.get("INFLUXDBCLOUD", "No") self.influxdbcloud = self.cbpi.config.get("INFLUXDBCLOUD", "No")
self.influxdbaddr = self.cbpi.config.get("INFLUXDBADDR", None) self.influxdbaddr = self.cbpi.config.get("INFLUXDBADDR", None)
self.influxdbport = self.cbpi.config.get("INFLUXDBPORT", None)
self.influxdbname = self.cbpi.config.get("INFLUXDBNAME", None) self.influxdbname = self.cbpi.config.get("INFLUXDBNAME", None)
self.influxdbuser = self.cbpi.config.get("INFLUXDBUSER", None) self.influxdbuser = self.cbpi.config.get("INFLUXDBUSER", None)
self.influxdbpwd = self.cbpi.config.get("INFLUXDBPWD", None) self.influxdbpwd = self.cbpi.config.get("INFLUXDBPWD", None)
self.influxdbmeasurement = self.cbpi.config.get("INFLUXDBMEASUREMENT", "measurement") self.influxdbmeasurement = self.cbpi.config.get("INFLUXDBMEASUREMENT", "measurement")
id = name id = name
timeout = Timeout(connect=5.0, read=None)
try: try:
chars = {'ö':'oe','ä':'ae','ü':'ue','Ö':'Oe','Ä':'Ae','Ü':'Ue'}
sensor=self.cbpi.sensor.find_by_id(name) sensor=self.cbpi.sensor.find_by_id(name)
if sensor is not None: if sensor is not None:
itemname=sensor.name.replace(" ", "_") itemname=sensor.name.replace(" ", "_")
for char in chars:
itemname = itemname.replace(char,chars[char])
out=str(self.influxdbmeasurement)+",source=" + itemname + ",itemID=" + str(id) + " value="+str(value) out=str(self.influxdbmeasurement)+",source=" + itemname + ",itemID=" + str(id) + " value="+str(value)
except Exception as e: except Exception as e:
logging.error("InfluxDB ID Error: {}".format(e)) logging.error("InfluxDB ID Error: {}".format(e))
if self.influxdbcloud == "Yes": if self.influxdbcloud == "Yes":
self.influxdburl="https://" + self.influxdbaddr + "/api/v2/write?org=" + self.influxdbuser + "&bucket=" + self.influxdbname + "&precision=s" self.influxdburl=self.influxdbaddr + "/api/v2/write?org=" + self.influxdbuser + "&bucket=" + self.influxdbname + "&precision=s"
try: try:
header = {'User-Agent': name, 'Authorization': "Token {}".format(self.influxdbpwd)} header = {'User-Agent': name, 'Authorization': "Token {}".format(self.influxdbpwd)}
http = urllib3.PoolManager() http = PoolManager(timeout=timeout)
req = http.request('POST',self.influxdburl, body=out, headers = header) req = http.request('POST',self.influxdburl, body=out.encode(), headers = header)
except Exception as e: except Exception as e:
logging.error("InfluxDB cloud write Error: {}".format(e)) logging.error("InfluxDB cloud write Error: {}".format(e))
else: else:
self.base64string = base64.b64encode(('%s:%s' % (self.influxdbuser,self.influxdbpwd)).encode()) self.base64string = base64.b64encode(('%s:%s' % (self.influxdbuser,self.influxdbpwd)).encode())
self.influxdburl='http://' + self.influxdbaddr + ':' + str(self.influxdbport) + '/write?db=' + self.influxdbname self.influxdburl= self.influxdbaddr + '/write?db=' + self.influxdbname
try: try:
header = {'User-Agent': name, 'Content-Type': 'application/x-www-form-urlencoded','Authorization': 'Basic %s' % self.base64string.decode('utf-8')} header = {'User-Agent': name, 'Content-Type': 'application/x-www-form-urlencoded','Authorization': 'Basic %s' % self.base64string.decode('utf-8')}
http = urllib3.PoolManager() http = PoolManager(timeout=timeout)
req = http.request('POST',self.influxdburl, body=out, headers = header) req = http.request('POST',self.influxdburl, body=out.encode(), headers = header)
except Exception as e: except Exception as e:
logging.error("InfluxDB write Error: {}".format(e)) logging.error("InfluxDB write Error: {}".format(e))

View file

@ -70,6 +70,10 @@ class SatelliteController:
method = topic_filter[1] method = topic_filter[1]
if message.topic.matches(topic): if message.topic.matches(topic):
await (method(message)) await (method(message))
except asyncio.CancelledError:
# Cancel
self.logger.warning("MQTT Listening Cancelled")
#break
except MqttError as e: except MqttError as e:
self.logger.error("MQTT Exception: {}".format(e)) self.logger.error("MQTT Exception: {}".format(e))
except Exception as e: except Exception as e:
@ -153,6 +157,7 @@ class SatelliteController:
return task return task
async def _subcribe(self, topic, method): async def _subcribe(self, topic, method):
self.error=False
while True: while True:
try: try:
if self.client._connected.done(): if self.client._connected.done():
@ -163,11 +168,14 @@ class SatelliteController:
await method(message.payload.decode()) await method(message.payload.decode())
except asyncio.CancelledError: except asyncio.CancelledError:
# Cancel # Cancel
self.logger.warning("Sub Cancelled") self.logger.warning("Subscription {} Cancelled".format(topic))
self.error=True
except MqttError as e: except MqttError as e:
self.logger.error("Sub MQTT Exception: {}".format(e)) self.logger.error("Sub MQTT Exception: {}".format(e))
except Exception as e: except Exception as e:
self.logger.error("Sub Exception: {}".format(e)) self.logger.error("Sub Exception: {}".format(e))
# wait before try to resubscribe # wait before try to resubscribe
if self.error == True:
break
else:
await asyncio.sleep(5) await asyncio.sleep(5)

View file

@ -40,7 +40,7 @@ class ConfigUpdate(CBPiExtension):
logfiles = self.cbpi.config.get("CSVLOGFILES", None) logfiles = self.cbpi.config.get("CSVLOGFILES", None)
influxdb = self.cbpi.config.get("INFLUXDB", None) influxdb = self.cbpi.config.get("INFLUXDB", None)
influxdbaddr = self.cbpi.config.get("INFLUXDBADDR", None) influxdbaddr = self.cbpi.config.get("INFLUXDBADDR", None)
influxdbport = self.cbpi.config.get("INFLUXDBPORT", None) #influxdbport = self.cbpi.config.get("INFLUXDBPORT", None)
influxdbname = self.cbpi.config.get("INFLUXDBNAME", None) influxdbname = self.cbpi.config.get("INFLUXDBNAME", None)
influxdbuser = self.cbpi.config.get("INFLUXDBUSER", None) influxdbuser = self.cbpi.config.get("INFLUXDBUSER", None)
influxdbpwd = self.cbpi.config.get("INFLUXDBPWD", None) influxdbpwd = self.cbpi.config.get("INFLUXDBPWD", None)
@ -52,6 +52,7 @@ class ConfigUpdate(CBPiExtension):
SENSOR_LOG_MAX_BYTES = self.cbpi.config.get("SENSOR_LOG_MAX_BYTES", None) SENSOR_LOG_MAX_BYTES = self.cbpi.config.get("SENSOR_LOG_MAX_BYTES", None)
slow_pipe_animation = self.cbpi.config.get("slow_pipe_animation", None) slow_pipe_animation = self.cbpi.config.get("slow_pipe_animation", None)
NOTIFY_ON_ERROR = self.cbpi.config.get("NOTIFY_ON_ERROR", None) NOTIFY_ON_ERROR = self.cbpi.config.get("NOTIFY_ON_ERROR", None)
PLAY_BUZZER = self.cbpi.config.get("PLAY_BUZZER", None)
BoilAutoTimer = self.cbpi.config.get("BoilAutoTimer", None) BoilAutoTimer = self.cbpi.config.get("BoilAutoTimer", None)
@ -224,15 +225,7 @@ class ConfigUpdate(CBPiExtension):
if influxdbaddr is None: if influxdbaddr is None:
logger.info("INIT Influxdbaddr") logger.info("INIT Influxdbaddr")
try: try:
await self.cbpi.config.add("INFLUXDBADDR", "localhost", ConfigType.STRING, "IP Address of your influxdb server (If INFLUXDBCLOUD set to Yes use URL Address of your influxdb cloud server)") await self.cbpi.config.add("INFLUXDBADDR", "http://localhost:8086", ConfigType.STRING, "URL Address of your influxdb server (If INFLUXDBCLOUD set to Yes use URL Address of your influxdb cloud server)")
except:
logger.warning('Unable to update config')
## Check if influxdbport is in config
if influxdbport is None:
logger.info("INIT Influxdbport")
try:
await self.cbpi.config.add("INFLUXDBPORT", "8086", ConfigType.STRING, "Port of your influxdb server")
except: except:
logger.warning('Unable to update config') logger.warning('Unable to update config')
@ -336,6 +329,16 @@ class ConfigUpdate(CBPiExtension):
except: except:
logger.warning('Unable to update config') logger.warning('Unable to update config')
## Check if PLAY_BUZZER is in config
if PLAY_BUZZER is None:
logger.info("INIT PLAY_BUZZER")
try:
await self.cbpi.config.add("PLAY_BUZZER", "No", ConfigType.SELECT, "Play buzzer sound in Web interface on Notifications",
[{"label": "Yes", "value": "Yes"},
{"label": "No", "value": "No"}])
except:
logger.warning('Unable to update config')
if BoilAutoTimer is None: if BoilAutoTimer is None:
logging.info("INIT BoilAutoTimer") logging.info("INIT BoilAutoTimer")
try: try:

View file

@ -11,8 +11,11 @@ from cbpi.api.dataclasses import NotificationAction, NotificationType
cache = {} cache = {}
@parameters([Property.Text(label="Key", configurable=True, description="Http Key"), @parameters([Property.Text(label="Key", configurable=True, description="Http Key"),
Property.Number(label="Timeout", configurable="True",unit="sec",description="Timeout in seconds to send notification (default:60 | deactivated: 0)") Property.Number(label="Timeout", configurable="True",unit="sec",description="Timeout in seconds to send notification (default:60 | deactivated: 0)"),
]) Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive (only Kettle or Fermenter to be selected)"),
Property.Fermenter(label="Fermenter", description="Reduced logging in seconds if Fermenter is inactive (only Kettle or Fermenter to be selected)"),
Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default: 60 sec | disabled: 0)")])
class HTTPSensor(CBPiSensor): class HTTPSensor(CBPiSensor):
def __init__(self, cbpi, id, props): def __init__(self, cbpi, id, props):
super(HTTPSensor, self).__init__(cbpi, id, props) super(HTTPSensor, self).__init__(cbpi, id, props)
@ -25,6 +28,19 @@ class HTTPSensor(CBPiSensor):
self.sensor=self.get_sensor(self.id) self.sensor=self.get_sensor(self.id)
self.lastdata=time.time() self.lastdata=time.time()
self.lastlog=0
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("HTTPSensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)])
async def Confirm(self, **kwargs): async def Confirm(self, **kwargs):
self.nextchecktime = time.time() + self.timeout self.nextchecktime = time.time() + self.timeout
self.notificationsend = False self.notificationsend = False
@ -41,6 +57,8 @@ class HTTPSensor(CBPiSensor):
In this example the code is executed every second In this example the code is executed every second
''' '''
while self.running is True: while self.running is True:
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
if self.timeout !=0: if self.timeout !=0:
currenttime=time.time() currenttime=time.time()
if currenttime > self.nextchecktime and self.notificationsend == False: if currenttime > self.nextchecktime and self.notificationsend == False:
@ -51,6 +69,13 @@ class HTTPSensor(CBPiSensor):
if cache_value is not None: if cache_value is not None:
self.value = float(cache_value) self.value = float(cache_value)
self.push_update(self.value) self.push_update(self.value)
if self.reducedlogging:
await self.logvalue()
else:
self.log_data(self.value)
self.lastlog = time.time()
if self.timeout !=0: if self.timeout !=0:
self.nextchecktime = currenttime + self.timeout self.nextchecktime = currenttime + self.timeout
self.notificationsend = False self.notificationsend = False
@ -60,13 +85,50 @@ class HTTPSensor(CBPiSensor):
pass pass
await asyncio.sleep(1) await asyncio.sleep(1)
async def logvalue(self):
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("Kettle Active")
self.lastlog = time.time()
else:
logging.info("Kettle Inactive")
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("Fermenter Active")
self.lastlog = time.time()
else:
logging.info("Fermenter Inactive")
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
def get_state(self): def get_state(self):
# return the current state of the sensor # return the current state of the sensor
return dict(value=self.value) return dict(value=self.value)
class HTTPSensorEndpoint(CBPiExtension): class HTTPSensorEndpoint(CBPiExtension):
def __init__(self, cbpi): def __init__(self, cbpi):
''' '''
Initializer Initializer

View file

@ -11,7 +11,10 @@ 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.Number(label="Timeout", configurable="True",unit="sec", Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive (only Kettle or Fermenter to be selected)"),
Property.Fermenter(label="Fermenter", description="Reduced logging in seconds if Fermenter is inactive (only Kettle or Fermenter to be selected)"),
Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default:60 sec | 0 disabled)"),
Property.Number(label="Timeout", configurable=True, unit="sec",
description="Timeout in seconds to send notification (default:60 | deactivated: 0)")]) description="Timeout in seconds to send notification (default:60 | deactivated: 0)")])
class MQTTSensor(CBPiSensor): class MQTTSensor(CBPiSensor):
@ -28,7 +31,18 @@ class MQTTSensor(CBPiSensor):
self.notificationsend = False self.notificationsend = False
self.nextchecktime=self.starttime+self.timeout self.nextchecktime=self.starttime+self.timeout
self.lastdata=time.time() self.lastdata=time.time()
self.lastlog=0
self.sensor=self.get_sensor(self.id) 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.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)])
async def Confirm(self, **kwargs): async def Confirm(self, **kwargs):
self.nextchecktime = time.time() + self.timeout self.nextchecktime = time.time() + self.timeout
@ -49,14 +63,60 @@ class MQTTSensor(CBPiSensor):
if isinstance(val, (int, float, str)): if isinstance(val, (int, float, str)):
self.value = float(val) self.value = float(val)
self.log_data(self.value)
self.push_update(self.value) 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: if self.timeout !=0:
self.nextchecktime = time.time() + self.timeout self.nextchecktime = time.time() + self.timeout
self.notificationsend = False self.notificationsend = False
self.lastdata=time.time() self.lastdata=time.time()
except Exception as e: except Exception as e:
logging.info("MQTT Sensor Error {}".format(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): async def run(self):
while self.running: while self.running:
@ -70,13 +130,17 @@ class MQTTSensor(CBPiSensor):
return dict(value=self.value) return dict(value=self.value)
async def on_stop(self): async def on_stop(self):
if self.mqtt_task.done() is False: was_cancelled=False
self.mqtt_task.cancel() if not self.mqtt_task.done():
logging.info("Task not done -> cancelling")
was_cancelled = self.mqtt_task.cancel()
try: try:
logging.info("Trying to call cancelled task")
await self.mqtt_task await self.mqtt_task
except asyncio.CancelledError: except asyncio.CancelledError:
logging.info("Task has been Cancelled")
pass pass
logging.info("Task cancelled: {}".format(was_cancelled))
def setup(cbpi): def setup(cbpi):
''' '''

View file

@ -1,13 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio import asyncio
import random import logging
import re
import random
from aiohttp import web from aiohttp import web
from cbpi.api import * from cbpi.api import *
import os, re, threading, time import os, threading, time
from subprocess import call from subprocess import call
import random from cbpi.api.dataclasses import NotificationAction, NotificationType
def getSensors(): def getSensors():
try: try:
@ -53,7 +51,11 @@ class ReadThread (threading.Thread):
@parameters([Property.Select(label="Sensor", options=getSensors()), @parameters([Property.Select(label="Sensor", options=getSensors()),
Property.Number(label="offset",configurable = True, default_value = 0, description="Sensor Offset (Default is 0)"), Property.Number(label="offset",configurable = True, default_value = 0, description="Sensor Offset (Default is 0)"),
Property.Select(label="Interval", options=[1,5,10,30,60], description="Interval in Seconds")]) Property.Select(label="Interval", options=[1,5,10,30,60], description="Interval in Seconds"),
Property.Kettle(label="Kettle", description="Reduced logging if Kettle is inactive (only Kettle or Fermenter to be selected)"),
Property.Fermenter(label="Fermenter", description="Reduced logging in seconds if Fermenter is inactive (only Kettle or Fermenter to be selected)"),
Property.Number(label="ReducedLogging", configurable=True, description="Reduced logging frequency in seconds if selected Kettle or Fermenter is inactive (default: 60 sec | disabled: 0)")
])
class OneWire(CBPiSensor): class OneWire(CBPiSensor):
def __init__(self, cbpi, id, props): def __init__(self, cbpi, id, props):
@ -63,16 +65,36 @@ class OneWire(CBPiSensor):
async def start(self): async def start(self):
await super().start() await super().start()
self.name = self.props.get("Sensor") self.name = self.props.get("Sensor")
self.interval = self.props.get("Interval", 60) self.interval = int(self.props.get("Interval", 60))
self.offset = float(self.props.get("offset",0)) self.offset = float(self.props.get("offset",0))
self.reducedfrequency=float(self.props.get("ReducedLogging", 60))
if self.reducedfrequency < 0:
self.reducedfrequency = 0
self.lastlog=0
self.sensor=self.get_sensor(self.id)
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("OneWire Sensor", "Sensor '" + str(self.sensor.name) + "' cant't have Fermenter and Kettle defined for reduced logging.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)])
if (self.reducedfrequency != 0) and (self.interval >= self.reducedfrequency):
self.reducedlogging=False
self.cbpi.notify("OneWire Sensor", "Sensor '" + str(self.sensor.name) + "' has shorter or equal 'reduced logging' compared to regular interval.", NotificationType.WARNING, action=[NotificationAction("OK", self.Confirm)])
self.t = ReadThread(self.name) self.t = ReadThread(self.name)
self.t.daemon = True self.t.daemon = True
def shudown(): def shutdown():
shudown.cb.shutdown() shutdown.cb.shutdown()
shudown.cb = self.t shutdown.cb = self.t
self.t.start() self.t.start()
async def Confirm(self, **kwargs):
pass
async def stop(self): async def stop(self):
try: try:
self.t.stop() self.t.stop()
@ -81,17 +103,65 @@ class OneWire(CBPiSensor):
pass pass
async def run(self): async def run(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
while self.running == True: while self.running == True:
self.TEMP_UNIT=self.get_config_value("TEMP_UNIT", "C") self.TEMP_UNIT=self.get_config_value("TEMP_UNIT", "C")
if self.TEMP_UNIT == "C": # Report temp in C if nothing else is selected in settings if self.TEMP_UNIT == "C": # Report temp in C if nothing else is selected in settings
self.value = round((self.t.value + self.offset),2) self.value = round((self.t.value + self.offset),2)
else: # Report temp in F if unit selected in settings else: # Report temp in F if unit selected in settings
self.value = round((9.0 / 5.0 * self.t.value + 32 + self.offset), 2) self.value = round((9.0 / 5.0 * self.t.value + 32 + self.offset), 2)
self.log_data(self.value)
self.push_update(self.value) self.push_update(self.value)
if self.reducedlogging:
await self.logvalue()
else:
logging.info("OneWire {} regular logging".format(self.sensor.name))
self.log_data(self.value)
self.lastlog = time.time()
await asyncio.sleep(self.interval) await asyncio.sleep(self.interval)
async def logvalue(self):
now=time.time()
logging.info("OneWire {} logging subroutine".format(self.sensor.name))
if self.kettle is not None:
try:
kettlestatus=self.kettle.instance.state
except:
kettlestatus=False
if kettlestatus:
self.log_data(self.value)
logging.info("OneWire {} Kettle Active".format(self.sensor.name))
self.lastlog = time.time()
else:
logging.info("OneWire {} 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("OneWire {} Fermenter Active".format(self.sensor.name))
self.lastlog = time.time()
else:
logging.info("OneWire {} 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
def get_state(self): def get_state(self):
return dict(value=self.value) return dict(value=self.value)

View file

@ -1,55 +0,0 @@
Metadata-Version: 2.1
Name: cbpi4
Version: 4.1.2
Summary: CraftBeerPi4 Brewing Software
Home-page: http://web.craftbeerpi.com
Author: Manuel Fritsch / Alexander Vollkopf
Author-email: manuel@craftbeerpi.com
License: GPLv3
Project-URL: Documentation, https://openbrewing.gitbook.io/craftbeerpi4_support/
Platform: UNKNOWN
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
# CraftBeerPi 4
[![Build](https://github.com/avollkopf/craftbeerpi4/actions/workflows/build.yml/badge.svg)](https://github.com/avollkopf/craftbeerpi4/actions/workflows/build.yml)
[![GitHub license](https://img.shields.io/github/license/avollkopf/craftbeerpi4)](https://github.com/avollkopf/craftbeerpi4/blob/master/LICENSE)
![GitHub issues](https://img.shields.io/github/issues-raw/avollkopf/craftbeerpi4)
![PyPI](https://img.shields.io/pypi/v/cbpi4)
![Happy Brewing](https://img.shields.io/badge/CraftBeerPi%204-Happy%20Brewing-%23FBB117)
<p align="center">
<img src="https://github.com/avollkopf/craftbeerpi4-ui/blob/main/cbpi4gui/public/logo192.png?raw=true" alt="CraftBeerPi Logo"/>
</p>
CraftBeerPi 4 is an open source software solution to control the brewing and
fermentation of beer :beer:.
## 📚 Documentation
Instructions on how to install CraftBeerPi and use its plugins is described
in the documentation, that can be found here: [gitbook.io](https://openbrewing.gitbook.io/craftbeerpi4_support/).
### Plugins
Plugins extend the base functionality of CraftBeerPi 4.
You can find a list of available plugins [here](https://openbrewing.gitbook.io/craftbeerpi4_support/master/plugin-installation#plugin-list).
## 🧑‍🤝‍🧑 Contribute
You want to help develop CraftBeerPi4? To get you quickly stated, this repository comes with a preconfigured
development environment. To be able to use this environment you need 2 things installed on your computer:
- docker
- visual studio code (vscode)
To start developing clone this repository, open the folder in vscode and use the _development container_ feature. The command is called _Reopen in container_. Please note that this quick start setup does not work if you want to develop directly on a 32bit raspberry pi os because docker is only available for 64bit arm plattform. Please use the regular development setup for that.
For a more detailed description of a development setup without the _development container_ feature see the documentation page:
[gitbook.io](https://openbrewing.gitbook.io/craftbeerpi4_support/)
### Contributors
Thanks to all the people who have contributed
[![contributors](https://contributors-img.web.app/image?repo=avollkopf/craftbeerpi4)](https://github.com/avollkopf/craftbeerpi4/graphs/contributors)

View file

@ -1,219 +0,0 @@
LICENSE
MANIFEST.in
README.md
setup.py
cbpi/__init__.py
cbpi/cli.py
cbpi/configFolder.py
cbpi/craftbeerpi.py
cbpi/eventbus.py
cbpi/satellite.py
cbpi/websocket.py
cbpi4.egg-info/PKG-INFO
cbpi4.egg-info/SOURCES.txt
cbpi4.egg-info/dependency_links.txt
cbpi4.egg-info/entry_points.txt
cbpi4.egg-info/requires.txt
cbpi4.egg-info/top_level.txt
cbpi/api/__init__.py
cbpi/api/actor.py
cbpi/api/base.py
cbpi/api/config.py
cbpi/api/dataclasses.py
cbpi/api/decorator.py
cbpi/api/exceptions.py
cbpi/api/extension.py
cbpi/api/fermenter_logic.py
cbpi/api/kettle_logic.py
cbpi/api/property.py
cbpi/api/sensor.py
cbpi/api/step.py
cbpi/api/timer.py
cbpi/config/actor.json
cbpi/config/chromium.desktop
cbpi/config/config.json
cbpi/config/config.yaml
cbpi/config/craftbeerpi.service
cbpi/config/craftbeerpiboot
cbpi/config/create_database.sql
cbpi/config/fermenter_data.json
cbpi/config/kettle.json
cbpi/config/plugin_list.txt
cbpi/config/sensor.json
cbpi/config/splash.png
cbpi/config/step_data.json
cbpi/controller/__init__.py
cbpi/controller/actor_controller.py
cbpi/controller/basic_controller2.py
cbpi/controller/config_controller.py
cbpi/controller/dashboard_controller.py
cbpi/controller/fermentation_controller.py
cbpi/controller/fermenter_recipe_controller.py
cbpi/controller/job_controller.py
cbpi/controller/kettle_controller.py
cbpi/controller/log_file_controller.py
cbpi/controller/notification_controller.py
cbpi/controller/plugin_controller.py
cbpi/controller/recipe_controller.py
cbpi/controller/satellite_controller.py
cbpi/controller/sensor_controller.py
cbpi/controller/step_controller.py
cbpi/controller/system_controller.py
cbpi/controller/upload_controller.py
cbpi/extension/__init__.py
cbpi/extension/ConfigUpdate/__init__.py
cbpi/extension/ConfigUpdate/config.yaml
cbpi/extension/ConfigUpdate/__pycache__/__init__.cpython-310.pyc
cbpi/extension/ConfigUpdate/__pycache__/__init__.cpython-311.pyc
cbpi/extension/ConfigUpdate/__pycache__/__init__.cpython-37.pyc
cbpi/extension/ConfigUpdate/__pycache__/__init__.cpython-39.pyc
cbpi/extension/FermentationStep/__init__.py
cbpi/extension/FermentationStep/config.yaml
cbpi/extension/FermentationStep/__pycache__/__init__.cpython-310.pyc
cbpi/extension/FermentationStep/__pycache__/__init__.cpython-311.pyc
cbpi/extension/FermentationStep/__pycache__/__init__.cpython-37.pyc
cbpi/extension/FermentationStep/__pycache__/__init__.cpython-39.pyc
cbpi/extension/FermenterHysteresis/__init__.py
cbpi/extension/FermenterHysteresis/config.yaml
cbpi/extension/FermenterHysteresis/__pycache__/__init__.cpython-310.pyc
cbpi/extension/FermenterHysteresis/__pycache__/__init__.cpython-311.pyc
cbpi/extension/FermenterHysteresis/__pycache__/__init__.cpython-37.pyc
cbpi/extension/FermenterHysteresis/__pycache__/__init__.cpython-39.pyc
cbpi/extension/__pycache__/__init__.cpython-310.pyc
cbpi/extension/__pycache__/__init__.cpython-311.pyc
cbpi/extension/__pycache__/__init__.cpython-37.pyc
cbpi/extension/__pycache__/__init__.cpython-39.pyc
cbpi/extension/dummyactor/__init__.py
cbpi/extension/dummyactor/config.yaml
cbpi/extension/dummyactor/__pycache__/__init__.cpython-310.pyc
cbpi/extension/dummyactor/__pycache__/__init__.cpython-311.pyc
cbpi/extension/dummyactor/__pycache__/__init__.cpython-37.pyc
cbpi/extension/dummyactor/__pycache__/__init__.cpython-39.pyc
cbpi/extension/dummysensor/__init__.py
cbpi/extension/dummysensor/config.yaml
cbpi/extension/dummysensor/__pycache__/__init__.cpython-310.pyc
cbpi/extension/dummysensor/__pycache__/__init__.cpython-311.pyc
cbpi/extension/dummysensor/__pycache__/__init__.cpython-37.pyc
cbpi/extension/dummysensor/__pycache__/__init__.cpython-39.pyc
cbpi/extension/gpioactor/__init__.py
cbpi/extension/gpioactor/config.yaml
cbpi/extension/gpioactor/__pycache__/__init__.cpython-310.pyc
cbpi/extension/gpioactor/__pycache__/__init__.cpython-311.pyc
cbpi/extension/gpioactor/__pycache__/__init__.cpython-37.pyc
cbpi/extension/gpioactor/__pycache__/__init__.cpython-39.pyc
cbpi/extension/httpsensor/__init__.py
cbpi/extension/httpsensor/config.yaml
cbpi/extension/httpsensor/__pycache__/__init__.cpython-310.pyc
cbpi/extension/httpsensor/__pycache__/__init__.cpython-311.pyc
cbpi/extension/httpsensor/__pycache__/__init__.cpython-37.pyc
cbpi/extension/httpsensor/__pycache__/__init__.cpython-39.pyc
cbpi/extension/hysteresis/__init__.py
cbpi/extension/hysteresis/config.yaml
cbpi/extension/hysteresis/__pycache__/__init__.cpython-310.pyc
cbpi/extension/hysteresis/__pycache__/__init__.cpython-311.pyc
cbpi/extension/hysteresis/__pycache__/__init__.cpython-37.pyc
cbpi/extension/hysteresis/__pycache__/__init__.cpython-39.pyc
cbpi/extension/mashstep/__init__.py
cbpi/extension/mashstep/config.yaml
cbpi/extension/mashstep/__pycache__/__init__.cpython-310.pyc
cbpi/extension/mashstep/__pycache__/__init__.cpython-311.pyc
cbpi/extension/mashstep/__pycache__/__init__.cpython-37.pyc
cbpi/extension/mashstep/__pycache__/__init__.cpython-39.pyc
cbpi/extension/mqtt_actor/__init__.py
cbpi/extension/mqtt_actor/config.yaml
cbpi/extension/mqtt_actor/generic_mqtt_actor.py
cbpi/extension/mqtt_actor/mqtt_actor.py
cbpi/extension/mqtt_actor/tasmota_mqtt_actor.py
cbpi/extension/mqtt_actor/__pycache__/__init__.cpython-310.pyc
cbpi/extension/mqtt_actor/__pycache__/__init__.cpython-311.pyc
cbpi/extension/mqtt_actor/__pycache__/__init__.cpython-37.pyc
cbpi/extension/mqtt_actor/__pycache__/__init__.cpython-39.pyc
cbpi/extension/mqtt_actor/__pycache__/generic_mqtt_actor.cpython-310.pyc
cbpi/extension/mqtt_actor/__pycache__/generic_mqtt_actor.cpython-311.pyc
cbpi/extension/mqtt_actor/__pycache__/generic_mqtt_actor.cpython-37.pyc
cbpi/extension/mqtt_actor/__pycache__/generic_mqtt_actor.cpython-39.pyc
cbpi/extension/mqtt_actor/__pycache__/mqtt_actor.cpython-310.pyc
cbpi/extension/mqtt_actor/__pycache__/mqtt_actor.cpython-311.pyc
cbpi/extension/mqtt_actor/__pycache__/mqtt_actor.cpython-37.pyc
cbpi/extension/mqtt_actor/__pycache__/mqtt_actor.cpython-39.pyc
cbpi/extension/mqtt_actor/__pycache__/tasmota_mqtt_actor.cpython-310.pyc
cbpi/extension/mqtt_actor/__pycache__/tasmota_mqtt_actor.cpython-311.pyc
cbpi/extension/mqtt_actor/__pycache__/tasmota_mqtt_actor.cpython-37.pyc
cbpi/extension/mqtt_actor/__pycache__/tasmota_mqtt_actor.cpython-39.pyc
cbpi/extension/mqtt_sensor/__init__.py
cbpi/extension/mqtt_sensor/config.yaml
cbpi/extension/mqtt_sensor/__pycache__/__init__.cpython-310.pyc
cbpi/extension/mqtt_sensor/__pycache__/__init__.cpython-311.pyc
cbpi/extension/mqtt_sensor/__pycache__/__init__.cpython-37.pyc
cbpi/extension/mqtt_sensor/__pycache__/__init__.cpython-39.pyc
cbpi/extension/mqtt_util/__init__.py
cbpi/extension/mqtt_util/config.yaml
cbpi/extension/mqtt_util/__pycache__/__init__.cpython-310.pyc
cbpi/extension/mqtt_util/__pycache__/__init__.cpython-311.pyc
cbpi/extension/mqtt_util/__pycache__/__init__.cpython-37.pyc
cbpi/extension/mqtt_util/__pycache__/__init__.cpython-39.pyc
cbpi/extension/onewire/__init__.py
cbpi/extension/onewire/config.yaml
cbpi/extension/onewire/__pycache__/__init__.cpython-310.pyc
cbpi/extension/onewire/__pycache__/__init__.cpython-311.pyc
cbpi/extension/onewire/__pycache__/__init__.cpython-37.pyc
cbpi/extension/onewire/__pycache__/__init__.cpython-39.pyc
cbpi/http_endpoints/__init__.py
cbpi/http_endpoints/http_actor.py
cbpi/http_endpoints/http_config.py
cbpi/http_endpoints/http_dashboard.py
cbpi/http_endpoints/http_fermentation.py
cbpi/http_endpoints/http_fermenterrecipe.py
cbpi/http_endpoints/http_kettle.py
cbpi/http_endpoints/http_log.py
cbpi/http_endpoints/http_login.py
cbpi/http_endpoints/http_notification.py
cbpi/http_endpoints/http_plugin.py
cbpi/http_endpoints/http_recipe.py
cbpi/http_endpoints/http_sensor.py
cbpi/http_endpoints/http_step.py
cbpi/http_endpoints/http_system.py
cbpi/http_endpoints/http_upload.py
cbpi/job/__init__.py
cbpi/job/_job.py
cbpi/job/_scheduler.py
cbpi/job/aiohttp.py
cbpi/static/beer_icon.svg
cbpi/static/calculator_icon.svg
cbpi/static/cbpi_icon.svg
cbpi/static/control_icon.svg
cbpi/static/glass_icon.svg
cbpi/static/grain.svg
cbpi/static/hops_icon.svg
cbpi/static/kettle2_icon.svg
cbpi/static/kettle_icon.svg
cbpi/static/led.svg
cbpi/static/liquid_icon.svg
cbpi/static/paddle_icon.svg
cbpi/static/pipe_icon.svg
cbpi/static/sensor_icon.svg
cbpi/static/splash.png
cbpi/static/svg_icon.svg
cbpi/static/tank_icon.svg
cbpi/static/target_temp.svg
cbpi/static/test.html
cbpi/static/thermomenter.svg
cbpi/static/yeast.svg
cbpi/utils/__init__.py
cbpi/utils/encoder.py
cbpi/utils/utils.py
tests/__init__.py
tests/cbpi_config_fixture.py
tests/test_actor.py
tests/test_cli.py
tests/test_config.py
tests/test_dashboard.py
tests/test_gpio.py
tests/test_index.py
tests/test_kettle.py
tests/test_logger.py
tests/test_notification_controller.py
tests/test_sensor.py
tests/test_step.py
tests/test_system.py
tests/test_ws.py

View file

@ -1 +0,0 @@
https://testpypi.python.org/pypi

View file

@ -1,3 +0,0 @@
[console_scripts]
cbpi = cbpi.cli:main

View file

@ -1,24 +0,0 @@
typing-extensions>=4
aiohttp==3.8.3
aiohttp-auth==0.1.1
aiohttp-route-decorator==0.1.4
aiohttp-security==0.4.0
aiohttp-session==2.12.0
aiohttp-swagger==1.0.16
aiojobs==1.1.0
aiosqlite==0.17.0
cryptography==36.0.1
requests==2.28.1
voluptuous==0.13.1
pyfiglet==0.8.post1
click==8.1.3
shortuuid==1.0.11
tabulate==0.9.0
asyncio-mqtt==0.16.1
inquirer==3.1.1
colorama==0.4.6
psutil==5.9.4
cbpi4gui
importlib_metadata
numpy==1.24.1
pandas==1.5.3

View file

@ -1,2 +0,0 @@
cbpi
tests

View file

@ -7,7 +7,7 @@ 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==36.0.1 cryptography==40.0.0
requests==2.28.1 requests==2.28.1
voluptuous==0.13.1 voluptuous==0.13.1
pyfiglet==0.8.post1 pyfiglet==0.8.post1

View file

@ -47,7 +47,7 @@ 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==36.0.1", "cryptography==40.0.0",
"requests==2.28.1", "requests==2.28.1",
"voluptuous==0.13.1", "voluptuous==0.13.1",
"pyfiglet==0.8.post1", "pyfiglet==0.8.post1",