Skip to content

Commit

Permalink
Add screenshot and update UI
Browse files Browse the repository at this point in the history
  • Loading branch information
terdia committed Aug 19, 2024
1 parent ef5c338 commit 01250a5
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 68 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

MQTT Web Interface is an open-source web application that provides a real-time visualization of MQTT (Message Queuing Telemetry Transport) message flows. It allows users to monitor MQTT topics, publish messages, and view message statistics through an intuitive web interface.

## Screenshot

![Application Screenshot](static/screenshot.png)

## Features

- Real-time visualization of MQTT topic hierarchy and message flow
Expand Down
Binary file added static/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 46 additions & 27 deletions static/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ let messageChart;
let network;
let nodes;
let edges;
let topicFilter = 'all';

function initChart() {
const ctx = document.getElementById('messageChart').getContext('2d');
Expand All @@ -13,30 +14,47 @@ function initChart() {
datasets: [{
label: 'Messages per second',
data: [],
borderColor: 'rgb(75, 192, 192)',
borderColor: 'rgb(59, 130, 246)',
tension: 0.1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
beginAtZero: true,
ticks: {
color: 'rgb(209, 213, 219)'
}
},
x: {
ticks: {
color: 'rgb(209, 213, 219)'
}
}
},
plugins: {
legend: {
labels: {
color: 'rgb(209, 213, 219)'
}
}
}
}
});
}

function updateMessageList(message) {
const messageList = document.getElementById('message-list');
const messageElement = document.createElement('div');
messageElement.className = 'mb-2 p-2 bg-gray-100 dark:bg-gray-700 rounded';
messageElement.textContent = `${message.topic}: ${message.payload}`;
messageList.insertBefore(messageElement, messageList.firstChild);

if (messageList.childElementCount > 100) {
messageList.removeChild(messageList.lastChild);
if (topicFilter === 'all' || message.topic === topicFilter) {
const messageList = document.getElementById('message-list');
const messageElement = document.createElement('div');
messageElement.className = 'mb-2 p-2 bg-gray-700 rounded';
messageElement.innerHTML = `<strong class="text-blue-400">${message.topic}:</strong> ${message.payload}`;
messageList.insertBefore(messageElement, messageList.firstChild);

if (messageList.childElementCount > 100) {
messageList.removeChild(messageList.lastChild);
}
}
}

Expand All @@ -57,11 +75,6 @@ function updateChart() {
let messageCount = 0;

function initNetwork() {
if (typeof vis === 'undefined') {
console.error('Vis.js library not loaded');
return;
}

nodes = new vis.DataSet([
{ id: 'broker', label: 'MQTT Broker', shape: 'hexagon', color: '#FFA500', size: 30 }
]);
Expand All @@ -80,8 +93,7 @@ function initNetwork() {
},
nodes: {
font: {
size: 12,
face: 'Tahoma'
color: '#FFFFFF'
}
},
edges: {
Expand Down Expand Up @@ -116,7 +128,7 @@ function updateNetwork(message) {
label: part,
color: getRandomColor(),
shape: 'dot',
size: 20 - index * 2 // Gradually decrease size for subtopics
size: 20 - index * 2
});
}
if (parentId !== nodeId) {
Expand Down Expand Up @@ -144,21 +156,13 @@ function updateNetwork(message) {
setTimeout(() => {
nodes.update({ id: finalNodeId, size: finalNode.size });
}, 1000);

// Focus on the relevant part of the network
network.focus(finalNodeId, {
scale: 1,
animation: {
duration: 1000,
easingFunction: 'easeInOutQuad'
}
});
}

socket.on('mqtt_message', function(data) {
updateMessageList(data);
messageCount++;
updateNetwork(data);
updateTopicFilter(data.topic);
});

function getRandomColor() {
Expand Down Expand Up @@ -197,6 +201,21 @@ function updateStats() {
});
}

function updateTopicFilter(newTopic) {
const topicFilter = document.getElementById('topic-filter');
if (!Array.from(topicFilter.options).some(option => option.value === newTopic)) {
const option = document.createElement('option');
option.value = newTopic;
option.textContent = newTopic;
topicFilter.appendChild(option);
}
}

document.getElementById('topic-filter').addEventListener('change', function(e) {
topicFilter = e.target.value;
document.getElementById('message-list').innerHTML = '';
});

document.addEventListener('DOMContentLoaded', function() {
initChart();
initNetwork();
Expand Down
34 changes: 31 additions & 3 deletions static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,47 @@
}

#message-list::-webkit-scrollbar-track {
background: #f1f1f1;
background: #1F2937;
}

#message-list::-webkit-scrollbar-thumb {
background: #888;
background: #4B5563;
border-radius: 4px;
}

#message-list::-webkit-scrollbar-thumb:hover {
background: #555;
background: #6B7280;
}

