379 lines
13 KiB
JavaScript
379 lines
13 KiB
JavaScript
// Configuration
|
|
const API_BASE_URL = 'http://localhost:80';
|
|
const MQTT_BROKER_URL = 'ws://localhost:9001/mqtt'; // WebSocket port for MQTT
|
|
const WS_URL = 'http://localhost:80';
|
|
|
|
// State
|
|
let mqttClient = null;
|
|
let wsClient = null;
|
|
let subscribedTopics = new Set();
|
|
|
|
// Initialize
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
|
|
function init() {
|
|
setupTabs();
|
|
setupAPI();
|
|
setupMQTT();
|
|
setupWebSocket();
|
|
setupDebug();
|
|
setupQuickActions();
|
|
}
|
|
|
|
// Tab Management
|
|
function setupTabs() {
|
|
const tabButtons = document.querySelectorAll('.tab-button');
|
|
const tabContents = document.querySelectorAll('.tab-content');
|
|
|
|
tabButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
const tabName = button.dataset.tab;
|
|
|
|
// Remove active class from all
|
|
tabButtons.forEach(btn => btn.classList.remove('active'));
|
|
tabContents.forEach(content => content.classList.remove('active'));
|
|
|
|
// Add active class to selected
|
|
button.classList.add('active');
|
|
document.getElementById(`${tabName}-tab`).classList.add('active');
|
|
});
|
|
});
|
|
}
|
|
|
|
// API Setup
|
|
function setupAPI() {
|
|
const endpointSelect = document.getElementById('api-endpoint');
|
|
const paramsTextarea = document.getElementById('api-params');
|
|
const sendBtn = document.getElementById('api-send-btn');
|
|
const responsePre = document.getElementById('api-response');
|
|
|
|
sendBtn.addEventListener('click', async () => {
|
|
const endpoint = endpointSelect.value;
|
|
const [method, path] = endpoint.split(' ');
|
|
const params = paramsTextarea.value.trim();
|
|
|
|
try {
|
|
let options = {
|
|
method: method,
|
|
headers: {}
|
|
};
|
|
|
|
if (method === 'POST' && params) {
|
|
// Try to parse as JSON, otherwise use as form data
|
|
try {
|
|
const jsonData = JSON.parse(params);
|
|
options.headers['Content-Type'] = 'application/json';
|
|
options.body = JSON.stringify(jsonData);
|
|
} catch {
|
|
// Not JSON, use form data
|
|
const formData = new URLSearchParams();
|
|
const pairs = params.split('&');
|
|
pairs.forEach(pair => {
|
|
const [key, value] = pair.split('=');
|
|
if (key && value) {
|
|
formData.append(key, decodeURIComponent(value));
|
|
}
|
|
});
|
|
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
options.body = formData.toString();
|
|
}
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE_URL}${path}`, options);
|
|
const text = await response.text();
|
|
|
|
let formatted;
|
|
try {
|
|
formatted = JSON.stringify(JSON.parse(text), null, 2);
|
|
} catch {
|
|
formatted = text;
|
|
}
|
|
|
|
responsePre.textContent = formatted;
|
|
|
|
} catch (error) {
|
|
responsePre.textContent = `Error: ${error.message}`;
|
|
}
|
|
});
|
|
}
|
|
|
|
// MQTT Setup
|
|
function setupMQTT() {
|
|
const topicInput = document.getElementById('mqtt-topic');
|
|
const payloadTextarea = document.getElementById('mqtt-payload');
|
|
const publishBtn = document.getElementById('mqtt-publish-btn');
|
|
const subscribeBtn = document.getElementById('mqtt-subscribe-btn');
|
|
const unsubscribeBtn = document.getElementById('mqtt-unsubscribe-btn');
|
|
const subscribeTopicInput = document.getElementById('mqtt-subscribe-topic');
|
|
const messagesContainer = document.getElementById('mqtt-messages');
|
|
const clearMessagesBtn = document.getElementById('clear-messages-btn');
|
|
|
|
// Connect to MQTT broker
|
|
try {
|
|
mqttClient = mqtt.connect(MQTT_BROKER_URL, {
|
|
clientId: 'debug-ui-' + Math.random().toString(16).substr(2, 8)
|
|
});
|
|
|
|
mqttClient.on('connect', () => {
|
|
console.log('MQTT connected');
|
|
updateStatus('mqtt-status', 'MQTT: Connected', true);
|
|
});
|
|
|
|
mqttClient.on('error', (error) => {
|
|
console.error('MQTT error:', error);
|
|
updateStatus('mqtt-status', 'MQTT: Error', false);
|
|
});
|
|
|
|
mqttClient.on('close', () => {
|
|
console.log('MQTT disconnected');
|
|
updateStatus('mqtt-status', 'MQTT: Disconnected', false);
|
|
});
|
|
|
|
mqttClient.on('message', (topic, message) => {
|
|
addMessage(topic, message.toString());
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to connect to MQTT:', error);
|
|
updateStatus('mqtt-status', 'MQTT: Connection Failed', false);
|
|
}
|
|
|
|
publishBtn.addEventListener('click', () => {
|
|
const topic = topicInput.value.trim();
|
|
let payload = payloadTextarea.value.trim();
|
|
|
|
if (!topic) {
|
|
alert('Please enter a topic');
|
|
return;
|
|
}
|
|
|
|
// Try to parse as JSON, otherwise use as-is
|
|
try {
|
|
const jsonData = JSON.parse(payload);
|
|
payload = JSON.stringify(jsonData);
|
|
} catch {
|
|
// Not JSON, use as-is
|
|
}
|
|
|
|
if (mqttClient && mqttClient.connected) {
|
|
mqttClient.publish(topic, payload, (err) => {
|
|
if (err) {
|
|
console.error('Publish error:', err);
|
|
alert('Failed to publish: ' + err.message);
|
|
} else {
|
|
console.log('Published to', topic);
|
|
}
|
|
});
|
|
} else {
|
|
alert('MQTT not connected');
|
|
}
|
|
});
|
|
|
|
subscribeBtn.addEventListener('click', () => {
|
|
const topic = subscribeTopicInput.value.trim();
|
|
if (!topic) {
|
|
alert('Please enter a topic pattern');
|
|
return;
|
|
}
|
|
|
|
if (mqttClient && mqttClient.connected) {
|
|
mqttClient.subscribe(topic, (err) => {
|
|
if (err) {
|
|
console.error('Subscribe error:', err);
|
|
alert('Failed to subscribe: ' + err.message);
|
|
} else {
|
|
subscribedTopics.add(topic);
|
|
console.log('Subscribed to', topic);
|
|
}
|
|
});
|
|
} else {
|
|
alert('MQTT not connected');
|
|
}
|
|
});
|
|
|
|
unsubscribeBtn.addEventListener('click', () => {
|
|
if (mqttClient && mqttClient.connected) {
|
|
subscribedTopics.forEach(topic => {
|
|
mqttClient.unsubscribe(topic);
|
|
});
|
|
subscribedTopics.clear();
|
|
console.log('Unsubscribed from all topics');
|
|
}
|
|
});
|
|
|
|
clearMessagesBtn.addEventListener('click', () => {
|
|
messagesContainer.innerHTML = '';
|
|
});
|
|
}
|
|
|
|
function addMessage(topic, payload) {
|
|
const messagesContainer = document.getElementById('mqtt-messages');
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = 'message-item';
|
|
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
let formattedPayload = payload;
|
|
try {
|
|
formattedPayload = JSON.stringify(JSON.parse(payload), null, 2);
|
|
} catch {}
|
|
|
|
messageDiv.innerHTML = `
|
|
<div class="timestamp">${timestamp}</div>
|
|
<div class="topic">${topic}</div>
|
|
<div class="payload">${formattedPayload}</div>
|
|
`;
|
|
|
|
messagesContainer.appendChild(messageDiv);
|
|
|
|
// Auto-scroll
|
|
if (document.getElementById('auto-scroll').checked) {
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
}
|
|
}
|
|
|
|
// WebSocket Setup
|
|
function setupWebSocket() {
|
|
if (typeof io !== 'undefined') {
|
|
try {
|
|
wsClient = io(SOCKET_IO_URL);
|
|
|
|
wsClient.on('connect', () => {
|
|
console.log('WebSocket connected');
|
|
updateStatus('ws-status', 'WebSocket: Connected', true);
|
|
});
|
|
|
|
wsClient.on('disconnect', () => {
|
|
console.log('WebSocket disconnected');
|
|
updateStatus('ws-status', 'WebSocket: Disconnected', false);
|
|
});
|
|
|
|
wsClient.on('update', (data) => {
|
|
console.log('WebSocket update:', data);
|
|
// Could display in a separate section
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to connect WebSocket:', error);
|
|
updateStatus('ws-status', 'WebSocket: Error', false);
|
|
}
|
|
} else {
|
|
console.warn('Socket.io not loaded');
|
|
updateStatus('ws-status', 'WebSocket: Library Not Loaded', false);
|
|
}
|
|
}
|
|
|
|
// Debug Endpoints Setup
|
|
function setupDebug() {
|
|
const debugButtons = document.querySelectorAll('[data-debug]');
|
|
const responsePre = document.getElementById('debug-response');
|
|
|
|
debugButtons.forEach(button => {
|
|
button.addEventListener('click', async () => {
|
|
const action = button.dataset.debug;
|
|
const endpoint = `/api/debug/${action}`;
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}${endpoint}`);
|
|
const text = await response.text();
|
|
responsePre.textContent = text;
|
|
} catch (error) {
|
|
responsePre.textContent = `Error: ${error.message}`;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Quick Actions Setup
|
|
function setupQuickActions() {
|
|
const quickActionButtons = document.querySelectorAll('[data-action]');
|
|
|
|
quickActionButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
const action = button.dataset.action;
|
|
const topicInput = document.getElementById('mqtt-topic');
|
|
const payloadTextarea = document.getElementById('mqtt-payload');
|
|
|
|
switch (action) {
|
|
case 'button-start1':
|
|
topicInput.value = 'aquacross/button/00:00:00:00:00:01';
|
|
payloadTextarea.value = JSON.stringify({
|
|
type: 2,
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
case 'button-stop1':
|
|
topicInput.value = 'aquacross/button/00:00:00:00:00:03';
|
|
payloadTextarea.value = JSON.stringify({
|
|
type: 1,
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
case 'button-start2':
|
|
topicInput.value = 'aquacross/button/00:00:00:00:00:02';
|
|
payloadTextarea.value = JSON.stringify({
|
|
type: 2,
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
case 'button-stop2':
|
|
topicInput.value = 'aquacross/button/00:00:00:00:00:04';
|
|
payloadTextarea.value = JSON.stringify({
|
|
type: 1,
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
case 'rfid-read':
|
|
topicInput.value = 'aquacross/button/rfid/00:00:00:00:00:01';
|
|
payloadTextarea.value = JSON.stringify({
|
|
uid: 'TEST123456'
|
|
}, null, 2);
|
|
break;
|
|
case 'battery-update':
|
|
topicInput.value = 'aquacross/battery/00:00:00:00:00:01';
|
|
payloadTextarea.value = JSON.stringify({
|
|
voltage: 3600
|
|
}, null, 2);
|
|
break;
|
|
case 'heartbeat':
|
|
topicInput.value = 'heartbeat/alive/00:00:00:00:00:01';
|
|
payloadTextarea.value = JSON.stringify({
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
case 'button-available':
|
|
topicInput.value = 'aquacross/button/status/00:00:00:00:00:01';
|
|
payloadTextarea.value = JSON.stringify({
|
|
available: true,
|
|
sleep: false,
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
case 'button-sleep':
|
|
topicInput.value = 'aquacross/button/status/00:00:00:00:00:01';
|
|
payloadTextarea.value = JSON.stringify({
|
|
available: false,
|
|
sleep: true,
|
|
timestamp: Date.now()
|
|
}, null, 2);
|
|
break;
|
|
}
|
|
|
|
// Auto-publish
|
|
document.getElementById('mqtt-publish-btn').click();
|
|
});
|
|
});
|
|
}
|
|
|
|
// Helper Functions
|
|
function updateStatus(elementId, text, connected) {
|
|
const element = document.getElementById(elementId);
|
|
element.textContent = text;
|
|
element.classList.remove('connected', 'disconnected');
|
|
element.classList.add(connected ? 'connected' : 'disconnected');
|
|
}
|
|
|
|
// Initialize on load
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|