import RPi.GPIO as GPIO import logging import logging.handlers as handlers import time from threading import Thread import cherrypy # https://docs.cherrypy.org/en/latest/tutorials.html#tutorial-1-a-basic-web-application import requests, json import datetime cherrypy.config.update({ 'server.socket_host' : '192.168.178.49', 'server.socket_port' : 8081, }) logger = logging.getLogger('Rotating Log') logger.setLevel(logging.INFO) logHandler = handlers.RotatingFileHandler('/home/pi/garage.log', maxBytes=1000000, backupCount=2) logHandler.setLevel(logging.INFO) logHandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s - %(message)s', '%Y-%m-%d %H:%M:%S')) logger.addHandler(logHandler) PIN_GARAGE_BUTTON = 23 PIN_GATE_BUTTON = 24 PIN_GARAGE = 21 PIN_GATE = 26 PIN_LED = 11 PIN_DOOR_OPEN = 6 PIN_DOOR_CLOSED = 13 PIN_SENSOR_GATE= 19 GPIO.setmode(GPIO.BCM) GPIO.setup(PIN_GARAGE_BUTTON, GPIO.IN, GPIO.PUD_UP) GPIO.setup(PIN_GATE_BUTTON, GPIO.IN, GPIO.PUD_UP) GPIO.setup(PIN_GARAGE, GPIO.OUT) GPIO.setup(PIN_GATE, GPIO.OUT) GPIO.setup(PIN_LED, GPIO.OUT) GPIO.setup(PIN_DOOR_OPEN, GPIO.IN, GPIO.PUD_UP) GPIO.setup(PIN_DOOR_CLOSED, GPIO.IN, GPIO.PUD_UP) GPIO.setup(PIN_SENSOR_GATE, GPIO.IN, GPIO.PUD_UP) def pushover(message): response = requests.post("https://api.pushover.net/1/messages.json", data = { "token": "a9k1uyzypuaz1yszqqdzmp17s6r5q2", "user": "ukja84pd2n2cmxvdrnfwdrf8eo1wc5", "message": message }) logger.info(response) def logpush(message): logger.info(message) Thread(target=pushover,args=[message]).start() # move robomower into safe zone def mower_safety(): try: response = requests.get("http://192.168.178.54/json?cmd=status&user=lewe&pass=nFdcX3sMD115WAap") except requests.exceptions.RequestException as e: logpush("couldn't reach mower (retrieving status)") return None status = json.loads(response.text) if (status["status"]["status"] in [2, 3, 5]): logger.info("mower, freeze") try: #response = requests.get("http://192.168.178.54/json?cmd=mode&mode=job&remotestart=2&user=lewe&pass=nFdcX3sMD115WAap") response = requests.get("http://192.168.178.54/json?cmd=stop&user=lewe&pass=nFdcX3sMD115WAap") except requests.exceptions.RequestException as e: logpush("couldn't reach mower (freeze)") else: if (status["status"]["status"] == 17 and (datetime.datetime.now().hour < 6 or datetime.datetime.now().hour > 13)): logger.info("mower's asleep, leave it alone") else: logger.info("lock mower in garage") try: response = requests.get("http://192.168.178.54/json?cmd=mode&mode=home&user=lewe&pass=nFdcX3sMD115WAap") except requests.exceptions.RequestException as e: logpush("couldn't reach mower (lock)") # robomower back to auto mode after gate has been closed def mower_auto(): logger.info("set mower back to auto mode") try: response = requests.get("http://192.168.178.54/json?cmd=mode&mode=auto&user=lewe&pass=nFdcX3sMD115WAap") except requests.exceptions.RequestException as e: logpush("couldn't reach mower (auto mode)") return None def garage_button(channel): time.sleep(0.01) # el cheapo relay module creates lots of interference, so we check again after a waiting period (see https://electronics.stackexchange.com/a/361793/253533) if channel == 0 or GPIO.input(PIN_GARAGE_BUTTON) == GPIO.LOW: logger.info("trigger motor") GPIO.output(PIN_GARAGE, GPIO.LOW) GPIO.output(PIN_LED, GPIO.HIGH) time.sleep(0.4) GPIO.output(PIN_GARAGE, GPIO.HIGH) GPIO.output(PIN_LED, GPIO.LOW) else: logger.info("flare (motor button)") def gate_button(channel): time.sleep(0.01) if channel == 0 or GPIO.input(PIN_GATE_BUTTON) == GPIO.LOW: toSafety = False if GPIO.input(PIN_SENSOR_GATE) == 0: toSafety = True logger.info("trigger gate") GPIO.output(PIN_GATE, GPIO.HIGH) GPIO.output(PIN_LED, GPIO.HIGH) time.sleep(0.4) GPIO.output(PIN_GATE, GPIO.LOW) GPIO.output(PIN_LED, GPIO.LOW) if toSafety == True: mower_safety() else: mower_auto() else: logger.info("flare (gate button)") GPIO.add_event_detect(PIN_GARAGE_BUTTON, GPIO.FALLING, callback=garage_button, bouncetime=250) GPIO.add_event_detect(PIN_GATE_BUTTON, GPIO.FALLING, callback=gate_button, bouncetime=250) GPIO.output(PIN_GARAGE, GPIO.HIGH) GPIO.output(PIN_GATE, GPIO.LOW) GPIO.output(PIN_LED, GPIO.LOW) class GarageService(object): @cherrypy.expose def index(self): return "Hello World!" # deprecated # @cherrypy.expose # def motor(self): # Thread(target=garage_button,args=[0]).start() # return "trigger garage door" # deprecated # @cherrypy.expose # def remote(self): # Thread(target=gate_button,args=[0]).start() # return "trigger gate" @cherrypy.expose def garage(self, action=None): if (action == "open" and GPIO.input(PIN_DOOR_CLOSED) and not GPIO.input(PIN_DOOR_OPEN)): Thread(target=garage_button,args=[0]).start() return '{ "action": "open" }' elif (action == "close" and GPIO.input(PIN_DOOR_OPEN) and not GPIO.input(PIN_DOOR_CLOSED)): Thread(target=garage_button,args=[0]).start() return '{ "action": "close" }' if (GPIO.input(PIN_DOOR_OPEN) and not GPIO.input(PIN_DOOR_CLOSED)): status = "true" else: status = "false" return '{ "status": ' + status + ' }' @cherrypy.expose def gate(self, action=None): if (action == "open" and not GPIO.input(PIN_SENSOR_GATE)): Thread(target=gate_button,args=[0]).start() return '{ "action": "open" }' elif (action == "close" and GPIO.input(PIN_SENSOR_GATE)): Thread(target=gate_button,args=[0]).start() return '{ "action": "close" }' if GPIO.input(PIN_SENSOR_GATE) == 0: status = "false" else: status = "true" return '{ "status": ' + status + ' }' @cherrypy.expose def all(self): Thread(target=garage_button,args=[0]).start() Thread(target=gate_button,args=[0]).start() return "trigger all" @cherrypy.expose def status(self): return '{ "open": ' + str(GPIO.input(PIN_DOOR_OPEN)) + ', "closed": ' + str(GPIO.input(PIN_DOOR_CLOSED)) + ', "gate": ' + str(GPIO.input(PIN_SENSOR_GATE)) + ' }' cherrypy.quickstart(GarageService()) logger.info('Shutdown') logger.info('Reset GPIO configuration and close') GPIO.cleanup()