/* Smooth transitions for dark mode */
body {
transition: background-color 0.3s, color 0.3s;
}

/* Additional styles for better readability */
#message-list div {
background-color: rgba(55, 65, 81, 0.5);
padding: 0.5rem;
border-radius: 0.25rem;
}

#network-visualization {
border: 1px solid #4B5563;
border-radius: 0.25rem;
}

/* Improve form input styling */
input[type="text"], textarea, select {
width: 100%;
padding: 0.5rem;
border-radius: 0.25rem;
background-color: #374151;
border: 1px solid #4B5563;
color: #F3F4F6;
}

input[type="text"]:focus, textarea:focus, select:focus {
outline: none;
border-color: #60A5FA;
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
}
92 changes: 54 additions & 38 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -1,54 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MQTT Web Interface</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis-network/9.1.2/dist/vis-network.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis-network/9.1.2/dist/vis-network.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>

<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
</head>
<body>
<body class="bg-gray-900 text-white">
<div class="container mx-auto p-4">
<h1 id="mqtt-web-interface" class="text-3xl font-bold mb-6">MQTT Web Interface</h1>

<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 id="last-messages" class="text-xl font-semibold mb-4">Last Messages</h2>
<div id="message-list" class="h-64 overflow-y-auto"></div>
<h1 class="text-4xl font-bold mb-6">MQTT Web Interface</h1>

<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2">
<div class="bg-gray-800 rounded-lg shadow-md p-6 mb-6">
<h2 class="text-2xl font-semibold mb-4">Message Flow</h2>
<div id="network-visualization" class="h-96"></div>
</div>

<div class="bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-semibold mb-4">Message Rate</h2>
<canvas id="messageChart"></canvas>
</div>
</div>

<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 id="publish-message" class="text-xl font-semibold mb-4">Publish Message</h2>
<form id="publish-form">
<input type="text" id="topic" placeholder="Topic" class="w-full p-2 mb-2 border rounded" required>
<input type="text" id="message" placeholder="Message" class="w-full p-2 mb-2 border rounded" required>
<button type="submit" class="bg-blue-500 text-white p-2 rounded">Publish</button>
</form>
</div>

<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 id="message-rate" class="text-xl font-semibold mb-4">Message Rate</h2>
<canvas id="messageChart"></canvas>
</div>

<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 id="stats" class="text-xl font-semibold mb-4">Stats</h2>
<p>Active Connections: <span id="connection-count" class="font-bold">0</span></p>
<p>Total Topics: <span id="topic-count" class="font-bold">0</span></p>
<p>Total Messages: <span id="message-count" class="font-bold">0</span></p>
</div>

<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 col-span-2">
<h2 id="message-flow" class="text-xl font-semibold mb-4">Message Flow</h2>
<div id="network-visualization" style="height: 400px;"></div>

<div class="space-y-6">
<div class="bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-semibold mb-4">Publish Message</h2>
<form id="publish-form" class="space-y-4">
<div>
<label for="topic" class="block text-sm font-medium text-gray-300">Topic</label>
<input type="text" id="topic" name="topic" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white">
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-300">Message</label>
<textarea id="message" name="message" rows="3" class="mt-1 block w-full rounded-md bg-gray-700 border-gray-600 text-white"></textarea>
</div>
<button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Publish</button>
</form>
</div>

<div class="bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-semibold mb-4">Stats</h2>
<p>Active Connections: <span id="connection-count" class="font-bold">0</span></p>
<p>Total Topics: <span id="topic-count" class="font-bold">0</span></p>
<p>Total Messages: <span id="message-count" class="font-bold">0</span></p>
</div>

<div class="bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-semibold mb-4">Topic Filter</h2>
<select id="topic-filter" class="block w-full mt-1 rounded-md bg-gray-700 border-gray-600 text-white">
<option value="all">All Topics</option>
</select>
</div>
</div>
</div>

<div class="mt-6 bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-semibold mb-4">Last Messages</h2>
<div id="message-list" class="h-64 overflow-y-auto space-y-2"></div>
</div>
</div>

<script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>

0 comments on commit 01250a5

Please sign in to comment.