Spaces:
Running
Running
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import time | |
import matplotlib.pyplot as plt | |
import datetime | |
from io import BytesIO | |
import base64 | |
# Set page configuration | |
st.set_page_config( | |
page_title="AI Smart Umbrella System", | |
page_icon="☂️", | |
layout="wide", | |
initial_sidebar_state="expanded" | |
) | |
# Define the environmental thresholds for the umbrella system | |
THRESHOLDS = { | |
"temperature": {"open": 32, "close": 25}, # °C | |
"humidity": {"open": 40, "close": 70}, # % | |
"soil_moisture": {"open": 20, "close": 40}, # % VWC | |
"sunlight": {"open": 700, "close": 400} # W/m² | |
} | |
class SmartUmbrellaSystem: | |
def __init__(self): | |
self.umbrella_state = "Closed" | |
self.current_readings = { | |
"temperature": 25.0, | |
"humidity": 60.0, | |
"soil_moisture": 35.0, | |
"sunlight": 500 | |
} | |
# Initialize empty lists for historical data | |
self.timestamps = [] | |
self.temp_history = [] | |
self.humidity_history = [] | |
self.soil_moisture_history = [] | |
self.sunlight_history = [] | |
self.umbrella_history = [] | |
self.log_messages = [] | |
# Add initial log message | |
self.add_log("System initialized with umbrella closed") | |
def add_log(self, message): | |
"""Add a log message with timestamp""" | |
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
self.log_messages.insert(0, f"{timestamp}: {message}") | |
def update_readings(self, readings=None): | |
"""Update sensor readings and determine umbrella state""" | |
if readings: | |
self.current_readings = readings | |
else: | |
# Simulate sensor readings with some variation | |
self.current_readings["temperature"] += np.random.uniform(-1.0, 1.0) | |
self.current_readings["humidity"] += np.random.uniform(-2.0, 2.0) | |
self.current_readings["soil_moisture"] += np.random.uniform(-1.0, 1.0) | |
self.current_readings["sunlight"] += np.random.uniform(-50, 50) | |
# Ensure values stay within realistic ranges | |
self.current_readings["temperature"] = max(10, min(45, self.current_readings["temperature"])) | |
self.current_readings["humidity"] = max(20, min(95, self.current_readings["humidity"])) | |
self.current_readings["soil_moisture"] = max(5, min(60, self.current_readings["soil_moisture"])) | |
self.current_readings["sunlight"] = max(0, min(1200, self.current_readings["sunlight"])) | |
# Record history | |
current_time = datetime.datetime.now() | |
self.timestamps.append(current_time) | |
self.temp_history.append(self.current_readings["temperature"]) | |
self.humidity_history.append(self.current_readings["humidity"]) | |
self.soil_moisture_history.append(self.current_readings["soil_moisture"]) | |
self.sunlight_history.append(self.current_readings["sunlight"]) | |
# Keep only the last 100 readings | |
if len(self.timestamps) > 100: | |
self.timestamps.pop(0) | |
self.temp_history.pop(0) | |
self.humidity_history.pop(0) | |
self.soil_moisture_history.pop(0) | |
self.sunlight_history.pop(0) | |
self.umbrella_history.pop(0) | |
# Determine umbrella state based on sensor readings | |
previous_state = self.umbrella_state | |
self.evaluate_umbrella_state() | |
# Record umbrella state | |
self.umbrella_history.append(1 if self.umbrella_state == "Open" else 0) | |
# Log state change if any | |
if previous_state != self.umbrella_state: | |
self.add_log(f"Umbrella state changed from {previous_state} to {self.umbrella_state}") | |
def evaluate_umbrella_state(self): | |
"""Evaluate umbrella state based on environmental thresholds""" | |
# Open umbrella when any condition exceeds threshold | |
if (self.current_readings["temperature"] > THRESHOLDS["temperature"]["open"] or | |
self.current_readings["humidity"] < THRESHOLDS["humidity"]["open"] or | |
self.current_readings["soil_moisture"] < THRESHOLDS["soil_moisture"]["open"] or | |
self.current_readings["sunlight"] > THRESHOLDS["sunlight"]["open"]): | |
self.umbrella_state = "Open" | |
# Close umbrella when all conditions are below threshold | |
elif (self.current_readings["temperature"] < THRESHOLDS["temperature"]["close"] and | |
self.current_readings["humidity"] > THRESHOLDS["humidity"]["close"] and | |
self.current_readings["soil_moisture"] > THRESHOLDS["soil_moisture"]["close"] and | |
self.current_readings["sunlight"] < THRESHOLDS["sunlight"]["close"]): | |
self.umbrella_state = "Closed" | |
def manually_set_umbrella(self, state): | |
"""Manually override umbrella state""" | |
previous_state = self.umbrella_state | |
self.umbrella_state = state | |
self.add_log(f"Manual override: Umbrella state set to {state}") | |
# Update umbrella history | |
if len(self.umbrella_history) > 0: | |
self.umbrella_history[-1] = 1 if state == "Open" else 0 | |
def get_history_dataframe(self): | |
"""Create a dataframe of historical data""" | |
data = { | |
"Timestamp": self.timestamps, | |
"Temperature (°C)": self.temp_history, | |
"Humidity (%)": self.humidity_history, | |
"Soil Moisture (%)": self.soil_moisture_history, | |
"Sunlight (W/m²)": self.sunlight_history, | |
"Umbrella State": ["Open" if state == 1 else "Closed" for state in self.umbrella_history] | |
} | |
return pd.DataFrame(data) | |
# Initialize session state for persistent data | |
if 'system' not in st.session_state: | |
st.session_state.system = SmartUmbrellaSystem() | |
st.session_state.simulation_running = False | |
st.session_state.update_interval = 1.0 # seconds | |
# Streamlit UI | |
st.title("☂️ AI-Controlled Solar-Powered Smart Umbrella System") | |
# Create sidebar for controls | |
with st.sidebar: | |
st.header("System Controls") | |
# Manual readings input | |
st.subheader("Manual Sensor Readings") | |
col1, col2 = st.columns(2) | |
with col1: | |
temp = st.number_input("Temperature (°C)", 10.0, 45.0, st.session_state.system.current_readings["temperature"], 0.1) | |
with col2: | |
humidity = st.number_input("Humidity (%)", 20.0, 95.0, st.session_state.system.current_readings["humidity"], 0.1) | |
col1, col2 = st.columns(2) | |
with col1: | |
soil_moisture = st.number_input("Soil Moisture (%)", 5.0, 60.0, st.session_state.system.current_readings["soil_moisture"], 0.1) | |
with col2: | |
sunlight = st.number_input("Sunlight (W/m²)", 0, 1200, int(st.session_state.system.current_readings["sunlight"]), 10) | |
if st.button("Update Readings"): | |
st.session_state.system.update_readings({ | |
"temperature": temp, | |
"humidity": humidity, | |
"soil_moisture": soil_moisture, | |
"sunlight": sunlight | |
}) | |
st.success("Readings updated!") | |
st.divider() | |
# Manual control | |
st.subheader("Manual Umbrella Control") | |
umbrella_control = st.radio("Set Umbrella State", ["Auto", "Force Open", "Force Closed"]) | |
if umbrella_control == "Force Open": | |
st.session_state.system.manually_set_umbrella("Open") | |
elif umbrella_control == "Force Closed": | |
st.session_state.system.manually_set_umbrella("Closed") | |
st.divider() | |
# Simulation controls | |
st.subheader("Simulation Controls") | |
st.session_state.update_interval = st.slider("Update Interval (seconds)", 0.5, 5.0, st.session_state.update_interval, 0.1) | |
if st.button("Start Simulation" if not st.session_state.simulation_running else "Stop Simulation"): | |
st.session_state.simulation_running = not st.session_state.simulation_running | |
st.success(f"Simulation {'started' if st.session_state.simulation_running else 'stopped'}!") | |
# Main content area with multiple sections | |
col1, col2 = st.columns([3, 2]) | |
# Left column - Current readings and umbrella state | |
with col1: | |
st.header("Current System Status") | |
# Umbrella state with icon | |
umbrella_icon = "🌂" if st.session_state.system.umbrella_state == "Closed" else "☂️" | |
st.subheader(f"Umbrella State: {umbrella_icon} {st.session_state.system.umbrella_state}") | |
# Create metrics for current readings | |
col_a, col_b, col_c, col_d = st.columns(4) | |
with col_a: | |
st.metric( | |
"Temperature (°C)", | |
f"{st.session_state.system.current_readings['temperature']:.1f}", | |
delta=f"{st.session_state.system.current_readings['temperature'] - THRESHOLDS['temperature']['open']:.1f}" if st.session_state.system.current_readings['temperature'] > THRESHOLDS['temperature']['open'] else None | |
) | |
with col_b: | |
# For humidity, lower is concerning so we reverse the delta | |
st.metric( | |
"Humidity (%)", | |
f"{st.session_state.system.current_readings['humidity']:.1f}", | |
delta=f"{THRESHOLDS['humidity']['open'] - st.session_state.system.current_readings['humidity']:.1f}" if st.session_state.system.current_readings['humidity'] < THRESHOLDS['humidity']['open'] else None | |
) | |
with col_c: | |
# For soil moisture, lower is concerning so we reverse the delta | |
st.metric( | |
"Soil Moisture (%)", | |
f"{st.session_state.system.current_readings['soil_moisture']:.1f}", | |
delta=f"{THRESHOLDS['soil_moisture']['open'] - st.session_state.system.current_readings['soil_moisture']:.1f}" if st.session_state.system.current_readings['soil_moisture'] < THRESHOLDS['soil_moisture']['open'] else None | |
) | |
with col_d: | |
st.metric( | |
"Sunlight (W/m²)", | |
f"{st.session_state.system.current_readings['sunlight']}", | |
delta=f"{st.session_state.system.current_readings['sunlight'] - THRESHOLDS['sunlight']['open']}" if st.session_state.system.current_readings['sunlight'] > THRESHOLDS['sunlight']['open'] else None | |
) | |
# Thresholds table | |
st.subheader("Environmental Thresholds") | |
threshold_data = { | |
"Parameter": ["Temperature (°C)", "Humidity (%)", "Soil Moisture (%)", "Sunlight (W/m²)"], | |
"Open Umbrella If": [ | |
f"> {THRESHOLDS['temperature']['open']}", | |
f"< {THRESHOLDS['humidity']['open']}", | |
f"< {THRESHOLDS['soil_moisture']['open']}", | |
f"> {THRESHOLDS['sunlight']['open']}" | |
], | |
"Close Umbrella If": [ | |
f"< {THRESHOLDS['temperature']['close']}", | |
f"> {THRESHOLDS['humidity']['close']}", | |
f"> {THRESHOLDS['soil_moisture']['close']}", | |
f"< {THRESHOLDS['sunlight']['close']}" | |
] | |
} | |
st.table(pd.DataFrame(threshold_data)) | |
# Right column - System logs | |
with col2: | |
st.header("System Logs") | |
log_container = st.container(height=300) | |
with log_container: | |
for log in st.session_state.system.log_messages: | |
st.text(log) | |
# Data visualization section | |
st.header("Data Visualization") | |
# Get historical data | |
if st.session_state.system.timestamps: | |
# Create 4 plots for each parameter | |
fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(5, 1, figsize=(10, 12), sharex=True) | |
# Format timestamps for display | |
formatted_times = [t.strftime("%H:%M:%S") for t in st.session_state.system.timestamps] | |
x_ticks = range(0, len(formatted_times), max(1, len(formatted_times) // 10)) | |
# Temperature plot | |
ax1.plot(st.session_state.system.temp_history, 'r-') | |
ax1.axhline(y=THRESHOLDS["temperature"]["open"], color='r', linestyle='--', alpha=0.5) | |
ax1.axhline(y=THRESHOLDS["temperature"]["close"], color='b', linestyle='--', alpha=0.5) | |
ax1.set_ylabel("Temperature (°C)") | |
ax1.grid(True) | |
# Humidity plot | |
ax2.plot(st.session_state.system.humidity_history, 'b-') | |
ax2.axhline(y=THRESHOLDS["humidity"]["open"], color='r', linestyle='--', alpha=0.5) | |
ax2.axhline(y=THRESHOLDS["humidity"]["close"], color='b', linestyle='--', alpha=0.5) | |
ax2.set_ylabel("Humidity (%)") | |
ax2.grid(True) | |
# Soil moisture plot | |
ax3.plot(st.session_state.system.soil_moisture_history, 'g-') | |
ax3.axhline(y=THRESHOLDS["soil_moisture"]["open"], color='r', linestyle='--', alpha=0.5) | |
ax3.axhline(y=THRESHOLDS["soil_moisture"]["close"], color='b', linestyle='--', alpha=0.5) | |
ax3.set_ylabel("Soil Moisture (%)") | |
ax3.grid(True) | |
# Sunlight plot | |
ax4.plot(st.session_state.system.sunlight_history, 'y-') | |
ax4.axhline(y=THRESHOLDS["sunlight"]["open"], color='r', linestyle='--', alpha=0.5) | |
ax4.axhline(y=THRESHOLDS["sunlight"]["close"], color='b', linestyle='--', alpha=0.5) | |
ax4.set_ylabel("Sunlight (W/m²)") | |
ax4.grid(True) | |
# Umbrella state plot | |
ax5.step(range(len(st.session_state.system.umbrella_history)), st.session_state.system.umbrella_history, 'k-', where='post') | |
ax5.set_ylim(-0.1, 1.1) | |
ax5.set_yticks([0, 1]) | |
ax5.set_yticklabels(["Closed", "Open"]) | |
ax5.set_ylabel("Umbrella State") | |
ax5.set_xlabel("Time") | |
ax5.grid(True) | |
# Set x-ticks to be timestamps | |
ax5.set_xticks(x_ticks) | |
ax5.set_xticklabels([formatted_times[i] for i in x_ticks], rotation=45) | |
plt.tight_layout() | |
st.pyplot(fig) | |
# Data table with historical values | |
with st.expander("View Historical Data Table"): | |
st.dataframe(st.session_state.system.get_history_dataframe()) | |
# Add download button | |
csv = st.session_state.system.get_history_dataframe().to_csv(index=False) | |
b64 = base64.b64encode(csv.encode()).decode() | |
href = f'<a href="data:file/csv;base64,{b64}" download="smart_umbrella_data.csv">Download CSV File</a>' | |
st.markdown(href, unsafe_allow_html=True) | |
else: | |
st.info("No historical data available yet. Start the simulation or update readings manually.") | |
# Update system readings periodically if simulation is running | |
if st.session_state.simulation_running: | |
st.session_state.system.update_readings() | |
time.sleep(st.session_state.update_interval) | |
st.experimental_rerun() | |
# Footer | |
st.markdown("---") | |
st.markdown("**AI-Controlled Solar-Powered Smart Umbrella System for Agricultural Technology**") | |
st.markdown("This system uses environmental sensors and AI decision logic to automatically control an umbrella for crop protection.") |