|
|
@@ -20,6 +20,7 @@ from colorama import Style |
|
|
from subprocess import check_call |
|
|
from subprocess import check_call |
|
|
from signal import pause |
|
|
from signal import pause |
|
|
import smtplib, ssl |
|
|
import smtplib, ssl |
|
|
|
|
|
from collections import deque |
|
|
|
|
|
|
|
|
picdir = os.path.realpath("/home/firestone/pi-zero-tank/pic") |
|
|
picdir = os.path.realpath("/home/firestone/pi-zero-tank/pic") |
|
|
libdir = os.path.realpath("/home/firestone/pi-zero-tank/lib") |
|
|
libdir = os.path.realpath("/home/firestone/pi-zero-tank/lib") |
|
|
@@ -37,6 +38,7 @@ import traceback |
|
|
logging.basicConfig(level=logging.DEBUG) |
|
|
logging.basicConfig(level=logging.DEBUG) |
|
|
|
|
|
|
|
|
NUM_SAMPLES = 100 |
|
|
NUM_SAMPLES = 100 |
|
|
|
|
|
AVG_WINDOWSIZE = 20 |
|
|
FILE = Path("config.ini") |
|
|
FILE = Path("config.ini") |
|
|
ALARM_LEVEL_CM = 5.0 |
|
|
ALARM_LEVEL_CM = 5.0 |
|
|
|
|
|
|
|
|
@@ -128,6 +130,15 @@ class Buttons: |
|
|
def none_event(self): |
|
|
def none_event(self): |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
class MovingAverage: |
|
|
|
|
|
def __init__(self, size: int): |
|
|
|
|
|
self.size = size |
|
|
|
|
|
self.buffer = deque(maxlen=size) |
|
|
|
|
|
|
|
|
|
|
|
def add_value(self, value: float): |
|
|
|
|
|
self.buffer.append(value) |
|
|
|
|
|
return sum(self.buffer) / len(self.buffer), len(self.buffer) == self.size |
|
|
|
|
|
|
|
|
def shutdown(epd): |
|
|
def shutdown(epd): |
|
|
print("System wird heruntergefahren:") |
|
|
print("System wird heruntergefahren:") |
|
|
print("Erstelle Backup...") |
|
|
print("Erstelle Backup...") |
|
|
@@ -148,8 +159,7 @@ def shutdown(epd): |
|
|
epd.sleep() |
|
|
epd.sleep() |
|
|
epd2in13_V4.epdconfig.module_exit(cleanup=True) |
|
|
epd2in13_V4.epdconfig.module_exit(cleanup=True) |
|
|
print("Linux shutdown...") |
|
|
print("Linux shutdown...") |
|
|
#check_call(['sudo', 'poweroff']) |
|
|
|
|
|
#TODO |
|
|
|
|
|
|
|
|
#check_call(['sudo', 'poweroff']) #TODO |
|
|
sys.exit(1) |
|
|
sys.exit(1) |
|
|
pause() |
|
|
pause() |
|
|
|
|
|
|
|
|
@@ -162,7 +172,6 @@ def measure(sensor): |
|
|
except RuntimeError: |
|
|
except RuntimeError: |
|
|
fails += 1 |
|
|
fails += 1 |
|
|
time.sleep(0.01) |
|
|
time.sleep(0.01) |
|
|
#print(len(data), fails) |
|
|
|
|
|
if fails < (NUM_SAMPLES / 2): |
|
|
if fails < (NUM_SAMPLES / 2): |
|
|
mid_count = NUM_SAMPLES // 2 |
|
|
mid_count = NUM_SAMPLES // 2 |
|
|
start_index = (NUM_SAMPLES - mid_count) // 2 |
|
|
start_index = (NUM_SAMPLES - mid_count) // 2 |
|
|
@@ -173,7 +182,6 @@ def measure(sensor): |
|
|
val = 0 |
|
|
val = 0 |
|
|
else: |
|
|
else: |
|
|
val = 0 |
|
|
val = 0 |
|
|
#val = random.uniform(5.0, 40.0) |
|
|
|
|
|
return val |
|
|
return val |
|
|
|
|
|
|
|
|
def store_value(config, val, meas, lvl, date): |
|
|
def store_value(config, val, meas, lvl, date): |
|
|
@@ -211,12 +219,11 @@ Die aktuelle Hoehe betraegt {3:.0f} cm. |
|
|
Gruesse vom Tank-Computer""".format(meas, sender_email, receiver_email, val) |
|
|
Gruesse vom Tank-Computer""".format(meas, sender_email, receiver_email, val) |
|
|
context = ssl.create_default_context() |
|
|
context = ssl.create_default_context() |
|
|
with smtplib.SMTP(smtp_server, smtp_port) as server: |
|
|
with smtplib.SMTP(smtp_server, smtp_port) as server: |
|
|
#TODO |
|
|
|
|
|
#server.set_debuglevel(1) |
|
|
#server.set_debuglevel(1) |
|
|
#server.ehlo() |
|
|
|
|
|
|
|
|
server.ehlo() |
|
|
server.starttls(context=context) |
|
|
server.starttls(context=context) |
|
|
#server.login(sender_email, smtp_password.strip()) |
|
|
|
|
|
#server.sendmail(sender_email, receiver_email, message) |
|
|
|
|
|
|
|
|
server.login(sender_email, smtp_password.strip()) |
|
|
|
|
|
server.sendmail(sender_email, receiver_email, message) |
|
|
|
|
|
|
|
|
def display_site_measure(epd, titel, titel_pos, val): |
|
|
def display_site_measure(epd, titel, titel_pos, val): |
|
|
dp = Image.new('1', (epd.height, epd.width), 255) |
|
|
dp = Image.new('1', (epd.height, epd.width), 255) |
|
|
@@ -231,35 +238,39 @@ def display_site_measure(epd, titel, titel_pos, val): |
|
|
draw.text((156, 110), "Abbrechen", font = font_label, fill = 0) |
|
|
draw.text((156, 110), "Abbrechen", font = font_label, fill = 0) |
|
|
epd.display(epd.getbuffer(dp)) |
|
|
epd.display(epd.getbuffer(dp)) |
|
|
|
|
|
|
|
|
def display_site_main(epd, cl, ph): |
|
|
|
|
|
|
|
|
def display_site_main(epd, cl, ph, clm, phm): |
|
|
dp = Image.new('1', (epd.height, epd.width), 255) |
|
|
dp = Image.new('1', (epd.height, epd.width), 255) |
|
|
newimage = Image.open(os.path.join(picdir, 'tankgrafik.bmp')) |
|
|
newimage = Image.open(os.path.join(picdir, 'tankgrafik.bmp')) |
|
|
dp.paste(newimage, (0,0)) |
|
|
dp.paste(newimage, (0,0)) |
|
|
draw = ImageDraw.Draw(dp) |
|
|
draw = ImageDraw.Draw(dp) |
|
|
draw.text((19, 31), f"Chlor: {cl:.0f} %", font = font_label, fill = 0) |
|
|
draw.text((19, 31), f"Chlor: {cl:.0f} %", font = font_label, fill = 0) |
|
|
draw.text((149, 31), f"pH-: {ph:.0f} %", font = font_label, fill = 0) |
|
|
draw.text((149, 31), f"pH-: {ph:.0f} %", font = font_label, fill = 0) |
|
|
if cl < 80: |
|
|
|
|
|
draw.rectangle((10, 54, 114, 61), outline = 1, fill = 1) |
|
|
|
|
|
if cl < 60: |
|
|
|
|
|
draw.rectangle((10, 66, 114, 73), outline = 1, fill = 1) |
|
|
|
|
|
if cl < 40: |
|
|
|
|
|
draw.rectangle((10, 78, 114, 85), outline = 1, fill = 1) |
|
|
|
|
|
if cl < 20: |
|
|
|
|
|
draw.rectangle((10, 90, 114, 97), outline = 1, fill = 1) |
|
|
|
|
|
if cl < ALARM_LEVEL_CM: #todo and email |
|
|
|
|
|
|
|
|
if clm == 1: # wenn mail gesendet (cl < alarm) |
|
|
|
|
|
# alle Balken ausblenden |
|
|
draw.rectangle((10, 54, 114, 109), outline = 1, fill = 1) |
|
|
draw.rectangle((10, 54, 114, 109), outline = 1, fill = 1) |
|
|
draw.text((37, 53), "X", font = font_huge, fill = 0) |
|
|
draw.text((37, 53), "X", font = font_huge, fill = 0) |
|
|
if ph < 80: |
|
|
|
|
|
draw.rectangle((135, 54, 239, 61), outline = 1, fill = 1) |
|
|
|
|
|
if ph < 60: |
|
|
|
|
|
draw.rectangle((135, 66, 239, 73), outline = 1, fill = 1) |
|
|
|
|
|
if ph < 40: |
|
|
|
|
|
draw.rectangle((135, 78, 239, 85), outline = 1, fill = 1) |
|
|
|
|
|
if ph < 20: |
|
|
|
|
|
draw.rectangle((135, 90, 239, 97), outline = 1, fill = 1) |
|
|
|
|
|
if ph < ALARM_LEVEL_CM: #todo and email |
|
|
|
|
|
|
|
|
else: |
|
|
|
|
|
if cl < 80: #100-80 |
|
|
|
|
|
draw.rectangle((10, 54, 114, 61), outline = 1, fill = 1) |
|
|
|
|
|
if cl < 60: #80-60 |
|
|
|
|
|
draw.rectangle((10, 66, 114, 73), outline = 1, fill = 1) |
|
|
|
|
|
if cl < 40: #60-40 |
|
|
|
|
|
draw.rectangle((10, 78, 114, 85), outline = 1, fill = 1) |
|
|
|
|
|
if cl < 20: #40-20 |
|
|
|
|
|
draw.rectangle((10, 90, 114, 97), outline = 1, fill = 1) |
|
|
|
|
|
if phm == 1: # wenn mail gesendet (ph- < alarm) |
|
|
|
|
|
# alle Balken ausblenden |
|
|
draw.rectangle((135, 54, 239, 109), outline = 1, fill = 1) |
|
|
draw.rectangle((135, 54, 239, 109), outline = 1, fill = 1) |
|
|
draw.text((164, 53), "X", font = font_huge, fill = 0) |
|
|
draw.text((164, 53), "X", font = font_huge, fill = 0) |
|
|
|
|
|
else: |
|
|
|
|
|
if ph < 80: #100-80 |
|
|
|
|
|
draw.rectangle((135, 54, 239, 61), outline = 1, fill = 1) |
|
|
|
|
|
if ph < 60: #80-60 |
|
|
|
|
|
draw.rectangle((135, 66, 239, 73), outline = 1, fill = 1) |
|
|
|
|
|
if ph < 40: #60-40 |
|
|
|
|
|
draw.rectangle((135, 78, 239, 85), outline = 1, fill = 1) |
|
|
|
|
|
if ph < 20: #40-20 |
|
|
|
|
|
draw.rectangle((135, 90, 239, 97), outline = 1, fill = 1) |
|
|
epd.display(epd.getbuffer(dp)) |
|
|
epd.display(epd.getbuffer(dp)) |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
if __name__ == '__main__': |
|
|
@@ -274,7 +285,7 @@ if __name__ == '__main__': |
|
|
font_label = ImageFont.truetype(os.path.join(picdir, 'squarea.ttf'), 15) |
|
|
font_label = ImageFont.truetype(os.path.join(picdir, 'squarea.ttf'), 15) |
|
|
font_huge = ImageFont.truetype(os.path.join(picdir, 'squarea.ttf'), 74) |
|
|
font_huge = ImageFont.truetype(os.path.join(picdir, 'squarea.ttf'), 74) |
|
|
|
|
|
|
|
|
display_site_main(epd, 0, 0) |
|
|
|
|
|
|
|
|
display_site_main(epd, 0, 0, 0, 0) |
|
|
|
|
|
|
|
|
print("Lese config.ini...") |
|
|
print("Lese config.ini...") |
|
|
config = configparser.ConfigParser() |
|
|
config = configparser.ConfigParser() |
|
|
@@ -307,35 +318,13 @@ if __name__ == '__main__': |
|
|
buttons = Buttons(button_cl, button_ph) |
|
|
buttons = Buttons(button_cl, button_ph) |
|
|
|
|
|
|
|
|
print("Starte Hauptprogramm...") |
|
|
print("Starte Hauptprogramm...") |
|
|
|
|
|
mavg_cl = MovingAverage(AVG_WINDOWSIZE) |
|
|
|
|
|
mavg_ph = MovingAverage(AVG_WINDOWSIZE) |
|
|
|
|
|
clp_prev = -1 |
|
|
|
|
|
php_prev = -1 |
|
|
|
|
|
force_update = False |
|
|
while True: |
|
|
while True: |
|
|
try: |
|
|
try: |
|
|
cl = measure(sensor_cl) |
|
|
|
|
|
ph = measure(sensor_ph) |
|
|
|
|
|
clp = get_percent(cl_leer, cl_voll, cl) |
|
|
|
|
|
php = get_percent(ph_leer, ph_voll, ph) |
|
|
|
|
|
display_site_main(epd, clp, php) |
|
|
|
|
|
print("Chlor: {:10.2f} cm, pH-Minus: {:10.2f} cm".format(cl, ph)) |
|
|
|
|
|
print("Chlor: {:10.2f} %, pH-Minus: {:10.2f} %".format(clp, php)) |
|
|
|
|
|
if abs(cl_leer - ALARM_LEVEL_CM) < cl: |
|
|
|
|
|
print(f"{Fore.RED}Chlor-Tank fast leer!{Style.RESET_ALL}") |
|
|
|
|
|
if cl_mail == 0: |
|
|
|
|
|
print("Sende E-Mail...") |
|
|
|
|
|
send_email("Chlor", abs(cl_leer - cl)) |
|
|
|
|
|
cl_mail = 1 |
|
|
|
|
|
config.set('Chlor', 'mail', str(cl_mail)) |
|
|
|
|
|
config.set('Chlor', 'datum_mail', str(datetime.datetime.now())) |
|
|
|
|
|
with open(FILE, 'w') as configfile: |
|
|
|
|
|
config.write(configfile) |
|
|
|
|
|
if abs(ph_leer - ALARM_LEVEL_CM) < ph: |
|
|
|
|
|
print(f"{Fore.RED}pH-Minus-Tank fast leer!{Style.RESET_ALL}") |
|
|
|
|
|
if ph_mail == 0: |
|
|
|
|
|
print("Sende E-Mail...") |
|
|
|
|
|
send_email("pH-Minus", abs(ph_leer - ph)) |
|
|
|
|
|
ph_mail = 1 |
|
|
|
|
|
config.set('pH-', 'mail', str(ph_mail)) |
|
|
|
|
|
config.set('pH-', 'datum_mail', str(datetime.datetime.now())) |
|
|
|
|
|
with open(FILE, 'w') as configfile: |
|
|
|
|
|
config.write(configfile) |
|
|
|
|
|
state = buttons.get_state() |
|
|
state = buttons.get_state() |
|
|
if state == States.MEASURE_CL_FULL: |
|
|
if state == States.MEASURE_CL_FULL: |
|
|
buttons.deactivate_events() |
|
|
buttons.deactivate_events() |
|
|
@@ -348,11 +337,18 @@ if __name__ == '__main__': |
|
|
if button_cl.is_pressed: |
|
|
if button_cl.is_pressed: |
|
|
store_value(config, val, "Chlor", "voll", "datum_voll") |
|
|
store_value(config, val, "Chlor", "voll", "datum_voll") |
|
|
cl_voll = val |
|
|
cl_voll = val |
|
|
break; |
|
|
|
|
|
|
|
|
cl_mail = 0 |
|
|
|
|
|
mavg_cl = MovingAverage(AVG_WINDOWSIZE) |
|
|
|
|
|
config.set('Chlor', 'mail', str(cl_mail)) |
|
|
|
|
|
config.set('Chlor', 'datum_mail', "") |
|
|
|
|
|
with open(FILE, 'w') as configfile: |
|
|
|
|
|
config.write(configfile) |
|
|
|
|
|
break |
|
|
elif button_ph.is_pressed: |
|
|
elif button_ph.is_pressed: |
|
|
print("Abbruch!") |
|
|
print("Abbruch!") |
|
|
break |
|
|
break |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
|
|
|
force_update = True |
|
|
buttons.activate_events() |
|
|
buttons.activate_events() |
|
|
elif state == States.MEASURE_CL_EMPTY: |
|
|
elif state == States.MEASURE_CL_EMPTY: |
|
|
buttons.deactivate_events() |
|
|
buttons.deactivate_events() |
|
|
@@ -370,6 +366,7 @@ if __name__ == '__main__': |
|
|
print("Abbruch!") |
|
|
print("Abbruch!") |
|
|
break |
|
|
break |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
|
|
|
force_update = True |
|
|
buttons.activate_events() |
|
|
buttons.activate_events() |
|
|
elif state == States.MEASURE_PH_FULL: |
|
|
elif state == States.MEASURE_PH_FULL: |
|
|
buttons.deactivate_events() |
|
|
buttons.deactivate_events() |
|
|
@@ -382,11 +379,18 @@ if __name__ == '__main__': |
|
|
if button_cl.is_pressed: |
|
|
if button_cl.is_pressed: |
|
|
store_value(config, val, "pH-", "voll", "datum_voll") |
|
|
store_value(config, val, "pH-", "voll", "datum_voll") |
|
|
ph_voll = val |
|
|
ph_voll = val |
|
|
break; |
|
|
|
|
|
|
|
|
ph_mail = 0 |
|
|
|
|
|
mavg_ph = MovingAverage(AVG_WINDOWSIZE) |
|
|
|
|
|
config.set('pH-', 'mail', str(ph_mail)) |
|
|
|
|
|
config.set('pH-', 'datum_mail', "") |
|
|
|
|
|
with open(FILE, 'w') as configfile: |
|
|
|
|
|
config.write(configfile) |
|
|
|
|
|
break |
|
|
elif button_ph.is_pressed: |
|
|
elif button_ph.is_pressed: |
|
|
print("Abbruch!") |
|
|
print("Abbruch!") |
|
|
break |
|
|
break |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
|
|
|
force_update = True |
|
|
buttons.activate_events() |
|
|
buttons.activate_events() |
|
|
elif state == States.MEASURE_PH_EMPTY: |
|
|
elif state == States.MEASURE_PH_EMPTY: |
|
|
buttons.deactivate_events() |
|
|
buttons.deactivate_events() |
|
|
@@ -399,14 +403,61 @@ if __name__ == '__main__': |
|
|
if button_cl.is_pressed: |
|
|
if button_cl.is_pressed: |
|
|
store_value(config, val, "pH-", "leer", "datum_leer") |
|
|
store_value(config, val, "pH-", "leer", "datum_leer") |
|
|
ph_leer = val |
|
|
ph_leer = val |
|
|
break; |
|
|
|
|
|
|
|
|
break |
|
|
elif button_ph.is_pressed: |
|
|
elif button_ph.is_pressed: |
|
|
print("Abbruch!") |
|
|
print("Abbruch!") |
|
|
break |
|
|
break |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
buttons.set_state(States.MAIN_MENU) |
|
|
|
|
|
force_update = True |
|
|
buttons.activate_events() |
|
|
buttons.activate_events() |
|
|
elif state == States.SHUTDOWN: |
|
|
elif state == States.SHUTDOWN: |
|
|
shutdown(epd) |
|
|
shutdown(epd) |
|
|
|
|
|
else: |
|
|
|
|
|
tmp = measure(sensor_cl) |
|
|
|
|
|
if tmp != 0: |
|
|
|
|
|
cl, cl_status = mavg_cl.add_value(tmp) |
|
|
|
|
|
else: |
|
|
|
|
|
cl = 0 |
|
|
|
|
|
cl_status = False |
|
|
|
|
|
tmp = measure(sensor_ph) |
|
|
|
|
|
if tmp != 0: |
|
|
|
|
|
ph, ph_status = mavg_ph.add_value(tmp) |
|
|
|
|
|
else: |
|
|
|
|
|
ph = 0 |
|
|
|
|
|
ph_status = False |
|
|
|
|
|
clp = get_percent(cl_leer, cl_voll, cl) |
|
|
|
|
|
php = get_percent(ph_leer, ph_voll, ph) |
|
|
|
|
|
print("Chlor: {:10.2f} cm, pH-Minus: {:10.2f} cm".format(cl, ph)) |
|
|
|
|
|
print("Chlor: {:10.2f} %, pH-Minus: {:10.2f} %".format(clp, php)) |
|
|
|
|
|
clp_now = round(clp, 0) |
|
|
|
|
|
php_now = round(php, 0) |
|
|
|
|
|
if clp_now != clp_prev or php_now != php_prev or force_update: |
|
|
|
|
|
display_site_main(epd, clp, php, cl_mail, ph_mail) |
|
|
|
|
|
force_update = False |
|
|
|
|
|
clp_prev = clp_now |
|
|
|
|
|
php_prev = php_now |
|
|
|
|
|
if cl_status and abs(cl_leer - ALARM_LEVEL_CM) < cl: |
|
|
|
|
|
print(f"{Fore.RED}Chlor-Tank fast leer!{Style.RESET_ALL}") |
|
|
|
|
|
if cl_mail == 0: |
|
|
|
|
|
print("Sende E-Mail...") |
|
|
|
|
|
send_email("Chlor", abs(cl_leer - cl)) |
|
|
|
|
|
cl_mail = 1 |
|
|
|
|
|
config.set('Chlor', 'mail', str(cl_mail)) |
|
|
|
|
|
config.set('Chlor', 'datum_mail', str(datetime.datetime.now())) |
|
|
|
|
|
with open(FILE, 'w') as configfile: |
|
|
|
|
|
config.write(configfile) |
|
|
|
|
|
force_update = True |
|
|
|
|
|
if ph_status and abs(ph_leer - ALARM_LEVEL_CM) < ph: |
|
|
|
|
|
print(f"{Fore.RED}pH-Minus-Tank fast leer!{Style.RESET_ALL}") |
|
|
|
|
|
if ph_mail == 0: |
|
|
|
|
|
print("Sende E-Mail...") |
|
|
|
|
|
send_email("pH-Minus", abs(ph_leer - ph)) |
|
|
|
|
|
ph_mail = 1 |
|
|
|
|
|
config.set('pH-', 'mail', str(ph_mail)) |
|
|
|
|
|
config.set('pH-', 'datum_mail', str(datetime.datetime.now())) |
|
|
|
|
|
with open(FILE, 'w') as configfile: |
|
|
|
|
|
config.write(configfile) |
|
|
|
|
|
force_update = True |
|
|
time.sleep(0.5) |
|
|
time.sleep(0.5) |
|
|
except KeyboardInterrupt: |
|
|
except KeyboardInterrupt: |
|
|
shutdown(epd) |
|
|
shutdown(epd) |