Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,568 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import gradio as gr
|
3 |
+
import paho.mqtt.client as mqtt
|
4 |
+
import time
|
5 |
+
import random
|
6 |
+
from queue import Queue
|
7 |
+
import numpy as np
|
8 |
+
import pandas as pd
|
9 |
+
from datetime import datetime, timedelta
|
10 |
+
import plotly.graph_objects as go
|
11 |
+
|
12 |
+
# Global configuration
|
13 |
+
MQTT_HOST = "broker.hivemq.com"
|
14 |
+
MQTT_PORT = 1883
|
15 |
+
|
16 |
+
# Global state
|
17 |
+
response_queue = Queue()
|
18 |
+
command_queue = Queue()
|
19 |
+
mqtt_ping_client = None
|
20 |
+
mqtt_pong_client = None
|
21 |
+
session_id = None
|
22 |
+
device_state = {
|
23 |
+
"rgb": {"r": 0, "g": 0, "b": 0},
|
24 |
+
"temperature": 25.0,
|
25 |
+
"rpm": 0
|
26 |
+
}
|
27 |
+
|
28 |
+
# Add historical data storage
|
29 |
+
history_data = {
|
30 |
+
"rgb": [],
|
31 |
+
"temperature": [],
|
32 |
+
"timestamps": []
|
33 |
+
}
|
34 |
+
|
35 |
+
# MQTT callback functions
|
36 |
+
def on_ping_connect(client, userdata, flags, rc):
|
37 |
+
print(f"Ping connected with result code {rc}")
|
38 |
+
if session_id:
|
39 |
+
client.subscribe(f"pong/{session_id}/response")
|
40 |
+
|
41 |
+
def on_pong_connect(client, userdata, flags, rc):
|
42 |
+
print(f"Pong connected with result code {rc}")
|
43 |
+
client.subscribe("ping/command")
|
44 |
+
|
45 |
+
def on_ping_message(client, userdata, msg):
|
46 |
+
try:
|
47 |
+
response = json.loads(msg.payload.decode())
|
48 |
+
response_queue.put(response)
|
49 |
+
print(f"Ping received: {response}")
|
50 |
+
except Exception as e:
|
51 |
+
print(f"Ping error: {e}")
|
52 |
+
|
53 |
+
def on_pong_message(client, userdata, msg):
|
54 |
+
try:
|
55 |
+
command = json.loads(msg.payload.decode())
|
56 |
+
command_queue.put(command)
|
57 |
+
print(f"Pong received: {command}")
|
58 |
+
except Exception as e:
|
59 |
+
print(f"Pong error: {e}")
|
60 |
+
|
61 |
+
# Ping functionality
|
62 |
+
def initialize_ping():
|
63 |
+
global mqtt_ping_client, session_id
|
64 |
+
session_id = f"ping_{int(time.time())}"
|
65 |
+
mqtt_ping_client = mqtt.Client()
|
66 |
+
mqtt_ping_client.on_connect = on_ping_connect
|
67 |
+
mqtt_ping_client.on_message = on_ping_message
|
68 |
+
mqtt_ping_client.connect(MQTT_HOST, MQTT_PORT, 60)
|
69 |
+
mqtt_ping_client.loop_start()
|
70 |
+
return f"Ping initialized: {session_id}"
|
71 |
+
|
72 |
+
def send_command(command_type, data=None):
|
73 |
+
if not mqtt_ping_client:
|
74 |
+
return "Please initialize ping first"
|
75 |
+
|
76 |
+
payload = {
|
77 |
+
"type": command_type,
|
78 |
+
"data": data or {},
|
79 |
+
"session_id": session_id,
|
80 |
+
"timestamp": time.time()
|
81 |
+
}
|
82 |
+
mqtt_ping_client.publish("ping/command", json.dumps(payload))
|
83 |
+
return f"Sent {command_type}"
|
84 |
+
|
85 |
+
def send_rgb(r, g, b):
|
86 |
+
if not mqtt_ping_client:
|
87 |
+
return "Please initialize ping first"
|
88 |
+
|
89 |
+
payload = {
|
90 |
+
"type": "RGB Command",
|
91 |
+
"data": {"r": r, "g": g, "b": b},
|
92 |
+
"session_id": session_id,
|
93 |
+
"timestamp": time.time()
|
94 |
+
}
|
95 |
+
# Send command
|
96 |
+
mqtt_ping_client.publish("ping/command", json.dumps(payload))
|
97 |
+
# Put command into command queue for pong display
|
98 |
+
command_queue.put(payload)
|
99 |
+
return f"Sent RGB Command: R={r}, G={g}, B={b}"
|
100 |
+
|
101 |
+
def send_weight_request(rpm):
|
102 |
+
return send_command("Weight Data", {"set_rpm": rpm, "request_weight": True})
|
103 |
+
|
104 |
+
# Pong functionality
|
105 |
+
def initialize_pong():
|
106 |
+
global mqtt_pong_client
|
107 |
+
mqtt_pong_client = mqtt.Client()
|
108 |
+
mqtt_pong_client.on_connect = on_pong_connect
|
109 |
+
mqtt_pong_client.on_message = on_pong_message
|
110 |
+
mqtt_pong_client.connect(MQTT_HOST, MQTT_PORT, 60)
|
111 |
+
mqtt_pong_client.loop_start()
|
112 |
+
return "Pong started"
|
113 |
+
|
114 |
+
def process_command(command):
|
115 |
+
global device_state
|
116 |
+
command_type = command.get("type")
|
117 |
+
data = command.get("data", {})
|
118 |
+
session_id = command.get("session_id")
|
119 |
+
timestamp = datetime.fromtimestamp(command.get("timestamp", time.time()))
|
120 |
+
|
121 |
+
if command_type == "RGB Command":
|
122 |
+
# Update device state
|
123 |
+
device_state = {
|
124 |
+
**device_state,
|
125 |
+
"rgb": {
|
126 |
+
"r": int(data.get("r", 0)),
|
127 |
+
"g": int(data.get("g", 0)),
|
128 |
+
"b": int(data.get("b", 0))
|
129 |
+
}
|
130 |
+
}
|
131 |
+
print(f"Processing RGB command: {data}")
|
132 |
+
response_data = {
|
133 |
+
"current_state": "applied",
|
134 |
+
"power_consumption": random.uniform(0.1, 1.0),
|
135 |
+
"applied_values": device_state["rgb"]
|
136 |
+
}
|
137 |
+
# Record RGB value (using average for simplicity)
|
138 |
+
rgb_avg = sum([int(data.get(k, 0)) for k in ['r', 'g', 'b']]) / 3
|
139 |
+
record_data("rgb", rgb_avg, timestamp)
|
140 |
+
elif command_type == "Temperature Reading":
|
141 |
+
device_state["temperature"] += random.uniform(-0.5, 0.5)
|
142 |
+
response_data = {
|
143 |
+
"current_temperature": device_state["temperature"],
|
144 |
+
"humidity": random.uniform(40, 60),
|
145 |
+
"pressure": random.uniform(980, 1020)
|
146 |
+
}
|
147 |
+
# Record temperature
|
148 |
+
record_data("temperature", device_state["temperature"], timestamp)
|
149 |
+
elif command_type == "Weight Data":
|
150 |
+
if "set_rpm" in data:
|
151 |
+
device_state["rpm"] = data["set_rpm"]
|
152 |
+
response_data = {
|
153 |
+
"calibrated_weight": random.uniform(95, 105),
|
154 |
+
"current_rpm": device_state["rpm"],
|
155 |
+
"stability": random.uniform(0.98, 1.02)
|
156 |
+
}
|
157 |
+
else:
|
158 |
+
response_data = {"error": "Unknown command type"}
|
159 |
+
|
160 |
+
response = {
|
161 |
+
"type": command_type,
|
162 |
+
"data": response_data,
|
163 |
+
"timestamp": time.time(),
|
164 |
+
"session_id": session_id
|
165 |
+
}
|
166 |
+
|
167 |
+
if mqtt_pong_client:
|
168 |
+
mqtt_pong_client.publish(f"pong/{session_id}/response", json.dumps(response))
|
169 |
+
return json.dumps(response, indent=2)
|
170 |
+
|
171 |
+
def check_ping_responses():
|
172 |
+
"""Check the ping response queue"""
|
173 |
+
responses = []
|
174 |
+
while not response_queue.empty():
|
175 |
+
response = response_queue.get_nowait()
|
176 |
+
responses.append(json.dumps(response, indent=2))
|
177 |
+
return "\n".join(responses) if responses else "No new responses"
|
178 |
+
|
179 |
+
def check_pong_commands():
|
180 |
+
"""Check and process command queue"""
|
181 |
+
responses = []
|
182 |
+
while not command_queue.empty():
|
183 |
+
command = command_queue.get_nowait()
|
184 |
+
response = process_command(command)
|
185 |
+
responses.append(response)
|
186 |
+
return "\n".join(responses) if responses else "No new commands"
|
187 |
+
|
188 |
+
# Add stop_mqtt function
|
189 |
+
def stop_mqtt():
|
190 |
+
global mqtt_ping_client, mqtt_pong_client
|
191 |
+
if mqtt_ping_client:
|
192 |
+
mqtt_ping_client.loop_stop()
|
193 |
+
mqtt_ping_client.disconnect()
|
194 |
+
if mqtt_pong_client:
|
195 |
+
mqtt_pong_client.loop_stop()
|
196 |
+
mqtt_pong_client.disconnect()
|
197 |
+
return "Both Ping and Pong clients stopped"
|
198 |
+
|
199 |
+
# Add real-time update functions
|
200 |
+
def update_rgb_preview(r, g, b):
|
201 |
+
"""Real-time update RGB preview values"""
|
202 |
+
global device_state
|
203 |
+
device_state = {
|
204 |
+
**device_state,
|
205 |
+
"rgb": {
|
206 |
+
"r": int(r),
|
207 |
+
"g": int(g),
|
208 |
+
"b": int(b)
|
209 |
+
}
|
210 |
+
}
|
211 |
+
return gr.update(value=device_state)
|
212 |
+
|
213 |
+
def update_rpm_preview(rpm):
|
214 |
+
"""Real-time update RPM preview values"""
|
215 |
+
global device_state
|
216 |
+
device_state = {
|
217 |
+
**device_state,
|
218 |
+
"rpm": int(rpm)
|
219 |
+
}
|
220 |
+
return gr.update(value=device_state)
|
221 |
+
|
222 |
+
def update_temperature_preview(temperature):
|
223 |
+
"""Real-time update temperature preview values"""
|
224 |
+
global device_state
|
225 |
+
device_state = {
|
226 |
+
**device_state,
|
227 |
+
"temperature": float(temperature)
|
228 |
+
}
|
229 |
+
return gr.update(value=device_state)
|
230 |
+
|
231 |
+
# Add data recording and prediction functions
|
232 |
+
def record_data(data_type, value, timestamp):
|
233 |
+
"""Record historical data"""
|
234 |
+
history_data[data_type].append(value)
|
235 |
+
history_data["timestamps"].append(timestamp)
|
236 |
+
# Keep only the last 100 data points
|
237 |
+
if len(history_data[data_type]) > 100:
|
238 |
+
history_data[data_type] = history_data[data_type][-100:]
|
239 |
+
history_data["timestamps"] = history_data["timestamps"][-100:]
|
240 |
+
|
241 |
+
def generate_prediction(data_type):
|
242 |
+
"""Generate simple prediction"""
|
243 |
+
if len(history_data[data_type]) < 2:
|
244 |
+
return None
|
245 |
+
|
246 |
+
# Use simple linear regression for prediction
|
247 |
+
x = np.arange(len(history_data[data_type]))
|
248 |
+
y = np.array(history_data[data_type])
|
249 |
+
z = np.polyfit(x, y, 1)
|
250 |
+
p = np.poly1d(z)
|
251 |
+
|
252 |
+
# Predict the next 5 points
|
253 |
+
future_x = np.arange(len(x), len(x) + 5)
|
254 |
+
future_y = p(future_x)
|
255 |
+
|
256 |
+
return {
|
257 |
+
"historical": history_data[data_type],
|
258 |
+
"predicted": future_y.tolist(),
|
259 |
+
"timestamps": history_data["timestamps"]
|
260 |
+
}
|
261 |
+
|
262 |
+
def plot_data_with_prediction(data_type):
|
263 |
+
"""Create chart with prediction"""
|
264 |
+
if len(history_data[data_type]) < 2:
|
265 |
+
return None
|
266 |
+
|
267 |
+
# Use simple linear regression for prediction
|
268 |
+
x = np.arange(len(history_data[data_type]))
|
269 |
+
y = np.array(history_data[data_type])
|
270 |
+
z = np.polyfit(x, y, 1)
|
271 |
+
p = np.poly1d(z)
|
272 |
+
|
273 |
+
# Predict the next 5 points
|
274 |
+
future_x = np.arange(len(x), len(x) + 5)
|
275 |
+
future_y = p(future_x)
|
276 |
+
|
277 |
+
# Create Plotly chart
|
278 |
+
fig = go.Figure()
|
279 |
+
|
280 |
+
# Add historical data
|
281 |
+
fig.add_trace(go.Scatter(
|
282 |
+
x=[t.strftime('%H:%M:%S') for t in history_data["timestamps"]],
|
283 |
+
y=history_data[data_type],
|
284 |
+
mode='lines+markers',
|
285 |
+
name='Historical'
|
286 |
+
))
|
287 |
+
|
288 |
+
# Add predicted data
|
289 |
+
future_times = [
|
290 |
+
(history_data["timestamps"][-1] + timedelta(minutes=i)).strftime('%H:%M:%S')
|
291 |
+
for i in range(1, 6)
|
292 |
+
]
|
293 |
+
fig.add_trace(go.Scatter(
|
294 |
+
x=future_times,
|
295 |
+
y=future_y,
|
296 |
+
mode='lines',
|
297 |
+
line=dict(dash='dash'),
|
298 |
+
name='Predicted'
|
299 |
+
))
|
300 |
+
|
301 |
+
# Update layout
|
302 |
+
fig.update_layout(
|
303 |
+
title=f"{data_type.upper()} Trend Analysis",
|
304 |
+
xaxis_title="Time",
|
305 |
+
yaxis_title="Value",
|
306 |
+
showlegend=True
|
307 |
+
)
|
308 |
+
|
309 |
+
return fig
|
310 |
+
|
311 |
+
# Add manual refresh function
|
312 |
+
def refresh_all():
|
313 |
+
"""Manually refresh all states"""
|
314 |
+
commands = check_pong_commands()
|
315 |
+
print(f"Current device state: {device_state}")
|
316 |
+
return [commands, gr.update(value=device_state)]
|
317 |
+
|
318 |
+
# Gradio interface
|
319 |
+
with gr.Blocks(title="MQTT Ping-Pong System", theme=gr.themes.Base(
|
320 |
+
primary_hue=gr.themes.colors.Color(
|
321 |
+
c50="#edf2f0",
|
322 |
+
c100="#dbE5E1",
|
323 |
+
c200="#B8CCC6",
|
324 |
+
c300="#96B3AB",
|
325 |
+
c400="#7FA595", # Right main color
|
326 |
+
c500="#5C8072", # Left main color
|
327 |
+
c600="#4A665B",
|
328 |
+
c700="#374D45",
|
329 |
+
c800="#25332E",
|
330 |
+
c900="#121917",
|
331 |
+
c950="#080C0B", # Add this
|
332 |
+
)
|
333 |
+
)) as demo:
|
334 |
+
gr.Markdown("# π MQTT Ping-Pong Communication System <span class='pong-emoji'>π</span>")
|
335 |
+
|
336 |
+
# Add CSS, including font settings
|
337 |
+
gr.HTML("""
|
338 |
+
<style>
|
339 |
+
/* Basic styles */
|
340 |
+
.ping-panel {
|
341 |
+
background-color: #5C8072 !important;
|
342 |
+
border-radius: 8px;
|
343 |
+
padding: 20px;
|
344 |
+
}
|
345 |
+
.pong-panel {
|
346 |
+
background-color: #7FA595 !important;
|
347 |
+
border-radius: 8px;
|
348 |
+
padding: 20px;
|
349 |
+
}
|
350 |
+
|
351 |
+
/* Font settings */
|
352 |
+
* {
|
353 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
354 |
+
"Helvetica Neue", Arial, sans-serif;
|
355 |
+
}
|
356 |
+
|
357 |
+
/* Title styles */
|
358 |
+
h1, h2, h3, .header {
|
359 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
360 |
+
"Helvetica Neue", Arial, sans-serif;
|
361 |
+
font-weight: 600;
|
362 |
+
color: #2c3e50;
|
363 |
+
}
|
364 |
+
|
365 |
+
/* Button styles */
|
366 |
+
.gr-button {
|
367 |
+
margin: 10px 0;
|
368 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
369 |
+
"Helvetica Neue", Arial, sans-serif;
|
370 |
+
font-weight: 500;
|
371 |
+
}
|
372 |
+
|
373 |
+
/* Group styles */
|
374 |
+
.gr-group {
|
375 |
+
background-color: rgba(255, 255, 255, 0.1);
|
376 |
+
border-radius: 8px;
|
377 |
+
padding: 15px;
|
378 |
+
margin: 10px 0;
|
379 |
+
}
|
380 |
+
|
381 |
+
/* Label styles */
|
382 |
+
label {
|
383 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
384 |
+
"Helvetica Neue", Arial, sans-serif;
|
385 |
+
font-weight: 500;
|
386 |
+
}
|
387 |
+
|
388 |
+
/* Pong emoji flip - applied to all elements with pong-emoji class */
|
389 |
+
.pong-emoji {
|
390 |
+
display: inline-block;
|
391 |
+
transform: scaleX(-1);
|
392 |
+
margin-left: 5px; /* Add a little space */
|
393 |
+
}
|
394 |
+
|
395 |
+
/* Step title styles */
|
396 |
+
.step-header {
|
397 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
398 |
+
"Helvetica Neue", Arial, sans-serif;
|
399 |
+
font-weight: 600;
|
400 |
+
color: #2c3e50;
|
401 |
+
margin: 15px 0 10px 0;
|
402 |
+
}
|
403 |
+
</style>
|
404 |
+
""")
|
405 |
+
|
406 |
+
with gr.Row():
|
407 |
+
# Ping part (left)
|
408 |
+
with gr.Column(scale=1, variant="panel", elem_classes=["ping-panel"]):
|
409 |
+
gr.Markdown("### π Ping Control (Sender)")
|
410 |
+
with gr.Group():
|
411 |
+
gr.Markdown("**Step 1: Initialize Connection**")
|
412 |
+
ping_init_btn = gr.Button("Initialize Ping", variant="primary", size="lg")
|
413 |
+
ping_status = gr.Textbox(label="Connection Status", lines=2)
|
414 |
+
|
415 |
+
with gr.Tabs():
|
416 |
+
with gr.TabItem("RGB Control"):
|
417 |
+
with gr.Group():
|
418 |
+
gr.Markdown("**Step 2: Configure RGB Values**")
|
419 |
+
r = gr.Slider(0, 255, 128, label="Red Value", interactive=True)
|
420 |
+
g = gr.Slider(0, 255, 128, label="Green Value", interactive=True)
|
421 |
+
b = gr.Slider(0, 255, 128, label="Blue Value", interactive=True)
|
422 |
+
gr.Markdown("**Step 3: Send Command**")
|
423 |
+
send_rgb_btn = gr.Button("Send RGB Command", variant="secondary", size="lg")
|
424 |
+
rgb_status = gr.Textbox(label="RGB Status", lines=2)
|
425 |
+
|
426 |
+
with gr.TabItem("Weight Control"):
|
427 |
+
with gr.Group():
|
428 |
+
gr.Markdown("**Step 2: Set RPM Value**")
|
429 |
+
rpm = gr.Slider(0, 5000, 1000, label="RPM Setting", interactive=True)
|
430 |
+
gr.Markdown("**Step 3: Send Request**")
|
431 |
+
send_weight_btn = gr.Button("Send Weight Request", variant="secondary", size="lg")
|
432 |
+
weight_status = gr.Textbox(label="Weight Status", lines=2)
|
433 |
+
|
434 |
+
# Add temperature and humidity control tab
|
435 |
+
with gr.TabItem("Temperature Control"):
|
436 |
+
with gr.Group():
|
437 |
+
gr.Markdown("**Step 2: Set Temperature Value**")
|
438 |
+
temperature = gr.Slider(0, 50, 25, label="Temperature (Β°C)", interactive=True)
|
439 |
+
gr.Markdown("**Step 3: Send Request**")
|
440 |
+
send_temp_btn = gr.Button("Send Temperature Request", variant="secondary", size="lg")
|
441 |
+
temp_status = gr.Textbox(label="Temperature Status", lines=2)
|
442 |
+
|
443 |
+
with gr.Group():
|
444 |
+
gr.Markdown("**Step 4: Check Responses**")
|
445 |
+
check_ping_btn = gr.Button("Check Responses", variant="secondary", size="lg")
|
446 |
+
ping_responses = gr.Textbox(
|
447 |
+
label="Response Log",
|
448 |
+
lines=10,
|
449 |
+
show_copy_button=True
|
450 |
+
)
|
451 |
+
|
452 |
+
# Pong part (right)
|
453 |
+
with gr.Column(scale=1, variant="panel", elem_classes=["pong-panel"]):
|
454 |
+
gr.Markdown("### Pong Monitor (Receiver) <span class='pong-emoji'>π</span>")
|
455 |
+
with gr.Group():
|
456 |
+
gr.Markdown("**Step 1: Start System**")
|
457 |
+
with gr.Row():
|
458 |
+
pong_init_btn = gr.Button("Start Pong", variant="primary", size="lg")
|
459 |
+
pong_stop_btn = gr.Button("Stop Pong", variant="secondary", size="lg")
|
460 |
+
pong_status = gr.Textbox(label="System Status", lines=2)
|
461 |
+
|
462 |
+
with gr.Group():
|
463 |
+
gr.Markdown("**Step 2: Monitor Device Status**")
|
464 |
+
refresh_btn = gr.Button("π Refresh All", variant="primary", size="lg")
|
465 |
+
device_info = gr.JSON(
|
466 |
+
label="Current Device State",
|
467 |
+
value=device_state,
|
468 |
+
show_label=True
|
469 |
+
)
|
470 |
+
|
471 |
+
with gr.Group():
|
472 |
+
gr.Markdown("**Step 3: Check Incoming Commands**")
|
473 |
+
check_pong_btn = gr.Button("Check Commands", variant="secondary", size="lg")
|
474 |
+
pong_commands = gr.Textbox(
|
475 |
+
label="Command Log",
|
476 |
+
lines=10,
|
477 |
+
show_copy_button=True
|
478 |
+
)
|
479 |
+
|
480 |
+
# Add Step 4: Data Analysis (Optional)
|
481 |
+
with gr.Group():
|
482 |
+
gr.Markdown("**Step 4: Data Analysis (Optional)**")
|
483 |
+
with gr.Row():
|
484 |
+
analyze_rgb_btn = gr.Button("Analyze RGB Trend", variant="secondary", size="lg")
|
485 |
+
analyze_temp_btn = gr.Button("Analyze Temperature Trend", variant="secondary", size="lg")
|
486 |
+
|
487 |
+
plot_output = gr.Plot(
|
488 |
+
label="Trend Analysis",
|
489 |
+
show_label=True
|
490 |
+
)
|
491 |
+
|
492 |
+
# Add event handling for analysis buttons
|
493 |
+
analyze_rgb_btn.click(
|
494 |
+
lambda: plot_data_with_prediction("rgb"),
|
495 |
+
outputs=plot_output
|
496 |
+
)
|
497 |
+
analyze_temp_btn.click(
|
498 |
+
lambda: plot_data_with_prediction("temperature"),
|
499 |
+
outputs=plot_output
|
500 |
+
)
|
501 |
+
|
502 |
+
# Modify event handling
|
503 |
+
ping_init_btn.click(initialize_ping, outputs=ping_status)
|
504 |
+
pong_init_btn.click(initialize_pong, outputs=pong_status)
|
505 |
+
pong_stop_btn.click(stop_mqtt, outputs=pong_status)
|
506 |
+
|
507 |
+
# Automatic refresh of pong state when sending commands
|
508 |
+
send_rgb_btn.click(
|
509 |
+
send_rgb,
|
510 |
+
[r, g, b],
|
511 |
+
rgb_status
|
512 |
+
).then(
|
513 |
+
check_pong_commands, # Update pong command display
|
514 |
+
outputs=[pong_commands]
|
515 |
+
).then(
|
516 |
+
check_ping_responses, # Update ping response display
|
517 |
+
outputs=[ping_responses]
|
518 |
+
)
|
519 |
+
|
520 |
+
send_weight_btn.click(
|
521 |
+
send_weight_request,
|
522 |
+
rpm,
|
523 |
+
weight_status
|
524 |
+
).then(
|
525 |
+
refresh_all, # Use refresh_all
|
526 |
+
outputs=[pong_commands, device_info]
|
527 |
+
)
|
528 |
+
|
529 |
+
# Modify manual refresh button event handling
|
530 |
+
refresh_btn.click(
|
531 |
+
refresh_all,
|
532 |
+
outputs=[pong_commands, device_info]
|
533 |
+
)
|
534 |
+
|
535 |
+
check_ping_btn.click(
|
536 |
+
check_ping_responses,
|
537 |
+
outputs=ping_responses
|
538 |
+
)
|
539 |
+
check_pong_btn.click(check_pong_commands, outputs=pong_commands)
|
540 |
+
|
541 |
+
# Add real-time update when slider values change
|
542 |
+
r.change(update_rgb_preview, inputs=[r, g, b], outputs=device_info)
|
543 |
+
g.change(update_rgb_preview, inputs=[r, g, b], outputs=device_info)
|
544 |
+
b.change(update_rgb_preview, inputs=[r, g, b], outputs=device_info)
|
545 |
+
|
546 |
+
# Add RPM slider real-time update
|
547 |
+
rpm.change(update_rpm_preview, inputs=rpm, outputs=device_info)
|
548 |
+
|
549 |
+
# Add temperature slider real-time update
|
550 |
+
temperature.change(update_temperature_preview, inputs=temperature, outputs=device_info)
|
551 |
+
|
552 |
+
# Add temperature command sending handling
|
553 |
+
send_temp_btn.click(
|
554 |
+
lambda temp: send_command("Temperature Reading", {"temperature": temp}),
|
555 |
+
inputs=[temperature],
|
556 |
+
outputs=temp_status
|
557 |
+
).then(
|
558 |
+
check_pong_commands,
|
559 |
+
outputs=[pong_commands]
|
560 |
+
).then(
|
561 |
+
check_ping_responses,
|
562 |
+
outputs=[ping_responses]
|
563 |
+
)
|
564 |
+
|
565 |
+
demo.load(lambda: None) # Initialize
|
566 |
+
|
567 |
+
if __name__ == "__main__":
|
568 |
+
demo.launch()
|