#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Lit et affiche les données fournies par la station météo complète

#importer les bibliothèques utilisées par le script
import bme680
import time
from datetime import datetime as dt, date
from gpiozero import MCP3008, Button
import statistics
import math
import os
import sys
from influxdb import InfluxDBClient


# Initialise l'intervalle de mesure 300s = 5 * 60s = 5mn
mesure = 300
# Initialise l'intervalle de mesure de vitesse du vent (en secondes)
temps_mesure = 5
# Initialise le temps de pause du script (en secondes)
stop_script = 5

#Crée le capteur BME680 en testant les deux adresses possibles 0x76 et 0x77
try:
    capteur = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
except IOError:
    capteur = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
    
# Paramétrage du suréchantillonnage et du filtre
capteur.set_humidity_oversample(bme680.OS_2X)
capteur.set_pressure_oversample(bme680.OS_4X)
capteur.set_temperature_oversample(bme680.OS_8X)
capteur.set_filter(bme680.FILTER_SIZE_3)


# Connecte le pluviomètre au GPIO 6
pluviometre = Button(6)
# Hauteur de pluie par impulsion du pluviomètre
impulsion_pluie = 0.2794
# Initialise le compteur d'impulsions
compteur_pluie = 0

# Liste pour l'enregistrement des hauteurs de pluie toutes les 5 minutes
cumul_jour_pluie = []
# Cumul journalier du jour précédent
cumul_veille = 0.0

# Connecte l'anémomètre au GPIO 5
anemometre = Button(5)
# Initialise le compteur de tours 
compteur = 0
# Initialise la durée de mesure du vent 5s
vent_mesure = 5

# ===================== Paramétrage BDD =====================

#Définir les paramètres pour accéder à la BDD
USER = 'utilisateur_meteo'
PASSWORD = 'MDP_meteo'
DBNAME = 'meteo'
host='localhost'
port=8086

# Créer une instance de InfluxDBClient pour accéder à la BDD
client = InfluxDBClient(host, port, USER, PASSWORD, DBNAME)

#Créer la liste qui contiendra 
# les points de mesure à envoyer à la BDD
points_mesure = []

# ===================== Fonctions du pluviomètre =====================

# Fonction appelée à chaque impulsion du pluviomètre
# incrémente le compteur à chaque basculement de la cuve
def comptage_pluie():
    global compteur_pluie
    compteur_pluie = compteur_pluie + 1
    
# Fonction qui enregistre la hauteur de pluie dans la liste cumul_jour_pluie
def enregistre_pluie(pluie):
    global cumul_jour_pluie
    cumul_jour_pluie.append(pluie)    

# Fonction utilisée pour la remise à zéro du compteur d'impulsions du pluviomètre  
def reset_compteur_pluie():
    global compteur_pluie
    compteur_pluie = 0
    
# Fonction de calcul de la hauteur de pluie en mm
def hauteur_pluie():
    return (compteur_pluie * impulsion_pluie ) 

# ===================== Fonctions de l'anémomètre =====================    
    
# Liste pour conserver la vitesse du vent
liste_5mn = []

# Fonction appelée à chaque impulsion de l'anémomètre
# incrémente le compteur à chaque demi-tour
def comptage():
    global compteur
    compteur = compteur + 1
    #print("pulse" + str(compteur))

# Fonction utilisée pour la remise à zéro du compteur de vitesse du vent    
def reset_compteur():
    global compteur
    compteur = 0
    
# Fonction de calcul de la vitesse du vent
def vitesse_vent():
    return ((compteur/2)/ vent_mesure *2.4)    
    
# Fonction qui enregistre la vitesse du vent dans une liste_5mn
def enregistre_vitesse(vitesse):
    global liste_5mn
    liste_5mn.append(vitesse)    
    
# ===================== Fonctions de la girouette =====================    
 
 # Liste pour conserver la direction du vent
liste_angles_5mn = []
# Initialise la direction précédente
direction_precedente = 0.0

# Fonction qui compare la tension lue à la liste de valeurs relevées
# Il est possible de régler la plage de comparaison (entre 0.0 et 0.2 environ)
def compare(valeur, tension_lue):
    # Définit la plage dans laquelle la comparaison est faite
    plage = 0.03
    # Vérifie si la tension lue est dans la plage définie
    if ( (tension_lue > (valeur * (1.0 - plage)))  and (tension_lue < (valeur *(1.0 + plage))) ):
        # Si c'est le cas, renvoyer True = Vrai
        return True
    # Si la tension n'a pas été trouvée il y a une erreur 
    # Renvoyer False = Faux    
    return False

