Spaces:
Running
Running
Streamlit code changed
Browse files- src/streamlit_app.py +340 -38
src/streamlit_app.py
CHANGED
@@ -1,40 +1,342 @@
|
|
1 |
-
import altair as alt
|
2 |
-
import numpy as np
|
3 |
-
import pandas as pd
|
4 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
|
11 |
-
forums](https://discuss.streamlit.io).
|
12 |
-
|
13 |
-
In the meantime, below is an example of what you can do with just a few lines of code:
|
14 |
-
"""
|
15 |
-
|
16 |
-
num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
|
17 |
-
num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
|
18 |
-
|
19 |
-
indices = np.linspace(0, 1, num_points)
|
20 |
-
theta = 2 * np.pi * num_turns * indices
|
21 |
-
radius = indices
|
22 |
-
|
23 |
-
x = radius * np.cos(theta)
|
24 |
-
y = radius * np.sin(theta)
|
25 |
-
|
26 |
-
df = pd.DataFrame({
|
27 |
-
"x": x,
|
28 |
-
"y": y,
|
29 |
-
"idx": indices,
|
30 |
-
"rand": np.random.randn(num_points),
|
31 |
-
})
|
32 |
-
|
33 |
-
st.altair_chart(alt.Chart(df, height=700, width=700)
|
34 |
-
.mark_point(filled=True)
|
35 |
-
.encode(
|
36 |
-
x=alt.X("x", axis=None),
|
37 |
-
y=alt.Y("y", axis=None),
|
38 |
-
color=alt.Color("idx", legend=None, scale=alt.Scale()),
|
39 |
-
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
|
40 |
-
))
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import time
|
5 |
+
import matplotlib.pyplot as plt
|
6 |
+
import datetime
|
7 |
+
from io import BytesIO
|
8 |
+
import base64
|
9 |
+
|
10 |
+
# Set page configuration
|
11 |
+
st.set_page_config(
|
12 |
+
page_title="AI Smart Umbrella System",
|
13 |
+
page_icon="☂️",
|
14 |
+
layout="wide",
|
15 |
+
initial_sidebar_state="expanded"
|
16 |
+
)
|
17 |
+
|
18 |
+
# Define the environmental thresholds for the umbrella system
|
19 |
+
THRESHOLDS = {
|
20 |
+
"temperature": {"open": 32, "close": 25}, # °C
|
21 |
+
"humidity": {"open": 40, "close": 70}, # %
|
22 |
+
"soil_moisture": {"open": 20, "close": 40}, # % VWC
|
23 |
+
"sunlight": {"open": 700, "close": 400} # W/m²
|
24 |
+
}
|
25 |
+
|
26 |
+
class SmartUmbrellaSystem:
|
27 |
+
def __init__(self):
|
28 |
+
self.umbrella_state = "Closed"
|
29 |
+
self.current_readings = {
|
30 |
+
"temperature": 25.0,
|
31 |
+
"humidity": 60.0,
|
32 |
+
"soil_moisture": 35.0,
|
33 |
+
"sunlight": 500
|
34 |
+
}
|
35 |
+
# Initialize empty lists for historical data
|
36 |
+
self.timestamps = []
|
37 |
+
self.temp_history = []
|
38 |
+
self.humidity_history = []
|
39 |
+
self.soil_moisture_history = []
|
40 |
+
self.sunlight_history = []
|
41 |
+
self.umbrella_history = []
|
42 |
+
self.log_messages = []
|
43 |
+
|
44 |
+
# Add initial log message
|
45 |
+
self.add_log("System initialized with umbrella closed")
|
46 |
+
|
47 |
+
def add_log(self, message):
|
48 |
+
"""Add a log message with timestamp"""
|
49 |
+
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
50 |
+
self.log_messages.insert(0, f"{timestamp}: {message}")
|
51 |
+
|
52 |
+
def update_readings(self, readings=None):
|
53 |
+
"""Update sensor readings and determine umbrella state"""
|
54 |
+
if readings:
|
55 |
+
self.current_readings = readings
|
56 |
+
else:
|
57 |
+
# Simulate sensor readings with some variation
|
58 |
+
self.current_readings["temperature"] += np.random.uniform(-1.0, 1.0)
|
59 |
+
self.current_readings["humidity"] += np.random.uniform(-2.0, 2.0)
|
60 |
+
self.current_readings["soil_moisture"] += np.random.uniform(-1.0, 1.0)
|
61 |
+
self.current_readings["sunlight"] += np.random.uniform(-50, 50)
|
62 |
+
|
63 |
+
# Ensure values stay within realistic ranges
|
64 |
+
self.current_readings["temperature"] = max(10, min(45, self.current_readings["temperature"]))
|
65 |
+
self.current_readings["humidity"] = max(20, min(95, self.current_readings["humidity"]))
|
66 |
+
self.current_readings["soil_moisture"] = max(5, min(60, self.current_readings["soil_moisture"]))
|
67 |
+
self.current_readings["sunlight"] = max(0, min(1200, self.current_readings["sunlight"]))
|
68 |
+
|
69 |
+
# Record history
|
70 |
+
current_time = datetime.datetime.now()
|
71 |
+
self.timestamps.append(current_time)
|
72 |
+
self.temp_history.append(self.current_readings["temperature"])
|
73 |
+
self.humidity_history.append(self.current_readings["humidity"])
|
74 |
+
self.soil_moisture_history.append(self.current_readings["soil_moisture"])
|
75 |
+
self.sunlight_history.append(self.current_readings["sunlight"])
|
76 |
+
|
77 |
+
# Keep only the last 100 readings
|
78 |
+
if len(self.timestamps) > 100:
|
79 |
+
self.timestamps.pop(0)
|
80 |
+
self.temp_history.pop(0)
|
81 |
+
self.humidity_history.pop(0)
|
82 |
+
self.soil_moisture_history.pop(0)
|
83 |
+
self.sunlight_history.pop(0)
|
84 |
+
self.umbrella_history.pop(0)
|
85 |
+
|
86 |
+
# Determine umbrella state based on sensor readings
|
87 |
+
previous_state = self.umbrella_state
|
88 |
+
self.evaluate_umbrella_state()
|
89 |
+
|
90 |
+
# Record umbrella state
|
91 |
+
self.umbrella_history.append(1 if self.umbrella_state == "Open" else 0)
|
92 |
+
|
93 |
+
# Log state change if any
|
94 |
+
if previous_state != self.umbrella_state:
|
95 |
+
self.add_log(f"Umbrella state changed from {previous_state} to {self.umbrella_state}")
|
96 |
+
|
97 |
+
def evaluate_umbrella_state(self):
|
98 |
+
"""Evaluate umbrella state based on environmental thresholds"""
|
99 |
+
# Open umbrella when any condition exceeds threshold
|
100 |
+
if (self.current_readings["temperature"] > THRESHOLDS["temperature"]["open"] or
|
101 |
+
self.current_readings["humidity"] < THRESHOLDS["humidity"]["open"] or
|
102 |
+
self.current_readings["soil_moisture"] < THRESHOLDS["soil_moisture"]["open"] or
|
103 |
+
self.current_readings["sunlight"] > THRESHOLDS["sunlight"]["open"]):
|
104 |
+
self.umbrella_state = "Open"
|
105 |
+
|
106 |
+
# Close umbrella when all conditions are below threshold
|
107 |
+
elif (self.current_readings["temperature"] < THRESHOLDS["temperature"]["close"] and
|
108 |
+
self.current_readings["humidity"] > THRESHOLDS["humidity"]["close"] and
|
109 |
+
self.current_readings["soil_moisture"] > THRESHOLDS["soil_moisture"]["close"] and
|
110 |
+
self.current_readings["sunlight"] < THRESHOLDS["sunlight"]["close"]):
|
111 |
+
self.umbrella_state = "Closed"
|
112 |
+
|
113 |
+
def manually_set_umbrella(self, state):
|
114 |
+
"""Manually override umbrella state"""
|
115 |
+
previous_state = self.umbrella_state
|
116 |
+
self.umbrella_state = state
|
117 |
+
self.add_log(f"Manual override: Umbrella state set to {state}")
|
118 |
+
|
119 |
+
# Update umbrella history
|
120 |
+
if len(self.umbrella_history) > 0:
|
121 |
+
self.umbrella_history[-1] = 1 if state == "Open" else 0
|
122 |
+
|
123 |
+
def get_history_dataframe(self):
|
124 |
+
"""Create a dataframe of historical data"""
|
125 |
+
data = {
|
126 |
+
"Timestamp": self.timestamps,
|
127 |
+
"Temperature (°C)": self.temp_history,
|
128 |
+
"Humidity (%)": self.humidity_history,
|
129 |
+
"Soil Moisture (%)": self.soil_moisture_history,
|
130 |
+
"Sunlight (W/m²)": self.sunlight_history,
|
131 |
+
"Umbrella State": ["Open" if state == 1 else "Closed" for state in self.umbrella_history]
|
132 |
+
}
|
133 |
+
return pd.DataFrame(data)
|
134 |
+
|
135 |
+
# Initialize session state for persistent data
|
136 |
+
if 'system' not in st.session_state:
|
137 |
+
st.session_state.system = SmartUmbrellaSystem()
|
138 |
+
st.session_state.simulation_running = False
|
139 |
+
st.session_state.update_interval = 1.0 # seconds
|
140 |
+
|
141 |
+
# Streamlit UI
|
142 |
+
st.title("☂️ AI-Controlled Solar-Powered Smart Umbrella System")
|
143 |
+
|
144 |
+
# Create sidebar for controls
|
145 |
+
with st.sidebar:
|
146 |
+
st.header("System Controls")
|
147 |
+
|
148 |
+
# Manual readings input
|
149 |
+
st.subheader("Manual Sensor Readings")
|
150 |
+
col1, col2 = st.columns(2)
|
151 |
+
with col1:
|
152 |
+
temp = st.number_input("Temperature (°C)", 10.0, 45.0, st.session_state.system.current_readings["temperature"], 0.1)
|
153 |
+
with col2:
|
154 |
+
humidity = st.number_input("Humidity (%)", 20.0, 95.0, st.session_state.system.current_readings["humidity"], 0.1)
|
155 |
+
|
156 |
+
col1, col2 = st.columns(2)
|
157 |
+
with col1:
|
158 |
+
soil_moisture = st.number_input("Soil Moisture (%)", 5.0, 60.0, st.session_state.system.current_readings["soil_moisture"], 0.1)
|
159 |
+
with col2:
|
160 |
+
sunlight = st.number_input("Sunlight (W/m²)", 0, 1200, int(st.session_state.system.current_readings["sunlight"]), 10)
|
161 |
+
|
162 |
+
if st.button("Update Readings"):
|
163 |
+
st.session_state.system.update_readings({
|
164 |
+
"temperature": temp,
|
165 |
+
"humidity": humidity,
|
166 |
+
"soil_moisture": soil_moisture,
|
167 |
+
"sunlight": sunlight
|
168 |
+
})
|
169 |
+
st.success("Readings updated!")
|
170 |
+
|
171 |
+
st.divider()
|
172 |
+
|
173 |
+
# Manual control
|
174 |
+
st.subheader("Manual Umbrella Control")
|
175 |
+
umbrella_control = st.radio("Set Umbrella State", ["Auto", "Force Open", "Force Closed"])
|
176 |
+
|
177 |
+
if umbrella_control == "Force Open":
|
178 |
+
st.session_state.system.manually_set_umbrella("Open")
|
179 |
+
elif umbrella_control == "Force Closed":
|
180 |
+
st.session_state.system.manually_set_umbrella("Closed")
|
181 |
+
|
182 |
+
st.divider()
|
183 |
+
|
184 |
+
# Simulation controls
|
185 |
+
st.subheader("Simulation Controls")
|
186 |
+
st.session_state.update_interval = st.slider("Update Interval (seconds)", 0.5, 5.0, st.session_state.update_interval, 0.1)
|
187 |
+
|
188 |
+
if st.button("Start Simulation" if not st.session_state.simulation_running else "Stop Simulation"):
|
189 |
+
st.session_state.simulation_running = not st.session_state.simulation_running
|
190 |
+
st.success(f"Simulation {'started' if st.session_state.simulation_running else 'stopped'}!")
|
191 |
+
|
192 |
+
# Main content area with multiple sections
|
193 |
+
col1, col2 = st.columns([3, 2])
|
194 |
+
|
195 |
+
# Left column - Current readings and umbrella state
|
196 |
+
with col1:
|
197 |
+
st.header("Current System Status")
|
198 |
+
|
199 |
+
# Umbrella state with icon
|
200 |
+
umbrella_icon = "🌂" if st.session_state.system.umbrella_state == "Closed" else "☂️"
|
201 |
+
st.subheader(f"Umbrella State: {umbrella_icon} {st.session_state.system.umbrella_state}")
|
202 |
+
|
203 |
+
# Create metrics for current readings
|
204 |
+
col_a, col_b, col_c, col_d = st.columns(4)
|
205 |
+
|
206 |
+
with col_a:
|
207 |
+
st.metric(
|
208 |
+
"Temperature (°C)",
|
209 |
+
f"{st.session_state.system.current_readings['temperature']:.1f}",
|
210 |
+
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
|
211 |
+
)
|
212 |
+
|
213 |
+
with col_b:
|
214 |
+
# For humidity, lower is concerning so we reverse the delta
|
215 |
+
st.metric(
|
216 |
+
"Humidity (%)",
|
217 |
+
f"{st.session_state.system.current_readings['humidity']:.1f}",
|
218 |
+
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
|
219 |
+
)
|
220 |
+
|
221 |
+
with col_c:
|
222 |
+
# For soil moisture, lower is concerning so we reverse the delta
|
223 |
+
st.metric(
|
224 |
+
"Soil Moisture (%)",
|
225 |
+
f"{st.session_state.system.current_readings['soil_moisture']:.1f}",
|
226 |
+
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
|
227 |
+
)
|
228 |
+
|
229 |
+
with col_d:
|
230 |
+
st.metric(
|
231 |
+
"Sunlight (W/m²)",
|
232 |
+
f"{st.session_state.system.current_readings['sunlight']}",
|
233 |
+
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
|
234 |
+
)
|
235 |
+
|
236 |
+
# Thresholds table
|
237 |
+
st.subheader("Environmental Thresholds")
|
238 |
+
|
239 |
+
threshold_data = {
|
240 |
+
"Parameter": ["Temperature (°C)", "Humidity (%)", "Soil Moisture (%)", "Sunlight (W/m²)"],
|
241 |
+
"Open Umbrella If": [
|
242 |
+
f"> {THRESHOLDS['temperature']['open']}",
|
243 |
+
f"< {THRESHOLDS['humidity']['open']}",
|
244 |
+
f"< {THRESHOLDS['soil_moisture']['open']}",
|
245 |
+
f"> {THRESHOLDS['sunlight']['open']}"
|
246 |
+
],
|
247 |
+
"Close Umbrella If": [
|
248 |
+
f"< {THRESHOLDS['temperature']['close']}",
|
249 |
+
f"> {THRESHOLDS['humidity']['close']}",
|
250 |
+
f"> {THRESHOLDS['soil_moisture']['close']}",
|
251 |
+
f"< {THRESHOLDS['sunlight']['close']}"
|
252 |
+
]
|
253 |
+
}
|
254 |
+
|
255 |
+
st.table(pd.DataFrame(threshold_data))
|
256 |
+
|
257 |
+
# Right column - System logs
|
258 |
+
with col2:
|
259 |
+
st.header("System Logs")
|
260 |
+
log_container = st.container(height=300)
|
261 |
+
with log_container:
|
262 |
+
for log in st.session_state.system.log_messages:
|
263 |
+
st.text(log)
|
264 |
+
|
265 |
+
# Data visualization section
|
266 |
+
st.header("Data Visualization")
|
267 |
+
|
268 |
+
# Get historical data
|
269 |
+
if st.session_state.system.timestamps:
|
270 |
+
# Create 4 plots for each parameter
|
271 |
+
fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(5, 1, figsize=(10, 12), sharex=True)
|
272 |
+
|
273 |
+
# Format timestamps for display
|
274 |
+
formatted_times = [t.strftime("%H:%M:%S") for t in st.session_state.system.timestamps]
|
275 |
+
x_ticks = range(0, len(formatted_times), max(1, len(formatted_times) // 10))
|
276 |
+
|
277 |
+
# Temperature plot
|
278 |
+
ax1.plot(st.session_state.system.temp_history, 'r-')
|
279 |
+
ax1.axhline(y=THRESHOLDS["temperature"]["open"], color='r', linestyle='--', alpha=0.5)
|
280 |
+
ax1.axhline(y=THRESHOLDS["temperature"]["close"], color='b', linestyle='--', alpha=0.5)
|
281 |
+
ax1.set_ylabel("Temperature (°C)")
|
282 |
+
ax1.grid(True)
|
283 |
+
|
284 |
+
# Humidity plot
|
285 |
+
ax2.plot(st.session_state.system.humidity_history, 'b-')
|
286 |
+
ax2.axhline(y=THRESHOLDS["humidity"]["open"], color='r', linestyle='--', alpha=0.5)
|
287 |
+
ax2.axhline(y=THRESHOLDS["humidity"]["close"], color='b', linestyle='--', alpha=0.5)
|
288 |
+
ax2.set_ylabel("Humidity (%)")
|
289 |
+
ax2.grid(True)
|
290 |
+
|
291 |
+
# Soil moisture plot
|
292 |
+
ax3.plot(st.session_state.system.soil_moisture_history, 'g-')
|
293 |
+
ax3.axhline(y=THRESHOLDS["soil_moisture"]["open"], color='r', linestyle='--', alpha=0.5)
|
294 |
+
ax3.axhline(y=THRESHOLDS["soil_moisture"]["close"], color='b', linestyle='--', alpha=0.5)
|
295 |
+
ax3.set_ylabel("Soil Moisture (%)")
|
296 |
+
ax3.grid(True)
|
297 |
+
|
298 |
+
# Sunlight plot
|
299 |
+
ax4.plot(st.session_state.system.sunlight_history, 'y-')
|
300 |
+
ax4.axhline(y=THRESHOLDS["sunlight"]["open"], color='r', linestyle='--', alpha=0.5)
|
301 |
+
ax4.axhline(y=THRESHOLDS["sunlight"]["close"], color='b', linestyle='--', alpha=0.5)
|
302 |
+
ax4.set_ylabel("Sunlight (W/m²)")
|
303 |
+
ax4.grid(True)
|
304 |
+
|
305 |
+
# Umbrella state plot
|
306 |
+
ax5.step(range(len(st.session_state.system.umbrella_history)), st.session_state.system.umbrella_history, 'k-', where='post')
|
307 |
+
ax5.set_ylim(-0.1, 1.1)
|
308 |
+
ax5.set_yticks([0, 1])
|
309 |
+
ax5.set_yticklabels(["Closed", "Open"])
|
310 |
+
ax5.set_ylabel("Umbrella State")
|
311 |
+
ax5.set_xlabel("Time")
|
312 |
+
ax5.grid(True)
|
313 |
+
|
314 |
+
# Set x-ticks to be timestamps
|
315 |
+
ax5.set_xticks(x_ticks)
|
316 |
+
ax5.set_xticklabels([formatted_times[i] for i in x_ticks], rotation=45)
|
317 |
+
|
318 |
+
plt.tight_layout()
|
319 |
+
st.pyplot(fig)
|
320 |
+
|
321 |
+
# Data table with historical values
|
322 |
+
with st.expander("View Historical Data Table"):
|
323 |
+
st.dataframe(st.session_state.system.get_history_dataframe())
|
324 |
+
|
325 |
+
# Add download button
|
326 |
+
csv = st.session_state.system.get_history_dataframe().to_csv(index=False)
|
327 |
+
b64 = base64.b64encode(csv.encode()).decode()
|
328 |
+
href = f'<a href="data:file/csv;base64,{b64}" download="smart_umbrella_data.csv">Download CSV File</a>'
|
329 |
+
st.markdown(href, unsafe_allow_html=True)
|
330 |
+
else:
|
331 |
+
st.info("No historical data available yet. Start the simulation or update readings manually.")
|
332 |
+
|
333 |
+
# Update system readings periodically if simulation is running
|
334 |
+
if st.session_state.simulation_running:
|
335 |
+
st.session_state.system.update_readings()
|
336 |
+
time.sleep(st.session_state.update_interval)
|
337 |
+
st.experimental_rerun()
|
338 |
|
339 |
+
# Footer
|
340 |
+
st.markdown("---")
|
341 |
+
st.markdown("**AI-Controlled Solar-Powered Smart Umbrella System for Agricultural Technology**")
|
342 |
+
st.markdown("This system uses environmental sensors and AI decision logic to automatically control an umbrella for crop protection.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|