#!/usr/bin/python3.4
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import datetime
import time
import os
from datetime import timedelta

#Setup
GPIO.setwarnings(False)
servoPIN = 21
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN, GPIO.OUT)
p = GPIO.PWM(servoPIN,50) #setting the PWM to use normal RC-Servo


led_red=22  #LED to show heater is on
led_orange=27 #LED to show presence-status
taster=17 #button to cancel errors and to get an extra-time boost, if one is longer awake for example

GPIO.setup(led_red,GPIO.OUT)
GPIO.setup(led_orange,GPIO.OUT)
GPIO.setup(taster,GPIO.IN)

#position of the servo
p_off=5.5
p_on=9
p_mitte=7.5 #mitte means middleposition
servo_pause=.4 #pause of 0,4 seconds between movements

#some variables
persons = False #presenceflag
timesgood = False #time is good to heat
heater = 0
alert = 2 #if something bad has happened this will turn to 1 otherwise 0
release= True #servo will be triggered to make heater either on or off
heater_old= 2 #variable of old cycle
counter=0 #counts the cycles
control_cycle=180 #every x control cycle the actual heater varlue will be put to the servos
excepter=False #if there is an exception in form of button is triggered
time_off=datetime.datetime.now() #actual time

onTimes = [datetime.time(6, 45, 0), datetime.time(9, 0,  0), datetime.time(9, 0,  0)] #several on-times can be put
offTimes= [datetime.time(7,40, 0), datetime.time(22, 50, 0), datetime.time(23, 50, 0)] #off-times means after that moment no presence can trigger the heater to on
ll = len(onTimes) #length of vector

def ledredon(channel): #will be triggered if button is pressed.
        if GPIO.input(taster)==False:
                GPIO.output(led_red,GPIO.HIGH)
                global excepter
                excepter=True
                global time_off
                time_off=datetime.datetime.now()+datetime.timedelta(minutes=45) #now heater will be on for 45 minutes even if no device is connected or time is bad. 

        

def servop(x): #pushes the button. in my case it has to always push the other button first to trigger safely the heater to on or off. thats way it makes to movements for on or off.
        p.start(p_mitte)
        p.ChangeDutyCycle(p_mitte) 
        time.sleep(servo_pause)
        if x==0:
                #p.ChangeDutyCycle(p_off) 
                #time.sleep(servo_pause)
                p.ChangeDutyCycle(p_on) 
                time.sleep(servo_pause)
                p.ChangeDutyCycle(p_off) 
                time.sleep(servo_pause)
        if x==1:
                #p.ChangeDutyCycle(p_on) 
                #time.sleep(servo_pause)
                p.ChangeDutyCycle(p_off) 
                time.sleep(servo_pause)
                p.ChangeDutyCycle(p_on) 
                time.sleep(servo_pause)
        p.ChangeDutyCycle(p_mitte) #middleposition
        time.sleep(servo_pause) 
        p.ChangeDutyCycle(0) #has to be put so that servo doesnt twitch

        

GPIO.add_event_detect(taster, GPIO.FALLING, callback=ledredon) #starts the above event

#program

while True:

    lastdatelog_raw=os.path.getmtime('/home/pi/opt/anwesenheit1') #writes timestamp of the presence-textfile
    lastdatelog=datetime.datetime.fromtimestamp(lastdatelog_raw) #writes only the time without the date
    current_datetime = datetime.datetime.now() #current time and date
    
    current_time = datetime.datetime.now().time() #current time
    
    f_text = open('/home/pi/opt/anwesenheit1', 'r')  #read the presence-textfile
    s_text = f_text.read()
    if ("p: present" in s_text) or ("m: present" in s_text) or ("rudi: present" in s_text) or ("phipp: present" in s_text) or ("flo: present" in s_text):
        persons=True #if minimum one of the residents above is there 
        GPIO.output(led_orange,GPIO.HIGH) #orange led will be turned on
        #print "someones there"
        
    else:
        GPIO.output(led_orange,GPIO.LOW) #orange led is off when nobodys at home

    if "WARNING: could not connect to http://fritz.box" in s_text: #if no connection could be made this string will be found
        alert=1 #somethin bad happened so alert to 1
        persons=False
        GPIO.output(led_orange,GPIO.LOW)
        #print "error connection"
        
    f_text.close #close textfile so that it can be updated 

         #no there are some actions to use different ontimes for different days. workdays the heater goes out earlier. (weekday counts from 0 to 6!)
    if (datetime.datetime.today().weekday()<5): #till friday
        if (current_time >= onTimes[0]) and (current_time <= offTimes[0]):
            timesgood = True
            
    if (datetime.datetime.today().weekday()<4): #till thursday
        if (current_time >= onTimes[1]) and (current_time <= offTimes[1]):
            timesgood = True
            
            
    if (datetime.datetime.today().weekday() == 4) or (datetime.datetime.today().weekday() == 5): #freitag und samstag
        if (current_time >= onTimes[2]) and (current_time <= offTimes[2]):
            timesgood = True

    if (datetime.datetime.today().weekday() == 6): #sunday
        if (current_time >= onTimes[1]) and (current_time <= offTimes[1]):
            timesgood = True

    #if someones there and the time is good, then heater can be put on
    if (persons==True) and (timesgood == True):
        heater=1
        GPIO.output(led_red,GPIO.HIGH) #red led on to show that heater should be on too.
    
    
    elif excepter==True: #if button was pressed heater will be turned on also
        heater=1
        excepter=False

    elif time_off>datetime.datetime.now(): #this belong also to the button. if button was pressed not long ago (in my case45 min) then heater is on. Had to put this too, so that controlcycle doesnt disturb this thing. 
        heater=1

    else: #if nothing is saying heater on, well it goes off.
        heater=0
        GPIO.output(led_red,GPIO.LOW)

    if (lastdatelog < current_datetime-datetime.timedelta(minutes=3)) and alert==0: #if the presence-textfile is too old there is an alert.
        alert=1
        #print "datei zu alt"
        
    if alert==1: #if there is an alert, the heater goes out.
        heater=0
        
    if heater_old==heater: #if no new status was given to heater, then nothing happens, no servo will be moved assuming the heater stays also on. 
        release=False
        counter=counter+1
    else: #if the heaterstatus has indeed changed then the servo should be released.
        release=True
        counter=0

    if counter>control_cycle: #after some control_cycles the servos will also be moved just to make sure.
        release=1
        counter=0

    if (heater==1) and (release==True): #now its time to move the servo if everything is good for that.
        servop(1)
        release=False
        
    elif (heater==0) and (release==True): #also moving the servo but to make the heater out.
        servop(0)
        release=False
        #print "heizung aus"

    if alert==1: #if tthere is an error or alert then the redled will blink and one has to push the button to cancel, then the loop continues or falls again in this alert mode. then you have to check and search for the error
            while GPIO.input(taster)==True:
                    GPIO.output(led_red,GPIO.LOW)
                    time.sleep(0.1)
                    GPIO.output(led_red,GPIO.HIGH)
                    time.sleep(0.1)
            alert=0
            time.sleep(1)
            excepter=False
            time_off=datetime.datetime.now()
            counter=0
            

    heater_old=heater   
    persons=False
    timesgood=False
    time.sleep(30) #loop is triggered every 30 seconds. while control_cycle is set to 180 this means a check every 90 minutes.

p.stop()
