import time
import urequests
import machine
import ujson
import dht
from onewire import OneWire
from ds18x20 import DS18X20
import neopixel
import BME280
from machine import ADC, Pin
import network

# ===========================
# Configuration and Constants
# ===========================

# WiFi Configuration
WIFI_SSID = "LITTLE 4795"          # Replace with your WiFi SSID
WIFI_PASSWORD = "U552;m84"  # Replace with your WiFi Password

# ThingSpeak settings
THINGSPEAK_API_KEY = "7BGA6EMJC7QLM8HB"
THINGSPEAK_URL = "https://api.thingspeak.com/update?api_key=7BGA6EMJC7QLM8HB&field1=0"

# Sensor Pins
DHT_PIN = 2        # GPIO4 for DHT22
DS18B20_PIN = 22    # GPIO5 for DS18B20
NEOPIXEL_PIN = 6   # GPIO2 for NeoPixel
I2C_SCL_PIN = 17   # GPIO15 for BME280 SCL
I2C_SDA_PIN = 16   # GPIO14 for BME280 SDA
UV_SENSOR_PIN = 26  # GPIO28 for UV Sensor (Analog)

# NeoPixel configuration
NEOPIXEL_COUNT = 12  # Total number of NeoPixels
UV_LED_RANGE = range(0, 6)      # LEDs 0-7 for UV
TEMP_LED_RANGE = range(6, 12)   # LEDs 8-15 for Temperature

# ThingSpeak rate limit (in seconds)
THINGSPEAK_DELAY = 1

# ===========================
# Initialization
# ===========================

def connect_wifi(ssid, password):
    """
    Connect to a WiFi network.

    :param ssid: WiFi SSID
    :param password: WiFi Password
    """
    wlan = network.WLAN(network.STA_IF)  # Create a station interface
    wlan.active(True)                    # Activate the interface
    if not wlan.isconnected():
        print(f"Connecting to WiFi SSID: {ssid}")
        wlan.connect(ssid, password)     # Connect to the network
        timeout = 20                     # Timeout after 20 seconds
        while not wlan.isconnected() and timeout > 0:
            print("Waiting for connection...")
            time.sleep(1)
            timeout -= 1
    if wlan.isconnected():
        print("Connected to WiFi")
        print("Network Configuration:", wlan.ifconfig())
    else:
        print("Failed to connect to WiFi")

# Connect to WiFi
connect_wifi(WIFI_SSID, WIFI_PASSWORD)

# Initialize DHT22 sensor
dht_sensor = dht.DHT22(Pin(DHT_PIN))

# Initialize DS18B20 sensor
ow = OneWire(Pin(DS18B20_PIN))
ds_sensor = DS18X20(ow)
ds_roms = ds_sensor.scan()
if not ds_roms:
    print("Warning: No DS18B20 devices found.")

# Initialize NeoPixel
np = neopixel.NeoPixel(Pin(NEOPIXEL_PIN), NEOPIXEL_COUNT)

# Initialize BME280 sensor
try:
    i2c_2 = machine.I2C(1, scl=Pin(I2C_SCL_PIN), sda=Pin(I2C_SDA_PIN), freq=40000)
    bme = bme280.BME280(i2c=i2c_2)
except Exception as e:
    print("Error initializing BME280:", e)
    bme = None

# Initialize UV Sensor
try:
    uv_sensor = ADC(UV_SENSOR_PIN)  # Adjust based on your microcontroller
    print("UV sensor initialized.")
except Exception as e:
    print("Error initializing UV sensor:", e)
    uv_sensor = None

# ===========================
# Utility Functions
# ===========================

def set_neopixel_color_uv(uv_intensity):
    """
    Set NeoPixel colors for UV indicators based on UV intensity.

    :param uv_intensity: UV intensity percentage (0-100)
    """
    if uv_intensity is None:
        color = (0, 0, 0)  # Turn off UV NeoPixels if UV level is unavailable
    elif uv_intensity > 75:
        color = (255, 0, 0)  # Red for high intensity
    elif uv_intensity > 25:
        color = (255, 255, 0)  # Yellow for medium intensity
    else:
        color = (255, 0, 255)  # Pink for low intensity

    for i in UV_LED_RANGE:
        np[i] = color
    np.write()

def set_neopixel_color_temp(temperature):
    """
    Set NeoPixel colors for Temperature indicators based on temperature.

    :param temperature: Temperature in Celsius
    """
    if temperature is None:
        color = (0, 0, 0)  # Turn off Temperature NeoPixels if temperature is unavailable
    elif 0 <= temperature <= 9:
        color = (0, 255, 0)  # Green for 0-30°C
    elif 10 <= temperature <= 19:
        color = (255, 255, 0)  # Yellow for 31-39°C
    elif temperature >= 20:
        color = (255, 0, 0)  # Red for 40°C and above
    else:
        color = (0, 0, 255)  # Blue for negative temperatures or unexpected values

    for i in TEMP_LED_RANGE:
        np[i] = color
    np.write()

