Moved brewfiles to source folder & add /source to sys path from __init__

This commit is contained in:
2021-10-03 19:26:22 +02:00
parent f58b33f08a
commit 7bdbf0b4dc
7 changed files with 10 additions and 2 deletions

5
source/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
print('at init')
import sys
sys.path.append('/home/pi/brewLogger/source')
print('added to source')

49
source/brewCamera.py Normal file
View File

@@ -0,0 +1,49 @@
import os
import time
import picamera
import threading
from datetime import datetime
from logger import logger
class BrewCamera():
def __init__(self, interval=10):
self.lastCaptureTimestamp = None
self.interval = interval
self.warmupTime = 0.3
def spawnBackgroundCapture(self):
thread = threading.Thread(target=self.captureOnIntervalForever, args=())
thread.daemon = True
thread.start()
logger.info("spawned camera capture daemon at interval: {}".format(self.interval))
def captureOnIntervalForever(self):
while True:
time.sleep(self.interval - self.warmupTime)
self.capture()
def capture(self):
try:
logger.debug('Capturing image')
with picamera.PiCamera() as camera:
camera.resolution = (1297, 972)
camera.rotation = 180
camera.annotate_background = picamera.Color('black')
camera.annotate_text_size = 50 # (values 6 to 160, default is 32)
camera.annotate_text = datetime.now().strftime('%A %d %b %Y %H:%M:%S')
# Camera warm-up time
time.sleep(self.warmupTime)
camera.capture('assets/foo.jpg')
self.lastCaptureTime = datetime.now()
os.replace('assets/foo.jpg', 'assets/capture.jpg')
except picamera.exc.PiCameraMMALError as error:
logger.error('Picamera MMAL exception. Retrying picture in 1 second', es={
error: str(error),
exception: error.__class__.__name__
})
time.sleep(1)
self.capture()

62
source/brewRelay.py Normal file
View File

@@ -0,0 +1,62 @@
import RPi.GPIO as GPIO
from logger import logger
from utils import getConfig
import sqlite3
class BrewRelay():
def __init__(self, pin, controls):
# GPIO.setmode(GPIO.BOARD)
self.pin = pin
self.controls = controls
config = getConfig()
self.conn = sqlite3.connect(config['database']['name'], check_same_thread=False)
self.cur = self.conn.cursor()
GPIO.setup(self.pin, GPIO.OUT)
self.set(self.state, True)
@property
def state(self):
query = 'select state from relay where pin = {}'.format(self.pin)
self.cur.execute(query)
return True if self.cur.fetchone()[0] == 1 else False
def saveStateToDB(self, state):
query = 'update relay set state = {} where pin = {}'
self.cur.execute(query.format(state, self.pin))
self.conn.commit()
def set(self, state, setup=False):
GPIO.output(self.pin, not state) # for some reason this is negated
if setup is False:
logger.info('Relay toggled', es={'relayState': state, 'relayType': self.controls})
else:
logger.info('Resuming relay state', es={'relayState': state, 'relayType': self.controls})
self.saveStateToDB(state)
def toggle(self):
self.set(not state)
@staticmethod
def fromYaml(loader, node):
return BrewRelay(**loader.construct_mapping(node))
@staticmethod
def getRelayByWhatItControls(relays, controls):
return next(( relay for relay in relays if relay.controls == controls), None)
def __exit__(self):
self.conn.close()
if __name__ == '__main__':
brewRelay = BrewRelay()
import time
while True:
print('toggling!')
brewRelay.toggle()
time.sleep(1)

192
source/brewSensor.py Normal file
View File

