diff --git a/.xinitrc b/.xinitrc new file mode 100644 index 0000000..9d649d8 --- /dev/null +++ b/.xinitrc @@ -0,0 +1,3 @@ +#ln -s /home/pi/software/thermostat/esp8266_get.php /tmp/esp8266_get.php +sleep 5 +/home/pi/software/thermostat/gui.py diff --git a/dallas_names b/dallas_names new file mode 100644 index 0000000..09819ad --- /dev/null +++ b/dallas_names @@ -0,0 +1,3 @@ +28-0316b5e979ff - detsky pokoj +28-0416b35d80ff - PC stul +28-0516c110d6ff - obyvak diff --git a/esp8266_get.php b/esp8266_get.php new file mode 100644 index 0000000..a8799f0 --- /dev/null +++ b/esp8266_get.php @@ -0,0 +1,32 @@ + diff --git a/get_data.py b/get_data.py new file mode 100755 index 0000000..ad45221 --- /dev/null +++ b/get_data.py @@ -0,0 +1,164 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import serial, time, os.path, urllib, re +#from urllib import urlopen + +debug = True + +path_to_files = '/tmp/' + +serial_port = "/dev/ttyUSB0" +serial_rate = 115200 +arduino = serial.Serial(serial_port, serial_rate) +#arduino = serial.Serial(port = None) +#arduino.port = serial_port +#arduino.baudrate = serial_rate +arduino.bytesize = serial.EIGHTBITS +arduino.parity = serial.PARITY_NONE +arduino.stopbits = serial.STOPBITS_ONE +arduino.timeout = 2 +arduino.xonxoff = False +arduino.rtscts = False +arduino.dsrdtr = False +arduino.writeTimeout = 0 +time.sleep(2.5) +#arduino.open() +arduino.flushInput() +arduino.flushOutput() +arduino.write("t".encode()) +#arduino.close() +time.sleep(2.5) + +main_temp_values = [None] * 2 +humidity_values = [None] * 2 +voltage_values = [None] * 2 + +temps_values = [None] * 3 +dallas_address = [ +"28-0416b35d80ff", # PC stul +"28-0516c110d6ff", # Skrinka +"28-0316b5e979ff", # Detsky pokoj +] + +# Print debug function +def debug_print(text): + if debug: + print(text) + +while 1: + debug_print("DEBUG: Reading data") + + if arduino.isOpen(): + debug_print("DEBUG: Arduino connected") + arduino.flushInput() + arduino.flushOutput() + arduino.write("t".encode()) + for x in range(0, 1): # Can be deleted after transitioning to BME (or other external temp) + output = arduino.readline().rstrip("\r\n".encode()) + debug_print("DEBUG: Arduino temperature " + str(x) + ": " + output.decode()) + if float(output) <= 70 and float(output) >= -50: + main_temp_values[x] = round(float(output), 1) + else: + main_temp_values[x] = u"---" + local_temps_file = open(path_to_files + 'local_temps', 'w') + local_temps_file.write("%s\n" % main_temp_values[x]) + local_temps_file.close() + arduino.readline() + arduino.write("h".encode()) + for x in range(0, 1): # Can be deleted after transitioning to BME (or other external humidity) + output = arduino.readline().rstrip("\r\n".encode()) + debug_print("DEBUG: Arduino humidity " + str(x) + ": " + output.decode()) + if float(output) <= 100 and float(output) >= 0: + humidity_values[x] = round(float(output), 1) + else: + humidity_values[x] = u"---" + humidity_file = open(path_to_files + 'local_humidity', 'w') + humidity_file.write("%s\n" % humidity_values[x]) + humidity_file.close() + arduino.readline() + + # Teploty z kotelny + temps_file = open(path_to_files + 'temps', 'w') + try: + data = urllib.urlopen("http://control.pavoukovo.cz/temps").read().decode() # Otevrit soubor + if data.find("html") == -1: + temps_file.write(data) + debug_print("DEBUG: HTTP temps read ok") + else: + for i in range(0, 8): + temps_file.write("%s\n" % u"---") # Kdyz se nepodari otevrit soubor, zapsat --- + debug_print("DEBUG: " + time.strftime("%H:%M:%S") + " HTTP temps read failed, remote file not found") + except: + for i in range(0, 8): + temps_file.write("%s\n" % u"---") # Kdyz se nepodari otevrit soubor, zapsat --- + debug_print("DEBUG: " + time.strftime("%H:%M:%S") + " HTTP temps read failed") + temps_file.close() + + # Udaje z bazenu/meteobudky + meteo_file = open(path_to_files + 'meteo', 'w') + try: + data = urllib.urlopen("http://meteo.pavoukovo.cz/meteo").read().decode() # Otevrit soubor + if data.find("html") == -1: + meteo_file.write(data) + debug_print("DEBUG: HTTP meteo read ok") + else: + for i in range(0, 7): + meteo_file.write("%s\n" % u"---") # Kdyz se nepodari otevrit soubor, zapsat --- + debug_print("DEBUG: " + time.strftime("%H:%M:%S") + " HTTP meteo read failed, remote file not found") + except: + for i in range(0, 7): + meteo_file.write("%s\n" % u"---") # Kdyz se nepodari otevrit soubor, zapsat --- + debug_print("DEBUG: " + time.strftime("%H:%M:%S") + " HTTP meteo read failed") + meteo_file.close() + + # Rychlost vetru + wind_file = open(path_to_files + 'wind', 'w') + try: + data = urllib.urlopen("http://wind.pavoukovo.cz/wind").read().decode() # Otevrit soubor + if data.find("html") == -1: + wind_file.write(data) + debug_print("DEBUG: HTTP wind read ok") + else: + for i in range(0, 5): + wind_file.write("%s\n" % u"---") # Kdyz se nepodari otevrit soubor, zapsat --- + debug_print("DEBUG: " + time.strftime("%H:%M:%S") + " HTTP wind read failed, remote file not found") + except: + for i in range(0, 5): + wind_file.write("%s\n" % u"---") # Kdyz se nepodari otevrit soubor, zapsat --- + debug_print("DEBUG: " + time.strftime("%H:%M:%S") + " HTTP wind read failed") + wind_file.close() + + # Dallas teplotni cidla + for i in range(0, len(dallas_address)): # Smycka pro cteni podle zadanych adres + try: + temp_file = open('/sys/bus/w1/devices/' + dallas_address[i] + '/w1_slave', 'r') # Otevrit soubor s daty + file_lines = temp_file.read().splitlines() + crc = re.compile('crc=.. (.*)') + crc_value = crc.search(file_lines[0]) + debug_print(crc_value.group(1)) + if crc_value.group(1) == "YES": # Overit kontrolni soucet + temp = re.compile('t=(.*)') + temp_value = temp.search(file_lines[1]) + debug_print(temp_value.group(1)) + if temp_value.group(1) != "85000": # Pokud neukazuje 85 stupnu, coz je chybovy stav + temps_values[i] = float(temp_value.group(1)) / 1000 # Ulozit teplotu + else: + temps_values[i] = u"---" # Kdyby ukazoval 85, tak ulozit --- + else: + temps_values[i] = u"---" # Kdyz neni kontrolni souce v poradku, zapsat --- + except: + temps_values[i] = u"---" # Kdyz se nepovede otevrit soubor s daty, zapsat --- + debug_print("DEBUG: " + "/sys/bus/w1/devices/" + dallas_address[i] + "/w1_slave" + " Temp read failed") + dallas_file = open(path_to_files + 'dallas', 'w') # Otevrit soubor pro zapsani teplot + for i in range(0, len(temps_values) + 1): + if i < len(temps_values) and temps_values[i] == u"---": # Kdyz je misto teploty ulozeno ---, nejde zaokrouhlit (aby nespadl program) + dallas_file.write("%s\n" % temps_values[i]) # Ulozit rovnou --- + elif i < len(temps_values): + dallas_file.write("%s\n" % round(temps_values[i], 1)) # Ulozit zaokrouhlenou teplotu + elif main_temp_values[0] == u"---" or (u"---" in temps_values and i == len(temps_values)): # Vyjimka, pokud je nekde ulozeno ---, nepocitat prumer (aby nespadl program) + dallas_file.write("%s\n" % u"---") + elif i == len(temps_values): # DO posledniho radku ulozit prumer teplot + dallas_file.write("%s\n" % round((temps_values[0] + temps_values[1] + main_temp_values[0]) / 3, 1)) # Prumer se pocita jen z teplot v obyvaku a "hlavni teploty" (na vypinaci v kuchyni) + dallas_file.close() + time.sleep(5) diff --git a/gui.py b/gui.py new file mode 100755 index 0000000..c0aac08 --- /dev/null +++ b/gui.py @@ -0,0 +1,381 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import pygame, sys, datetime, time, serial, os.path, codecs, math +from pygame.locals import * +from time import strftime + +wind_angle = 15 + +debug = 0 +path_to_files = '/tmp/' + +window_width = 480 +window_height = 320 + +pygame.init() +clock = pygame.time.Clock() +display = pygame.display.set_mode((0, 0), pygame.FULLSCREEN|pygame.HWSURFACE|pygame.DOUBLEBUF) + +# Colors +black = (0, 0, 0) +white = (255, 255, 255) +red = (255, 0, 0) +red_dark = (63, 0, 0) +green = (0, 255, 0) +green_dark = (0, 63, 0) +grey_dark = (63, 63, 63) + +border = 20 +main_temp_vertical_divider = 300 +divider_border = border / 2 +degrees_text_offset = 40 +main_temp_text_offset = 150 + + +# Text sizes +size_text_header = 18 +size_text_main_temp = 100 +size_text_degrees = 30 +size_text_humidity = 20 +size_text_clock = 32 +size_text_status = 14 +size_text_default = 14 +size_text_button = 18 +size_space_small = 8 +size_space_large = 10 + +# Text fonts +text_header = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_header) +text_main_temp = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_main_temp) +text_degrees = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_degrees) +text_humidity = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_humidity) +text_clock = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_clock) +text_status = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_status) +text_default = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_default) +text_button = pygame.font.Font('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', size_text_button) + +# State of buttons +buttons_pressed = [0, 0, 0, 0] + +main_temp_names = [u"Uvnitř", u"Venku"] +temp_names = [None] * 8 # Will get filled from reading a file + +# Values arrays/lists +main_temp_values = [None] * 2 +current_main_temp_values = [None] * 2 +humidity_values = [None] * 2 +current_humidity_values = [None] * 2 +temp_values = [None] * 8 +current_temp_values = [None] * 8 +wind_values = [None] * 5 +current_wind_values= [None] * 5 + +heating_time = 1800 # How long to run heating, in seconds +heating_time_end = heating_time # When the heating should end +heating_time_show = 0 # What to show on button + +# Where we are (navigation); 0 = "screensaver", 1 = Showing temps + time + buttons +window_type = 1 + +draw_static_gui = 1 # Very probably is not used +pygame.mouse.set_pos(0, 0) # Resets the mouse when starting the GUI +x, y = (0, 0) + +# Whole section may not be needed +previous_time = 0 +previous_date = 0 +previous_redraw_time = 0 + +# Print debug +def debug_print(text): + if debug: + print(text) + +# Function to use various text +def text_objects(text, font, color): # (what to show, what font to use, what color should it be) - selects from presets + textSurface = font.render(text, True, color) + return textSurface, textSurface.get_rect() + +# Draws buttons + sets their states +def button_on_off(index, text_line1, text_line2, color_text, position_x, position_y, width, height, color_off, color_off_border, color_on, color_on_border): + global x, y + button = pygame.Rect(position_x, position_y, width, height) + #x, y = event.pos + if button.collidepoint(x, y): + if buttons_pressed[index] == 0: + buttons_pressed[index] = 1 + else: + buttons_pressed[index] = 0 + x, y = (0, 0) + if buttons_pressed[index] == 1: + button_background_rect = pygame.draw.rect(display, color_on_border, (position_x, position_y, width, height)); + pygame.draw.rect(display, color_on, (position_x + 2, position_y + 2, width - 4, height - 4)); + else: + button_background_rect = pygame.draw.rect(display, color_off_border, (position_x, position_y, width, height)); + pygame.draw.rect(display, color_off, (position_x + 2, position_y + 2, width - 4, height - 4)); + TextSurf, TextRect = text_objects(text_line1, text_button, color_text) + TextRect.center = (position_x + width / 2, position_y + height / 2 - size_text_button / 2) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(text_line2, text_default, color_text) + TextRect.center = (position_x + width / 2, position_y + height / 2 + size_text_button / 2) + display.blit(TextSurf, TextRect) + #pygame.display.update(button_background_rect) + #pygame.display.update(TextRect) + +# Should be able to use button_on_off instead +def button_default(index, text, color_text, position_x, position_y, width, height, color, color_border): + global window_type + button = pygame.Rect(position_x, position_y, width, height) + x, y = event.pos + if button.collidepoint(x, y): + # předělat na window_type / přepínání­ oken + if buttons_pressed[index] == 0: + buttons_pressed[index] = 1 + else: + buttons_pressed[index] = 0 + button_background_rect = pygame.draw.rect(display, color_border, (position_x, position_y, width, height)); + pygame.draw.rect(display, color, (position_x + 2, position_y + 2, width - 4, height - 4)); + TextSurf, TextRect = text_objects(text + u" (" + str(buttons_pressed[index]) + u")", text_button, color_text) + TextRect.center = (position_x + width / 2, position_y + height / 2) + display.blit(TextSurf, TextRect) + pygame.display.update(button_background_rect) + pygame.display.update(TextRect) + +def compass_line(color, angle, length, width): + if length == "long": + pygame.draw.line(display, color, (main_temp_vertical_divider + border + 65 + round(45 * math.sin(math.radians(angle + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round(45 * math.cos(math.radians(angle + wind_angle)))), (main_temp_vertical_divider + border + 65 + round(30 * math.sin(math.radians(angle + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round(30 * math.cos(math.radians(angle + wind_angle)))), width) + elif length == "short": + pygame.draw.line(display, color, (main_temp_vertical_divider + border + 65 + round(45 * math.sin(math.radians(angle + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round(45 * math.cos(math.radians(angle + wind_angle)))), (main_temp_vertical_divider + border + 65 + round(35 * math.sin(math.radians(angle + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round(35 * math.cos(math.radians(angle + wind_angle)))), width) + +# Rework needed - add repeated reads if one fails; rework may not be needed if FPS is 5 +# 2022 - rework probably not needed, backend reworked instead +def read_data(): + global main_temp_values, humidity_values, temp_values, temp_names, wind_values + + try: + with open(path_to_files + 'local_temps', 'r') as main_temp_file: + main_temp_lines = main_temp_file.read().splitlines() + main_temp_values[0] = main_temp_lines[0] + main_temp_file.close() + #if (len(main_temp_values) == 0): + # main_temp_values[0] = u"---" + except: + main_temp_values[0] = u"---" + debug_print("DEBUG: " + strftime("%H:%M:%S") + " " + path_to_files + "local_temps read failed") + pass + try: + with open(path_to_files + 'meteo', 'r') as meteo_file: + meteo_lines = meteo_file.read().splitlines() + meteo_file.close() + main_temp_values[1] = str(round(float(meteo_lines[2]), 1)) + except: + main_temp_values[1] = u"---" + debug_print("DEBUG: " + strftime("%H:%M:%S") + " " + path_to_files + "meteo temp read failed") + pass + + try: + with open(path_to_files + 'local_humidity', 'r') as humidity_file: + humidity_lines = humidity_file.read().splitlines() + humidity_values[0] = humidity_lines[0] + humidity_file.close() + #if (len(humidity_values) == 0): + # humidity_values[0] = [u"---"]; + except: + humidity_values[0] = u"---" + debug_print("DEBUG: " + strftime("%H:%M:%S") + " " + path_to_files + "local_humidity read failed") + pass + try: + with open(path_to_files + 'meteo', 'r') as meteo_file: + meteo_lines = meteo_file.read().splitlines() + meteo_file.close() + humidity_values[1] = str(round(float(meteo_lines[5]), 1)) + except: + humidity_values[1] = u"---" + debug_print("DEBUG: " + strftime("%H:%M:%S") + " " + path_to_files + "meteo humidity read failed") + pass + + try: + with open(path_to_files + 'wind', 'r') as wind_file: + wind_values = wind_file.read().splitlines() + wind_file.close() + if (len(wind_values) == 0): + wind_values = [u"---"] * 5; + except: + wind_values = [u"---"] * 5; + debug_print("DEBUG: " + strftime("%H:%M:%S") + " " + path_to_files + "wind read failed") + pass + + temp_names_file = codecs.open('/home/pi/software/thermostat/temp_names', 'r', 'utf-8') + temp_names = temp_names_file.read().splitlines() + temp_names_file.close() + + try: + with open(path_to_files + 'temps', 'r') as temp_values_file: + temp_values = temp_values_file.read().splitlines() + temp_values_file.close() + if (len(temp_values) == 0): + temp_values = [u"---"] * 8; + except: + temp_values = [u"---"] * 8; + debug_print("DEBUG: " + strftime("%H:%M:%S") + " " + path_to_files + "temps read failed") + pass + +def write_data(): + global buttons_pressed + buttons_file = open(path_to_files + 'states', 'w') + for i in range(0, len(buttons_pressed)): + buttons_file.write("%s\n" % buttons_pressed[i]) + buttons_file.close() + +def window_main(window_type, mouse_click): + global heating_time_end + read_data() + if window_type == 1: + pygame.draw.rect(display, black, (0, 0, 480, 320)) + # Draw time + # clock_rect = pygame.draw.rect(display, black, (divider_top + border, border + size_text_default, window_width - 2 * border - divider_top, size_text_main_temp)) + #TextSurf, TextRect = text_objects(strftime("%H:%M"), text_default, white) + #TextRect.bottomright = (window_width - border - 100, window_height - border) + #display.blit(TextSurf, TextRect) + #pygame.display.update(clock_rect) + + # Draw date + #date_rect = pygame.draw.rect(display, black, (divider_top + border, border + size_text_default + size_text_clock + size_space_small, window_width - 2 * border - divider_top, size_text_default)) + #TextSurf, TextRect = text_objects(strftime("%d. %m. %Y"), text_default, white) + #TextRect.bottomright = (window_width - border, window_height - border) + #display.blit(TextSurf, TextRect) + #pygame.display.update(date_rect) + + # Out temp + humidity + TextSurf, TextRect = text_objects(main_temp_values[0], text_main_temp, white) + TextRect.topright = (main_temp_vertical_divider - divider_border - degrees_text_offset, 0 + border) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(u"\u00b0" + "C", text_degrees, white) + TextRect.bottomright = (main_temp_vertical_divider - divider_border, 0 + border + size_text_main_temp) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(main_temp_names[0], text_humidity, white) + TextRect.topleft = (main_temp_vertical_divider - divider_border - main_temp_text_offset, 0 + border + size_text_main_temp) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(str(humidity_values[0]) + " %", text_humidity, white) + TextRect.topright = (main_temp_vertical_divider - divider_border, 0 + border + size_text_main_temp) + display.blit(TextSurf, TextRect) + + # In temp + humidity + TextSurf, TextRect = text_objects(main_temp_values[1], text_main_temp, white) + TextRect.topright = (main_temp_vertical_divider - divider_border - degrees_text_offset, 0 + border + size_text_main_temp + size_text_humidity + border + border / 2) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(u"\u00b0" + "C", text_degrees, white) + TextRect.bottomright = (main_temp_vertical_divider - divider_border, 0 + border + + size_text_main_temp + size_text_main_temp + size_text_humidity + border + border / 2) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(main_temp_names[1], text_humidity, white) + TextRect.topleft = (main_temp_vertical_divider - divider_border - main_temp_text_offset, 0 + border + size_text_main_temp + size_text_main_temp + size_text_humidity + border + border / 2) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(str(humidity_values[1]) + " %", text_humidity, white) + TextRect.topright = (main_temp_vertical_divider - divider_border, 0 + border + size_text_main_temp + size_text_main_temp + size_text_humidity + border + border / 2) + display.blit(TextSurf, TextRect) + + # Button for water heating + text on it + button_on_off(0, u"Ohřev vody", u"(" + str(temp_values[4]) + "/" + str(temp_values[5]) + " " + u"\u00b0" + "C)", white, main_temp_vertical_divider + border, 0 + border, 130, 70, red_dark, red, green_dark, green) + + # Button for heating + if heating_time_end != heating_time: + minutes = str(int((heating_time_end - int(time.time())) / 60)) + seconds = (heating_time_end - int(time.time())) % 60 + if seconds < 10: + seconds = "0" + str(seconds) + else: + seconds = str(seconds) + heating_time_show = "(" + minutes + ":" + seconds + ")" + else: + minutes = str(int(heating_time / 60)) + seconds = heating_time % 60 + if seconds < 10: + seconds = "0" + str(seconds) + else: + seconds = str(seconds) + heating_time_show = "(" + minutes + ":" + seconds + ")" + button_on_off(1, u"Ruční topení", heating_time_show, white, main_temp_vertical_divider + border, 0 + border + 70 + border, 130, 70, red_dark, red, green_dark, green) + + # Compass for wind direction and speed + pygame.draw.circle(display, white, (main_temp_vertical_divider + border + 65, 0 + border + 70 + border + 70 + border + 45), 45 , 2) + compass_line(white, 0, "long", 3) + compass_line(white, 45, "short", 1) + TextSurf, TextRect = text_objects("S", text_default, white) + TextRect.center = (main_temp_vertical_divider + border + 65 + round((45 + 10) * math.sin(math.radians(wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round((45 + 10) * math.cos(math.radians(wind_angle)))) + display.blit(TextSurf, TextRect) + compass_line(white, 90, "long", 3) + compass_line(white, 135, "short", 1) + TextSurf, TextRect = text_objects("V", text_default, white) + TextRect.center = (main_temp_vertical_divider + border + 65 + round((45 + 10) * math.sin(math.radians(90 + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round((45 + 10) * math.cos(math.radians(90 + wind_angle)))) + display.blit(TextSurf, TextRect) + compass_line(white, 180, "long", 3) + compass_line(white, 225, "short", 1) + TextSurf, TextRect = text_objects("J", text_default, white) + TextRect.center = (main_temp_vertical_divider + border + 65 + round((45 + 10) * math.sin(math.radians(180 + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round((45 + 10) * math.cos(math.radians(180 + wind_angle)))) + display.blit(TextSurf, TextRect) + compass_line(white, 270, "long", 3) + compass_line(white, 315, "short", 1) + TextSurf, TextRect = text_objects("Z", text_default, white) + TextRect.center = (main_temp_vertical_divider + border + 65 + round((45 + 10) * math.sin(math.radians(270 + wind_angle))), 0 + border + 70 + border + 70 + border + 45 - round((45 + 10) * math.cos(math.radians(270 + wind_angle)))) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects(str(wind_values[3]), text_button, white) + TextRect.midbottom = (main_temp_vertical_divider + border + 65, 0 + border + 70 + border + 70 + border + 45) + display.blit(TextSurf, TextRect) + TextSurf, TextRect = text_objects("(" + str(wind_values[1]) + ")", text_default, white) + TextRect.midtop = (main_temp_vertical_divider + border + 65, 0 + border + 70 + border + 70 + border + 45) + display.blit(TextSurf, TextRect) + + if wind_values[1] != "0.0" and wind_values[0] != u"---": + if int(wind_values[4]) % 90: + compass_line(red, int(wind_values[4]), "long", 5) + else: + compass_line(red, int(wind_values[4]), "long", 5) + + pygame.display.update(); + + +while True: +# global x, y, heating_time, heating_time_end + for event in pygame.event.get(): + if event.type == QUIT: + pygame.quit() + sys.exit() + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + x, y = event.pos + pygame.mouse.set_pos(0, 0) + #window_main(window_type, 1) + + window_main(window_type, 0) + + # Continue running heating time even if in another menu + if buttons_pressed[1] == 0: + heating_time_end = heating_time + elif buttons_pressed[1] == 1 and heating_time_end == heating_time: + heating_time_end = int(time.time()) + heating_time + elif buttons_pressed[1] == 1 and heating_time_end < int(time.time()): + heating_time_end = heating_time + buttons_pressed[1] = 0 + # Write state data in the file + write_data() + + # Ověření překreslování­ (výpis timestamp) +# current_redraw_time = int(time.time()) +# if previous_redraw_time + 5 <= current_redraw_time: +# previous_redraw_time = current_redraw_time +# redraw_rect = pygame.draw.rect(display, black, (0, window_height - size_text_default, 150, size_text_default)) +# TextSurf, TextRect = text_objects(str(current_redraw_time), text_default, white) +# TextRect.topleft = (0, window_height - size_text_default) +# display.blit(TextSurf, TextRect) +# pygame.display.update(redraw_rect) + +# pygame.draw.line(display, white, (divider_top / 2, border), (divider_top / 2, divider_horizontal)) +# pygame.display.update() + + # Show framerate +# TextSurf, TextRect = text_objects(str(clock.get_fps()), text_default, white) +# TextRect.bottomright = (window_width, window_height) +# display.blit(TextSurf, TextRect) +# pygame.display.update() + + clock.tick(10) diff --git a/gui_start b/gui_start new file mode 100755 index 0000000..eb68983 --- /dev/null +++ b/gui_start @@ -0,0 +1,2 @@ +#!/bin/bash +sudo xinit /home/pi/.xinitrc > /tmp/xinit.log 2>&1 diff --git a/package_list b/package_list new file mode 100644 index 0000000..123f5ce --- /dev/null +++ b/package_list @@ -0,0 +1,30 @@ +sudo apt-get install python3-pygame python3-serial +sudo chmod +x /home/pi/software/thermostat/get_data.py +sudo chmod +x /home/pi/software/thermostat/gui.py +sudo chmod +x /home/pi/software/thermostat/gui_start +sudo chmod +x /home/pi/software/thermostat/thermostat_snmp.py +sudo ln -s /home/pi/software/thermostat/thermostat_get_data.service /etc/systemd/system/thermostat_get_data.service +sudo ln -s /home/pi/software/thermostat/thermostat_gui.service /etc/systemd/system/thermostat_gui.service +sudo systemctl enable thermostat_get_data.service +sudo systemctl enable thermostat_gui.service + +xserver-xorg +xserver-xorg-fbturbo +xinit + +cp /boot/config.txt /boot/config.txt.old +cp /boot/cmdline.txt /boot/cmdline.txt.old +git clone https://github.com/waveshare/LCD-show.git +# nano LCD35-show -> zakomentovat reboot +nano -w /boot/cmdline.txt -> zmenit root na sda2 + +(fbcp?) + +sudo apt-get install openvpn +sudo systemctl enable openvpn@client.service + +sudo apt-get install snmpd +ln -s /home/pi/software/thermostat/thermostat_snmp.py /etc/snmp/thermostat.py + +sudo apt-get install xserver-xorg-input-evdev +sudo mv /usr/share/X11/xorg.conf.d/10-evdev.conf /usr/share/X11/xorg.conf.d/45-evdev.conf diff --git a/temp_names b/temp_names new file mode 100644 index 0000000..a6824e6 --- /dev/null +++ b/temp_names @@ -0,0 +1,8 @@ +Voda mix (teplá) +Vracečka čerpadlo +Vracečka klapka +Kotel nahoře +Bojler nahoře +Bojler dole +Solar teplá +Solar studená diff --git a/thermostat_get_data.service b/thermostat_get_data.service new file mode 100644 index 0000000..d52a137 --- /dev/null +++ b/thermostat_get_data.service @@ -0,0 +1,12 @@ +[Unit] +Description=Thermostat get data script + +[Service] +Type=simple +User=root +WorkingDirectory=/home/pi/software/thermostat +ExecStart=/home/pi/software/thermostat/get_data.py +Restart=on-failure + +[Install] +RequiredBy=thermostat_gui.service diff --git a/thermostat_gui.service b/thermostat_gui.service new file mode 100644 index 0000000..b99605f --- /dev/null +++ b/thermostat_gui.service @@ -0,0 +1,13 @@ +[Unit] +Description=Thermostat GUI script +Requires=thermostat_get_data.service + +[Service] +Type=simple +User=root +WorkingDirectory=/home/pi/software/thermostat +ExecStart=/home/pi/software/thermostat/gui_start +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/thermostat_snmp.py b/thermostat_snmp.py new file mode 100755 index 0000000..d6bfb18 --- /dev/null +++ b/thermostat_snmp.py @@ -0,0 +1,98 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import time, os.path + +path_to_files = '/tmp/' + +def read_data(file_name): + global local_temps_values, humidity_values, voltage_values, temps_values, dallas_values + opened_file = open(path_to_files + file_name, 'r') + if file_name == 'local_temps': + local_temps_values = opened_file.read().splitlines() + while not len(local_temps_values): + time.sleep(0.05) + opened_file.seek(0) + local_temps_values = opened_file.read().splitlines() + elif file_name == 'local_humidity': + humidity_values = opened_file.read().splitlines() + while not len(humidity_values): + time.sleep(0.05) + opened_file.seek(0) + humidity_values = opened_file.read().splitlines() +# elif file_name == 'sensor_voltage': +# voltage_values = opened_file.read().splitlines() +# while not len(voltage_values): +# time.sleep(0.05) +# opened_file.seek(0) +# voltage_values = opened_file.read().splitlines() + elif file_name == 'temps': + temps_values = opened_file.read().splitlines() + while not len(temps_values): + time.sleep(0.05) + opened_file.seek(0) + temps_values = opened_file.read().splitlines() + elif file_name == 'dallas': + dallas_values = opened_file.read().splitlines() + while not len(temps_values): + time.sleep(0.05) + opened_file.seek(0) + dallas_values = opened_file.read().splitlines() + opened_file.close() + +def output_data(output_type): + global local_temps_values, humidity_values, voltage_values, temps_values + if output_type == 'local_temps': + for i in xrange(0, 2): +# if local_temps_values[i] != '---' and float(local_temps_values[i]) <= 70 and float(local_temps_values[i]) >= -50: +# print local_temps_values[i] +# else: + print local_temps_values[i] + elif output_type == 'local_humidity': + for i in xrange(0, 2): +# if humidity_values[i] != '---' and float(humidity_values[i]) <= 100 and float(humidity_values[i]) >= 0: +# print humidity_values[i] +# else: + print humidity_values[i] +# elif output_type == 'sensor_voltage': +# for i in xrange(0, 2): +# if voltage_values[i] != '---': +# print voltage_values[i] +# else: +# print voltage_values[i] + elif output_type == 'temps': + for i in xrange(0, 8): + print temps_values[i] + elif output_type == 'dallas': + for i in xrange(0, 4): + print dallas_values[i] + +# No longer used? +while os.path.isfile(path_to_files + 'thermostat.lock'): + time.sleep(0.1); + +#if not os.path.isfile(path_to_files + 'thermostat.lock'): +# lock_file = open(path_to_files + 'thermostat.lock', 'w') +read_data('local_temps') +read_data('local_humidity') +#read_data('sensor_voltage') +read_data('temps') +read_data('dallas') + #temp_file = open(path_to_files + 'local_temps', 'r') + #humidity_file = open(path_to_files + 'local_humidity', 'r') + #voltage_file = open(path_to_files + 'sensor_voltage', 'r') + #temp_values = temp_file.read().splitlines() + #humidity_values = humidity_file.read().splitlines() + #voltage_values = voltage_file.read().splitlines() + #temp_file.close() + #humidity_file.close() + #voltage_file.close() +# lock_file.close() +# os.remove(path_to_files + 'thermostat.lock') +output_data('local_temps') +output_data('local_humidity') +#output_data('sensor_voltage') +output_data('temps') +output_data('dallas') +#else: +# print '0\n0\n0\n0\n0\n0' diff --git a/write_data.py b/write_data.py new file mode 100755 index 0000000..3c4aaeb --- /dev/null +++ b/write_data.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import time, os.path +from datetime import datetime + +debug = True + +path_to_files = '/tmp/' + +# Print debug function +def debug_print(text): + if debug: + print(text) + +temp_kids_room = 0 +temp_average = 0 +hot_water_state = 0 +heating_state = 0 + +# control list: +# 0 - thermostat +# 1 - water valve +control = [0] * 2 + +while 1: + # Open dallas temperatures file + try: + with open(path_to_files + 'dallas', 'r') as dallas_file: + dallas_lines = dallas_file.read().splitlines() + temp_kids_room = dallas_lines[2] + temp_average = dallas_lines[3] + dallas_file.close() + debug_print("DEBUG: Read Dallas ok " + temp_kids_room + " " + temp_average) + except: # Write -1 (error) if file couldn't be opened + temp_kids_room = u"---" + temp_average = u"---" + debug_print("DEBUG: Read Dallas file failed") + # If the file was opened and temps were read correctly + if temp_kids_room != u"---" and temp_average != u"---": + # If temperature is low in kids room or the average is low AND it'đ daytime, start heating + if (float(temp_kids_room) < 20.4 or float(temp_average) < 20.4) and (int(datetime.now().hour) < 20 or int(datetime.now().hour) > 8): + debug_print("STATUS: Temperature low and daytime, heating ON"); + control[0] = 1 + # If both temperatures are good OR it's not daytime, stop heating + elif (float(temp_kids_room) > 20.6 and float(temp_average) > 20.6) or (int(datetime.now().hour) > 20 or int(datetime.now().hour) < 8): + debug_print("STATUS: Temperature ok or night, heating OFF") + control[0] = 0 + else: + # pricist errory + debug_print("DEBUG: Dallas read failed, error logged") + pass + + # Open buttons states file + try: + with open(path_to_files + 'states', 'r') as states_file: + states_lines = states_file.read().splitlines() + hot_water_state = states_lines[0] + heating_state = states_lines[1] + states_file.close() + debug_print("DEBUG: Read states ok " + hot_water_state + " " + heating_state) + except: # Write -1 (error) if file couldn't be opened + hot_water_state = -1 + heating_state = -1 + debug_print("DEBUG: Read states file failed") + # If the file was opened and states are correct (0 or 1) + if hot_water_state != -1 and heating_state != -1: + # podminky na zapnuti vody nebo topeni + debug_print("STATUS: ") + else: + #pricist errory + debug_print("DEBUG: States read failed, error logged") + + # Podminka zkontroluje, jestli je nekde aktivni změna (zapnuto) a jestli je nekde error (tzn. -1); pri vsech errorech zapise 0 (vsechno vypne), kdyz bude jen nekolik erroru a platny pozadavek, zapise pozadavek + #if errory a pozadavky + time.sleep(1)