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' : '0.0.0.0',
    'server.socket_port' : 8081,
    'log.screen' : False
})

logger = logging.getLogger('Rotating Log')
logger.setLevel(logging.INFO)
logHandler = handlers.RotatingFileHandler('/home/pi/garagelog.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)
        
# DEAKTIVIEREN FÜR WINTERPAUSE:
        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()