@@ -0,0 +1,192 @@
import bme680
import adafruit_dht
import board
import threading
import time
from logger import logger
class BrewSensor():
def __init__(self, location, interval=2):
self.location = location
self.interval = interval
@staticmethod
def getSensorByItsLocation(sensors, location):
return next(( sensor for sensor in sensors if sensor.location == location), None)
class DHT11Sensor(BrewSensor):
def __init__(self, pin, location, interval):
super().__init__(location, interval)
self.pin = pin
self.sensor = adafruit_dht.DHT11(board.D17)
@property
def temp(self):
try:
return self.sensor.temperature
except RuntimeError as error:
timeout = 2
telemetry = {
'location': self.location,
'error': str(error),
'exception': error.__class__.__name__
}
logger.error('DHT sensor got invalid checksum, trying again in {} seconds.'.format(timeout), es=telemetry)
time.sleep(timeout)
return self.temp
@property
def humidity(self):
return self.sensor.humidity
def logReadings(self):
telemetry = {
'temperature': self.temp,
'humidity': self.humidity,
'location': self.location
}
logger.info("Sensor readings", es=telemetry)
return
def spawnBackgroundSensorLog(self):
thread = threading.Thread(target=self.logSensorOnIntervalForever, args=())
thread.daemon = True
thread.start()
logger.info("spawned background sensor {} log at interval: {}".format(self.location, self.interval))
def logSensorOnIntervalForever(self):
try:
self.logReadings()
except RuntimeError as error:
logger.error('Sensor log daemon failed, sleeping and trying again', es={
'location': self.location,
'error': str(error),
'exception': error.__class__.__name__
})
time.sleep(2)
time.sleep(self.interval)
self.logSensorOnIntervalForever()
@staticmethod
def fromYaml(loader, node):
return DHT11Sensor(**loader.construct_mapping(node))
class BCM600Sensor(BrewSensor):
def __init__(self, location, interval):
super().__init__(location, interval)
self.sensor = bme680.BME680()
self.sensor.set_humidity_oversample(bme680.OS_2X)
self.setupSensors()
self.lastSensorRead = time.time()
def setupSensors(self):
self.sensor.set_pressure_oversample(bme680.OS_4X)
self.sensor.set_temperature_oversample(bme680.OS_8X)
self.sensor.set_filter(bme680.FILTER_SIZE_3)
self.sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
self.sensor.set_gas_heater_temperature(320)
self.sensor.set_gas_heater_duration(150)
self.sensor.select_gas_heater_profile(0)
def read(self):
self.lastSensorRead = time.time()
return self.sensor.get_sensor_data()
def logReadings(self, detailed):
if self.needToUpdateReadings:
self.read()
telemetry = {
'temperature': self.temp,
'pressure': self.pressure,
'humidity': self.humidity,
'location': self.location
}
if detailed:
telemetry['gasResistance'] = self.gasResistance
telemetry['stableHeat'] = self.stableHeat
logger.info("Sensor readings", es=telemetry)
return
def saveToFile(self, filename):
with open(filename, "w") as file:
file.write("{}".format(self.temp))
def spawnBackgroundSensorLog(self):
thread = threading.Thread(target=self.logSensorOnIntervalForever, args=())
thread.daemon = True
thread.start()
logger.info("spawned background sensor {} log at interval: {}".format(self.location, self.interval))
def logSensorOnIntervalForever(self):
try:
self.logReadings(detailed=True)
except exception as error:
logger.error('Sensor log daemon failed, sleeping and trying again', es={
'location': self.location,
'error': str(error),
'exception': error.__class__.__name__
})
time.sleep(2)
time.sleep(self.interval)
self.logSensorOnIntervalForever()
@property
def needToUpdateReadings(self):
return time.time() - self.lastSensorRead > 1
@property
def temp(self):
if self.needToUpdateReadings:
self.read()
return self.sensor.data.temperature
@property
def pressure(self):
if self.needToUpdateReadings:
self.read()
return self.sensor.data.pressure
@property
def humidity(self):
if self.needToUpdateReadings:
self.read()
return self.sensor.data.humidity
@property
def gasResistance(self):
if self.needToUpdateReadings:
self.read()
return self.sensor.data.gas_resistance
@property
def stableHeat(self):
if self.needToUpdateReadings:
self.read()
return self.sensor.data.heat_stable
@staticmethod
def fromYaml(loader, node):
return BCM600Sensor(**loader.construct_mapping(node))
def __repr__(self):
return "{0:.2f} C,{1:.2f} hPa,{2:.2f} %RH".format(self.temp, self.pressure, self.humidity)
if __name__ == '__main__':
# brewSensor = DHT11Sensor(13, 'outside', 30)
brewSensor = BCM600Sensor('inside', 2)
while True:
print(brewSensor.temp)
time.sleep(1)

79
source/logger.py Normal file
View File

@@ -0,0 +1,79 @@
#!/bin/usr/python3
import logging
import os
import json
import uuid
from datetime import datetime, date
import urllib.request
from utils import getConfig
config = getConfig()
LOGGER_NAME = config['logger']['name']
esHost = config['elastic']['host']
esPort = config['elastic']['port']
class ESHandler(logging.Handler):
def __init__(self, *args, **kwargs):
self.host = kwargs.get('host')
self.port = kwargs.get('port') or 9200
self.date = date.today()
self.sessionID = uuid.uuid4()
logging.StreamHandler.__init__(self)
def emit(self, record):
self.format(record)
timestamp = datetime.fromtimestamp(record.created).strftime('%Y-%m-%dT%H:%M:%S.%f+02:00')
indexURL = 'http://{}:{}/{}-{}/_doc'.format(self.host, self.port, LOGGER_NAME, self.date.strftime('%Y.%m.%d'))
doc = {
'severity': record.levelname,
'message': record.message,
'@timestamp': timestamp,
'sessionID': str(self.sessionID)
}
if hasattr(record, 'es'):
for param in record.es.values():
if ': {}'.format(param) in record.message:
doc['message'] = record.message.replace(': {}'.format(str(param)), '')
doc = {**record.es, **doc}
payload = json.dumps(doc).encode('utf8')
req = urllib.request.Request(indexURL, data=payload,
headers={'content-type': 'application/json'})
response = urllib.request.urlopen(req)
response = response.read().decode('utf8')
return response
class ElasticFieldParameterAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra={}):
super().__init__(logger, extra)
def process(self, msg, kwargs):
if kwargs == {}:
return (msg, kwargs)
extra = kwargs.get("extra", {})
extra.update({"es": kwargs.pop("es", True)})
kwargs["extra"] = extra
return (msg, kwargs)
logger = logging.getLogger(LOGGER_NAME)
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING)
eh = ESHandler(host=esHost, port=esPort)
eh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)8s | %(message)s')
logger.addHandler(ch)
logger.addHandler(eh)
logger = ElasticFieldParameterAdapter(logger)

22
source/utils.py Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/usr/python3
import os
import yaml
def loadYaml(filePath):
with open(filePath, "r") as stream:
try:
return yaml.safe_load(stream)
except yaml.YAMLError as exception:
print('Error: {} is unparsable'.format(filePath))
print(exception)
def getConfig():
pwd = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(pwd,'../', 'config.yaml')
if not os.path.isfile(path):
print('Please fill out and rename config file. Check README for more info.')
exit(0)
return loadYaml(path)