# Convertit la tension lue en angle en utilisant
# la fonction compare() ci-dessus 
def conversion_volt_degres(tension_lue, direction_precedente):
    # Si la comparaison est vraie, retourne la valeur de l'angle (16 possibilités)
    # Les tensions utilisées dans la liste sont celles qui ont été relevées 
    # Lors des tests de la girouette
    if (compare(2.86, tension_lue)):
        return 0.0
        
    if (compare(1.87, tension_lue)):    
        return 22.5
        
    if (compare(2.05, tension_lue)): 
        return 45
        
    if (compare(0.50, tension_lue)):     
        return 67.5
        
    if (compare(0.55, tension_lue)):
        return 90.0

    if (compare(0.40, tension_lue)):
        return 112.5

    if (compare(1.00, tension_lue)):
        return 135.0

    if (compare(0.72, tension_lue)):
        return 157.5

    if (compare(1.44, tension_lue)):    
        return 180

    if (compare(1.27, tension_lue)):
        return 202.5

    if (compare(2.51, tension_lue)):
        return 225

    if (compare(2.43, tension_lue)):
        return 247.5

    if (compare(3.17, tension_lue)):
        return 270.0
    
    if (compare(2.94, tension_lue)):
        return 292.5

    if (compare(3.06, tension_lue)): 
        return 315.0
    
    if (compare(2.68, tension_lue)):
        return 337.5
    
    # Si aucun comparaison n'est vraie, il y a une erreur et
    # la fonction retourne la valeur précédente de la direction du vent 
    # On considère qu'il n'y a pas de gros écart entre 2 valeurs consécutives    
    return direction_precedente  
    
# Fonction qui enregistre la direction du vent dans une liste_angles_5mn
def enregistre_direction(direction):
    global liste_angles_5mn
    liste_angles_5mn.append(direction)
    
# Fonction qui calcule la moyenne des directions en fonction du contenu de la liste_angles_5mn    
def direction_moyenne(angles):
        # d'après http://en.wikipedia.org/wiki/Directional_statistics

        somme_sinus = 0.0
        somme_cosinus = 0.0

        for angle in angles:
            r = math.radians(angle)
            somme_sinus += math.sin(r)
            somme_cosinus += math.cos(r)

        nombre_mesures = float(len(angles))
        s = somme_sinus / nombre_mesures
        c = somme_cosinus / nombre_mesures
        arc = math.degrees(math.atan(s/c))
        moyenne_dir_vent = 0.0

        # Gérer la division par zéro
        if c == 0 :
            c = 0.00001

        if s > 0 and c > 0:
            moyenne_dir_vent = arc
        elif c < 0:
            moyenne_dir_vent = arc + 180
        elif s < 0 and c > 0:
            moyenne_dir_vent = arc + 360

        return 0.0 if moyenne_dir_vent == 360 else moyenne_dir_vent

# ===================== Appel des fonctions de comptage anémomètre et pluviomètre ===================== 

# Appeler la fonction de comptage à chaque impulsion
anemometre.when_pressed = comptage

# Appeler la fonction de comptage à chaque impulsion du pluviomètre
pluviometre.when_pressed = comptage_pluie

