import smbus 
import time
import requests
import RPi.GPIO as GPIO 
import telepot
from telepot.loop import MessageLoop

TOKEN = '1629362245:AAFHJUf03n5YQsYKMUR6_yut6mHox0oIUhs' #Zugriffstoken für Telegram Bot

#POWER_DOWN = 0x00
#POWER_ON   = 0x01
#RESET      = 0x07

# Konstanten (Zeit in s)
READ_INTERVAL = 60 #Auslesezeit 
SLEEP_INTERVAL = 0.2 #Zeitlicher Abstand zwischen zwei Messungen 
LIGHT_TRESHOLD = 5 #Schwellenwert für Lichtintensität in Lux
EFF_VOLTAGE = 230 #Angenomenne konstante Netzspannung 
MAX_CURRENT_TRESHOLD = 11 #Maximal zulässige Stromstärke

# Felder für ThingSpeak (Darstellungsfelder auf der Web-Seite)
API_KEY = "LOTRO3WVN9028LIB" #Schlüssel für ThingSpeak
POWER_FIELD = "field1"
CURRENT_FIELD = "field2"
POWER_CONSUMPTION_FIELD = "field3"

#Pins
MOSFET_PIN = 25 #GPIO Pin 25
LIGHT_SENSOR_PIN = 0x23 #Pin für das Signal vom Sensor 

bus = smbus.SMBus(1)

t1bot = telepot.Bot(TOKEN) #Erstellung des Telegram Bots

#Umwandlung des aufgenommenen Lichtwertes zur Darstellung als Lichtintensität in Lux
def convertToNumber(data):
    result=(data[1] + (256 * data[0])) / 1.2
    return (result)

#Messung der Lichtintensität über Lichtsensor 
def readLight(addr=LIGHT_SENSOR_PIN):
    data = bus.read_i2c_block_data(addr,0x20)
    return convertToNumber(data)

#Zählung der gültigen Lichtimpulse über die Zeit, die durch die Konstanten gegeben ist
def readLightOverTime():
    ticks = 0
    times = int(READ_INTERVAL/SLEEP_INTERVAL)
    for i in range(0, times):
        light_intensity = readLight()
        if(light_intensity > LIGHT_TRESHOLD):
            ticks += 1
        time.sleep(SLEEP_INTERVAL)
    return ticks

#Errechnung der umgestzten Leistung innerhalb einer Minute anhand der übergebenen Lichtimpulse 
def calculatePowerFromTicks(ticks):
    return ticks * 3600 / READ_INTERVAL

#Errechnung des Stromverbrauches innerhalb einer Minute anhand der übergebenen Leistung
def calculateCurrentFromPower(power):
    return power / EFF_VOLTAGE

#Errechnung der umgestzten elektrischen Arbeit innerhalb einer Minute anhand der übergebenen Leistung
def calculatePowerConsumptionOverHour(power):
    return power / 1000;

#Funktion, welche ausgeführt wird, falls der Telegram Bot eine Nachricht erhält 
def telebotCallback(msg):
    chat_id = msg['chat']['id']
    command = msg['text']
    if(command=="/start"):
        t1bot.sendMessage(chat_id, "Nutze /register um dich für Warnmeldungen zu registrieren oder /an und /aus um die Garagenbeleuchtung zu bedienen.")
    elif(command=="/register"): #Eintragen der Chat ID des Nutzers in die Datei "users.txt"
        f = open("./users.txt", "a")  
        f.write(str(chat_id) + "\n")
        t1bot.sendMessage(chat_id, "Du hast dich für Warnmeldungen registriert.")
        print("Ein Benutzer hat sich für Warnmeldungen registriert.")
        f.close() #Datei "users.txt" wird geschlossen
    elif(command=="/an"):
        GPIO.output(MOSFET_PIN , GPIO.HIGH) #MOSFET-PIN bekommt 3.3 V HIGH-Signal
        t1bot.sendMessage(chat_id, "Die Garagenbeleuchtung wurde eingeschaltet.")
    elif(command=="/aus"):
        GPIO.output(MOSFET_PIN , GPIO.LOW) #MOSFET-PIN bekommt 0 V LOW-Signal
        t1bot.sendMessage(chat_id, "Die Garagenbeleuchtung wurde ausgeschaltet.")
    else:
        t1bot.sendMessage(chat_id, "Dieses Kommando existiert nicht. Nutze /register /an oder /aus.")

#Funktion, welche eine Warnnachricht an alle Nutzer aus der Registrierungsdatei schickt, falls die Stromstärke den maximal zulässigen Wert überschreitet 
def sendWarnIfCurrentToHigh(current):
    if(current>=MAX_CURRENT_TRESHOLD):
        try:
            f = open("./users.txt", "r")
            for line in f.readlines():
                t1bot.sendMessage(line, "Warnung: Der Stromfluss beträgt " + str(current) + " A. Der Stromfluss ist zu hoch, bitte überprüfen.")
                #Die Warnnachricht beinhaltet auch die aktuelle Stromstärke in A.
            f.close()
        except IOError:
            print("Bisher ist kein Benutzer für Warnmeldungen registriert.") #Wenn die Registrierungsdatei nicht geöffnet werden kann oder keiner registriert wurde
            #wird diese Nachricht in die Konsole geschrieben
            
def main():
    MessageLoop(t1bot, telebotCallback).run_as_thread() #Kontinuierliche Abfrage der Nachrichten vom Server
    GPIO.setmode(GPIO.BCM) #Initialisieren der GPIO Bibliothek
    GPIO.setwarnings(False)
    GPIO.setup(MOSFET_PIN , GPIO.OUT, initial=GPIO.LOW) #Registrierung des Steuerpins für das MOS-Modul mit Initialwert LOW
    GPIO.setup(LIGHT_SENSOR_PIN, GPIO.IN) #Registrierung des Sensorpins für den Lichtsensor als Input
    
    while(True):
        ticks = readLightOverTime() #Dauerhaftes Auslesen des Sensors und errechnen der gefragten Werte
        power = round(calculatePowerFromTicks(ticks), 2) #Leistung auf zwei Nachkommastellen runden 
        current = round(calculateCurrentFromPower(power), 2) #Strom auf zwei Nachkommastellen runden 
        powerConsumption = round(calculatePowerConsumptionOverHour(power),2) #Elektrische Arbeit auf zwei Nachkommastellen runden 
        requests.get("https://api.thingspeak.com/update?api_key=" + API_KEY + "&" + POWER_FIELD + "=" + str(power) + "&" + CURRENT_FIELD + "=" + str(current) +
                     "&" + POWER_CONSUMPTION_FIELD + "=" + str(powerConsumption)) #Übermitteln der aktuellen Werte an ThingSpeak
        sendWarnIfCurrentToHigh(current) #Kontinuierliches Abfragen nach zu hohem Strom
        print("Energiedaten an ThingSpeak geschickt!") 

if __name__=="__main__": #Startpunkt der Main-Methode
    main()
   


  