def read_dht22():
    """
    Read temperature and humidity from DHT22 sensor.

    :return: Tuple of (temperature, humidity) or (None, None) on failure
    """
    try:
        dht_sensor.measure()
        temperature = dht_sensor.temperature()
        humidity = dht_sensor.humidity()
        return temperature, humidity
    except OSError as e:
        print("DHT22 read error:", e)
        return None, None

def read_ds18b20():
    """
    Read temperature from DS18B20 sensor.

    :return: Soil temperature or None on failure
    """
    if not ds_roms:
        return None
    try:
        ds_sensor.convert_temp()
        time.sleep(1)  # Wait for conversion
        soil_temp = ds_sensor.read_temp(ds_roms[0])
        return soil_temp
    except Exception as e:
        print("DS18B20 read error:", e)
        return None

def read_bme280():
    """
    Read pressure from BME280 sensor.

    :return: Pressure in hPa or None on failure
    """
    if not bme:
        return None
    try:
        pressure = bme.pressure
        return pressure
    except Exception as e:
        print("BME280 read error:", e)
        return None

def read_uv_sensor():
    """
    Read UV level from UV sensor.

    :return: UV level percentage (0-100) or None on failure
    """
    if not uv_sensor:
        return None
    try:
        uv_value = uv_sensor.read_u16()  # Read 16-bit ADC value (adjust based on MCU ADC resolution)
        uv_percentage = (uv_value / 65535) * 100  # Convert to percentage (for 16-bit ADC)
        # If your ADC has a different resolution, adjust the divisor accordingly
        return uv_percentage
    except Exception as e:
        print("UV sensor read error:", e)
        return None

def send_thingspeak(temperature, humidity, soil_temp, pressure, uv_level):
    """
    Send sensor data to ThingSpeak.

    :param temperature: Room temperature
    :param humidity: Room humidity
    :param soil_temp: Soil temperature
    :param pressure: Atmospheric pressure
    :param uv_level: UV intensity level
    """
    try:
        # Prepare URL-encoded data
        data = {
            "api_key": THINGSPEAK_API_KEY
        }
        if temperature is not None:
            data["field1"] = temperature
        if humidity is not None:
            data["field2"] = humidity
        if soil_temp is not None:
            data["field3"] = soil_temp
        if pressure is not None:
            data["field4"] = pressure
        if uv_level is not None:
            data["field5"] = uv_level

        # Encode data for application/x-www-form-urlencoded
        encoded_data = "&".join([f"{key}={value}" for key, value in data.items()])

        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        response = urequests.post(THINGSPEAK_URL, data=encoded_data, headers=headers)
        if response.status_code == 200:
            print("Data successfully sent to ThingSpeak.")
        else:
            print(f"ThingSpeak HTTP Error: {response.status_code}")
        response.close()
    except Exception as e:
        print("Error sending data to ThingSpeak:", e)
        
# ===========================
# Main Execution Loop
# ===========================

def main():
    data_counter = 0  # Initialize data sent counter

    try:
        while True:
            # Read sensor data
            temperature, humidity = read_dht22()
            soil_temp = read_ds18b20()
            pressure = read_bme280()
            uv_level = read_uv_sensor()

            # Display sensor readings
            print(f"Temperature: {temperature}°C" if temperature is not None else "Temperature: N/A")
            print(f"Humidity: {humidity}%" if humidity is not None else "Humidity: N/A")
            print(f"Soil Temperature: {soil_temp}°C" if soil_temp is not None else "Soil Temperature: N/A")
            print(f"Pressure: {pressure} hPa" if pressure is not None else "Pressure: N/A")
            print(f"UV Level: {uv_level}%" if uv_level is not None else "UV Level: N/A")

            # Update NeoPixels based on UV intensity and Temperature
            set_neopixel_color_uv(uv_level)
            set_neopixel_color_temp(temperature)

            # Send data to ThingSpeak
            send_thingspeak(temperature, humidity, soil_temp, pressure, uv_level)
            data_counter += 1
            print(f"Data transmission count: {data_counter}")

            # Wait for the specified delay
            time.sleep(THINGSPEAK_DELAY)
    except KeyboardInterrupt:
        print("Program terminated by user.")
    except Exception as e:
        print("An unexpected error occurred:", e)

if __name__ == "__main__":
    main()

Error initializing BME280: name 'bme280' isn't defined