# -*- coding: utf-8 -*-
#!/usr/bin/env python3

import asyncio
import websockets
import json
import time
import random

try:
    import grovepi
    HAS_GROVE = True
except:
    HAS_GROVE = False

# ===== CONFIGURACIÓN SENSOR pH =====
PH_PIN = 0           # A0
VREF = 4.95
PH_OFFSET = 365

# ===== CONFIGURACIÓN DHT =====
DHT_PORT = 4
DHT_TYPE = 0

PORT = 8764
connected = set()


# ===== LECTURA pH CALIBRADA =====
def leer_ph_calibrado():
    if not HAS_GROVE:
        return None

    try:
        buf = []

        # Tomar 10 muestras ADC
        for _ in range(10):
            v = grovepi.analogRead(PH_PIN)
            buf.append(v)
            time.sleep(0.01)

        # Pin flotante / sensor desconectado
        if max(buf) - min(buf) > 300:
            return None

        buf.sort()

        # Promedio de las 6 muestras centrales
        avg_value = sum(buf[2:8]) / 6

        # ADC inválido
        if avg_value < 50 or avg_value > 1000:
            return None

        # Fórmula oficial Crowtall
        ph = 7 - (1000 * (avg_value - PH_OFFSET) * VREF / 59.16 / 1023)

        # pH imposible
        if ph < 0 or ph > 14:
            return None

        return round(ph, 2)

    except Exception as e:
        print("Error lectura pH:", e)
        return None


# ===== LECTURA DHT =====
def leer_dht():
    if not HAS_GROVE:
        return (
            round(20 + random.random() * 10, 2),
            round(30 + random.random() * 40, 2)
        )

    try:
        temp, hum = grovepi.dht(DHT_PORT, DHT_TYPE)
        return temp, hum
    except:
        return None, None


async def handler(ws, path):
    print("Cliente conectado:", ws.remote_address)
    connected.add(ws)
    try:
        async for message in ws:
            print("Mensaje recibido:", message)
    except websockets.exceptions.ConnectionClosed:
        pass
    finally:
        connected.remove(ws)
        print("Cliente desconectado:", ws.remote_address)


async def broadcast_sensor_values():
    while True:
        # TEMPERATURA Y HUMEDAD
        temp_amb, hum_amb = leer_dht()

        # pH CALIBRADO
        ph = leer_ph_calibrado()

        # NPK (aún simulado)
        nitro = round(10 + random.random() * 30, 2)
        fosfo = round(5 + random.random() * 20, 2)
        potas = round(8 + random.random() * 25, 2)

        payload = {
            "temp": temp_amb,
            "hum": hum_amb,
            "valor_adc": ph, 
            "n": nitro,
            "p": fosfo,
            "k": potas,
            "ts": time.time()
        }

        print("Broadcast ->", payload)

        if connected:
            msg = json.dumps(payload)
            for ws in list(connected):
                try:
                    await ws.send(msg)
                except:
                    connected.discard(ws)

        await asyncio.sleep(5)


async def main_async():
    server = await websockets.serve(handler, "0.0.0.0", PORT)
    print(f"Servidor WebSocket escuchando en puerto {PORT}")
    await broadcast_sensor_values()


def main():
    try:
        asyncio.run(main_async())
    except KeyboardInterrupt:
        print("Servidor detenido por usuario")


if __name__ == "__main__":
    main()


