import os import site import json import datetime import logging from threading import Thread from time import sleep from flask import Flask from flask import render_template from flask import make_response from flask import request from w1thermsensor import W1ThermSensor import RPi.GPIO as GPIO sensor = W1ThermSensor() water_pin = 22 #17/27/22 heat_pin = 26 heat_state = False PACKAGE_PATH = site.getsitepackages()[0] CONFIG_FILE = os.path.join(PACKAGE_PATH, "greenhouse/config/greenhouse.json") GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(water_pin, GPIO.OUT) log_level = logging.INFO LOG_FILE = "/var/log/greenhouse.log" LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s" # logging.basicConfig(format=LOG_FORMAT, level=log_level, filename=LOG_FILE) logging.basicConfig(format=LOG_FORMAT, level=log_level) log = logging.getLogger('greenhouse') class Heat(Thread): def __init__(self, pin): super(Heat, self).__init__() self.__pin = pin self.__state = False GPIO.setup(pin, GPIO.OUT) if GPIO.input(pin): self.__state = True self.__run_condition = True self.__next_update = datetime.datetime.now() def on(self): self.__state = True GPIO.output(self.__pin, 0) def off(self): self.__state = False GPIO.output(self.__pin, 1) def run(self): self.__next_update = datetime.datetime.now() while self.__run_condition: now = datetime.datetime.now() if now >= self.__next_update: if self.__state: # Do a power cycle to prevent auto-poweroff GPIO.output(self.__pin, 1) sleep(5) GPIO.output(self.__pin, 0) self.__next_update = now + datetime.timedelta(minutes=5) sleep(1) def stop(self): self.__run_condition = False self.join() def state(self): return self.__state heat = Heat(heat_pin) heat.start() class GreenControl(Thread): def __init__(self, cfg_file:str): super(GreenControl, self).__init__() self.cfg_file = cfg_file self.config = {} self.__run_condition = True self.__water_state = False self.__trigger_read_config = True def reload_config(self): self.__trigger_read_config = True def run(self): global sensor global heat next_update = datetime.datetime.now() while self.__run_condition: if self.__trigger_read_config: self.__trigger_read_config = False with open(self.cfg_file, "r") as f: self.config = json.load(f) now = datetime.datetime.now() if now >= next_update: if self.config['heat']['state']: temperature = sensor.get_temperature() if float(temperature) < float(self.config['heat']['on_temperature']) and not heat.state(): heat.on() log.info("Switch heat on by temperature level: %.1f °C", float(temperature)) elif float(temperature) > float(self.config['heat']['off_temperature']) and heat.state(): heat.off() log.info("Switch heat off by temperature level: %.1f °C", float(temperature)) elif heat.state(): # Do a power cycle to prevent auto-poweroff heat.off() sleep(5) heat.on() next_update = now + datetime.timedelta(minutes=5) if self.config['water']['state']: index = 0 if int(now.hour) >= 12: index = 1 on_time_pattern = self.config['water']['times'][index]['on_time'] on_time_pattern = on_time_pattern.split(':') on_time = now.replace(hour=int(on_time_pattern[0]), minute=int(on_time_pattern[1]), second=0, microsecond=0) off_time_pattern = self.config['water']['times'][index]['off_time'] off_time_pattern = off_time_pattern.split(':') off_time = now.replace(hour=int(off_time_pattern[0]), minute=int(off_time_pattern[1]), second=0, microsecond=0) if now > on_time and now <= off_time and not self.__water_state: GPIO.output(water_pin, 0) self.__water_state = True log.info("Switch water on by time") elif now > off_time and self.__water_state: GPIO.output(water_pin, 1) self.__water_state = False log.info("Switch water off by time") sleep(1) green_ctrl = GreenControl(CONFIG_FILE) green_ctrl.start() app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/config') def config(): return render_template('config.html') @app.route('/cfg', methods=['GET']) def get_config(): res = {} with open(CONFIG_FILE, "r") as f: res = json.load(f) return res @app.route('/cfg', methods=['PATCH']) def patch_config(): global green_ctrl res = make_response("", 500) cfg = json.loads(request.data) with open(CONFIG_FILE, "w") as f: json.dump(cfg, f) res = make_response("", 204) green_ctrl.reload_config() return res @app.route('/sample', methods=['GET']) def get_sample(): global heat global sensor try: temperature = f"{float(sensor.get_temperature()):.1f}" except Exception: temperature = None water_state = False if not GPIO.input(water_pin): water_state = True res = {} res["id"] = str(1) if temperature: res["temperature"] = temperature res["water"] = water_state res["heat"] = heat.state() return res @app.route('/sample', methods=['PATCH']) def patch_sample(): global heat record = json.loads(request.data) if "water" in record: if record["water"]: GPIO.output(water_pin, 0) log.info("Switch water on by button: {}".format(datetime.datetime.now())) else: GPIO.output(water_pin, 1) log.info("Switch water off by button: {}".format(datetime.datetime.now())) if "heat" in record: if record["heat"]: heat.on() log.info("Switch heat on by button: {}".format(datetime.datetime.now())) else: heat.off() log.info("Switch heat off by button: {}".format(datetime.datetime.now())) res = make_response("", 204) return res if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)