|
|
import threading |
|
|
from collections import deque |
|
|
import gradio as gr |
|
|
import paho.mqtt.client as mqtt |
|
|
import json |
|
|
import pandas as pd |
|
|
from datetime import datetime |
|
|
import plotly.express as px |
|
|
|
|
|
|
|
|
data_buffer = deque(maxlen=100) |
|
|
|
|
|
|
|
|
client = mqtt.Client() |
|
|
MQTT_BROKER = "b6bdb89571144b3d8e5ca4bbe666ddb5.s1.eu.hivemq.cloud" |
|
|
MQTT_PORT = 8883 |
|
|
MQTT_TOPIC = "sensors/bme680/data" |
|
|
MQTT_USERNAME = "LuthiraMQ" |
|
|
MQTT_PASSWORD = "jLVx8y9v83gmgERTr0AP" |
|
|
|
|
|
|
|
|
connection_status = "β Not connected" |
|
|
data_status = "β Waiting for data..." |
|
|
|
|
|
|
|
|
status_lock = threading.Lock() |
|
|
|
|
|
def get_status(): |
|
|
""" |
|
|
Returns the current status of the MQTT connection and data reception. |
|
|
""" |
|
|
with status_lock: |
|
|
return f"**MQTT Connection:** {connection_status}\n**Data Status:** {data_status}" |
|
|
|
|
|
def update_data_status(new_status): |
|
|
""" |
|
|
Safely updates the data reception status. |
|
|
""" |
|
|
global data_status |
|
|
with status_lock: |
|
|
data_status = new_status |
|
|
|
|
|
def on_connect(client, userdata, flags, rc): |
|
|
""" |
|
|
Callback for MQTT connection events. |
|
|
""" |
|
|
global connection_status |
|
|
if rc == 0: |
|
|
client.subscribe(MQTT_TOPIC) |
|
|
connection_status = "β
Connected" |
|
|
else: |
|
|
connection_status = f"β Connection failed (code {rc})" |
|
|
|
|
|
def on_message(client, userdata, msg): |
|
|
""" |
|
|
Callback for processing incoming MQTT messages. |
|
|
Expects JSON payload with keys: "temperature", "humidity", "pressure", "gas". |
|
|
""" |
|
|
try: |
|
|
payload = json.loads(msg.payload.decode()) |
|
|
|
|
|
temperature = float(payload.get("temperature", 0)) |
|
|
humidity = float(payload.get("humidity", 0)) |
|
|
pressure = float(payload.get("pressure", 0)) |
|
|
gas = float(payload.get("gas", 0)) |
|
|
|
|
|
dt = datetime.now() |
|
|
data_buffer.append({ |
|
|
'timestamp': dt, |
|
|
'temperature': temperature, |
|
|
'humidity': humidity, |
|
|
'pressure': pressure, |
|
|
'gas': gas |
|
|
}) |
|
|
update_data_status("β
Receiving data") |
|
|
reset_inactivity_timer() |
|
|
except Exception as e: |
|
|
print(f"Error processing message: {e}") |
|
|
|
|
|
def connect_mqtt(broker, username, password): |
|
|
""" |
|
|
Connects to the MQTT broker using provided credentials. |
|
|
""" |
|
|
global MQTT_BROKER, MQTT_USERNAME, MQTT_PASSWORD, connection_status |
|
|
try: |
|
|
MQTT_BROKER = broker |
|
|
MQTT_USERNAME = username |
|
|
MQTT_PASSWORD = password |
|
|
|
|
|
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD) |
|
|
|
|
|
if not client._ssl: |
|
|
client.tls_set() |
|
|
|
|
|
client.on_connect = on_connect |
|
|
client.on_message = on_message |
|
|
client.connect(MQTT_BROKER, MQTT_PORT, 60) |
|
|
client.loop_start() |
|
|
return "Connection initiated..." |
|
|
except Exception as e: |
|
|
connection_status = f"β Connection error: {str(e)}" |
|
|
return f"Connection error: {str(e)}" |
|
|
|
|
|
|
|
|
inactivity_timer = None |
|
|
|
|
|
def reset_inactivity_timer(): |
|
|
""" |
|
|
Resets the inactivity timer to monitor data reception. |
|
|
""" |
|
|
global inactivity_timer |
|
|
if inactivity_timer is not None: |
|
|
inactivity_timer.cancel() |
|
|
inactivity_timer = threading.Timer(5.0, on_inactivity) |
|
|
inactivity_timer.start() |
|
|
|
|
|
def on_inactivity(): |
|
|
""" |
|
|
Callback for when no data is received within the timeout period. |
|
|
""" |
|
|
update_data_status("β Waiting for data...") |
|
|
|
|
|
def update_graph(): |
|
|
""" |
|
|
Returns a Plotly figure showing Temperature over time from the data buffer. |
|
|
""" |
|
|
if not data_buffer: |
|
|
fig = px.line(title="Waiting for Data...") |
|
|
else: |
|
|
df = pd.DataFrame(list(data_buffer)) |
|
|
fig = px.line(df, x='timestamp', y='temperature', title="Temperature Over Time") |
|
|
fig.update_layout( |
|
|
xaxis=dict( |
|
|
type="date", |
|
|
tickformat="%H:%M:%S", |
|
|
title="Time" |
|
|
), |
|
|
yaxis=dict(title="Temperature (Β°C)") |
|
|
) |
|
|
return fig |
|
|
|
|
|
def update_humidity_graph(): |
|
|
""" |
|
|
Returns a Plotly figure showing Humidity over time from the data buffer. |
|
|
""" |
|
|
if not data_buffer: |
|
|
fig = px.line(title="Waiting for Data...") |
|
|
else: |
|
|
df = pd.DataFrame(list(data_buffer)) |
|
|
fig = px.line(df, x='timestamp', y='humidity', title="Humidity Over Time") |
|
|
fig.update_layout( |
|
|
xaxis=dict( |
|
|
type="date", |
|
|
tickformat="%H:%M:%S", |
|
|
title="Time" |
|
|
), |
|
|
yaxis=dict(title="Humidity (%)") |
|
|
) |
|
|
return fig |
|
|
|
|
|
def update_metrics(): |
|
|
""" |
|
|
Returns a Markdown string with the latest sensor readings. |
|
|
""" |
|
|
if not data_buffer: |
|
|
return "No sensor data available yet." |
|
|
df = pd.DataFrame(list(data_buffer)) |
|
|
latest = df.iloc[-1] |
|
|
return ( |
|
|
f"**Latest Sensor Readings:** \n" |
|
|
f"- Temperature: {latest['temperature']:.2f} Β°C \n" |
|
|
f"- Humidity: {latest['humidity']:.2f} % \n" |
|
|
f"- Pressure: {latest['pressure']:.2f} hPa \n" |
|
|
f"- Gas: {latest['gas']:.2f} ohms \n\n" |
|
|
f"{get_status()}" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Blocks() as app: |
|
|
gr.Markdown("# Temperature Sensor Monitor") |
|
|
gr.Markdown("## Connect to MQTT") |
|
|
|
|
|
with gr.Row(): |
|
|
broker_input = gr.Textbox( |
|
|
label="MQTT Broker", |
|
|
value="b6bdb89571144b3d8e5ca4bbe666ddb5.s1.eu.hivemq.cloud" |
|
|
) |
|
|
username_input = gr.Textbox( |
|
|
label="MQTT Username", |
|
|
value="LuthiraMQ" |
|
|
) |
|
|
password_input = gr.Textbox( |
|
|
label="MQTT Password", |
|
|
value="jLVx8y9v83gmgERTr0AP", |
|
|
type="password" |
|
|
) |
|
|
|
|
|
connect_button = gr.Button("Connect") |
|
|
connection_status_md = gr.Markdown(get_status, every=10) |
|
|
|
|
|
gr.Markdown("### Temperature Graph") |
|
|
temp_plot = gr.Plot(update_graph, every=2.5) |
|
|
|
|
|
gr.Markdown("### Humidity Graph") |
|
|
humidity_plot = gr.Plot(update_humidity_graph, every=2.5) |
|
|
|
|
|
gr.Markdown("### Latest Sensor Readings") |
|
|
sensor_metrics_md = gr.Markdown(update_metrics, every=2.5) |
|
|
|
|
|
connect_button.click( |
|
|
fn=connect_mqtt, |
|
|
inputs=[broker_input, username_input, password_input], |
|
|
outputs=[connection_status_md] |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
app.launch() |
|
|
|