try:
    # Démarrage du programme de mesure et d'affichage
    # Effacer l'écran
    os.system("clear")
    print("Programme météo démarré : Mesure des valeurs commencée")
    # Boucle infinie du script
    while True:
        # Vider la liste des directions du vent
        liste_angles_5mn = []
        # Vider la liste des vitesses du vent
        liste_5mn = []
        # Remettre le compteur de hauteur de pluie à 0 toutes les 5 minutes
        reset_compteur_pluie()
    
        # Noter l'heure de début de l'intervalle de 5mn
        heure_debut = time.time()
    
        # Enregistrer l'heure de début de l'intervalle de 5mn
        heure_debut = time.time()
        # Début de la temporisation de 5mn => temps pendant lequel on enregistre la pluie
        while time.time() - heure_debut <= mesure:
        
            # Lire l'heure
            heure = dt.now()
            # Remise à zéro de la liste de hauteur de pluie à Minuit
            # La condition sera vraie durant la première minute de la journée
            # Cette partie du script se déclenchera à minuit
            if heure.hour == 0 and heure.minute == 0 and heure.second > 0:
                # Si la liste n'est pas vide faire le cumul et remettre la liste à zéro
                if len(cumul_jour_pluie) > 2:
                    # Calculer le cumul et remettre la liste à zéro
                    cumul_veille = sum(cumul_jour_pluie)
                    cumul_jour_pluie = []
                    # Attendre que la seconde 0 soit terminée pour éviter de multiples exécutions du if
                    time.sleep (2)
    
            # Remettre compteur vitesse du vent à 0
            reset_compteur()
           
            # Début de la mesure de vitesse du vent pendant 5s (temps_mesure)
            time.sleep(temps_mesure)
            # Enregistrer la vitesse du vent 
            enregistre_vitesse(vitesse_vent())
            # Lignes print utilisées pour la mise au point du script
            #print (f'Enregistre 5s {vitesse_vent():.2f} ')
            #print(liste_5mn)
        
            # Lire la direction du vent 
    
            # lire l'entrée A0 du MCP3008
            adc = MCP3008(channel=0)
        
            # Les lignes en commentaire impriment les valeurs pour# Aider à la mise au point du script
            #print("Valeur : ", adc.value)
            #print(f'Tension : {adc.value * 3.3:.3f}v ')
        
            # Calcul de la tension lue en multipliant la valeur sur A0 par 3,3
            tension_lue = adc.value * 3.3
    
            # Retrouver la direction du vent en fonction de la tension lue
            direction = conversion_volt_degres(tension_lue, direction_precedente)
            # Enregistrer la direction actuelle
            # Sera utilisée en cas d'erreur dans la conversion tension => angle
            direction_precedente = direction
            # Enregistrer la direction du vent dans la liste
            enregistre_direction(direction)
       
            #print (direction)
            #print(f'Direction : {direction:.2f} degrés')
            #print(liste_angles_5mn)
        
            # Arrêter le script pour éviter la surcharge du CPU
            time.sleep(stop_script)
            # Effacer l'écran (utile si les print() précedents sont utilisés.
            #os.system("clear")
         
        # Les 5 minutes sont terminées
        
        # Effacer l'écran
        os.system("clear")
        
        # Ajouter la valeur de hauteur de pluie mesurée pendant les 5 minutes à la liste
        enregistre_pluie(hauteur_pluie())

               
        # ======= Mesure et Calcul des valeurs ============
       
        # ====== BME680 ======
        temp_valeur = capteur.data.temperature
        humi_valeur = capteur.data.humidity
        press_valeur = capteur.data.pressure
                    
        # ====== Anémomètre ======
        # Calcul du vent moyen et du vent maxi
        vent_moyen = statistics.mean(liste_5mn)
        vent_max = max(liste_5mn)

        # ====== Girouette ======
        # Calcul de la direction moyenne du vent
        direction = direction_moyenne(liste_angles_5mn)

        # ====== Pluviomètre ======        
        # Calcul de la hauteur de pluie de la veille
        pluie_valeur = hauteur_pluie()
        # Cumul de pluie de la veille
        # Le calcul est fait dans le script à minuit
        # et rangé dans la variable cumul_veille
        
        print("Heure", heure.hour,"H", heure.minute,":", heure.second)
        
        print ("\nMesures BME680")
        print ("==============")
        # Si les données du capteur sont lues
        if capteur.get_sensor_data():
            # Afficher la température
            print(f'Température : \t{temp_valeur:.2f} °C')
            # Afficher la pression
            print(f'Pression : \t{press_valeur:.2f} hPa')
            # Afficher l'humidité relative
            print(f'Humidité : \t{humi_valeur:.2f} %HR')

        print ("\nMesures Anémomètre")
        print ("==================")
    
        # Calculer et afficher la vitesse moyenne du vent
        print (f'Vent moyen :    {vent_moyen : .2f} Km/h')
        # Calculer et afficher la vitesse maxi du vent
        print (f'Vent maxi :     {vent_max : .2f} Km/h')
        
        print ("\nMesures Girouette")
        print ("==================")
         
        # Afficher la direction moyenne du vent pendant les 5 minutes précédentes.
        print (f'Direction moyenne : {direction : .2f} degrés')

        print ("\nMesures Pluviomètre")
        print ("===================")
        
        print (f'Hauteur de pluie des 5 minutes : {pluie_valeur:.2f}mm')
        print (f'Cumul de pluie de la veille : {cumul_veille : .0f}mm')

        # ============== Envoi des données à la BDD meteo ==================
        
        point = {
            "measurement": 'totale',
            "fields": {
                "Pression": press_valeur,
                "Temperature" : temp_valeur,
                "Humidité" : humi_valeur,
                "Vent_moyen" : vent_moyen,
                "Vent_maxi" : vent_max,
                "Direction_moyenne" : direction,
                "Pluie" : pluie_valeur,
                "cumul pluie" : cumul_veille
            }
        }
       #Ajouter le point à la liste
        points_mesure.append(point)
    
        print ("Envoi à la BDD")
   
        #Ecrire la liste de points dans la base de données
        client.write_points(points_mesure)
        time.sleep(1)
  
                
# Sortir du script sur CTRL+C
except KeyboardInterrupt:
    os.system("clear")
    sys.exit()



