Font End using Bulma and MQTT.js for functionality
This commit is contained in:
parent
d7b785e58c
commit
b400e5be3c
0
web/.gitkeep
Normal file
0
web/.gitkeep
Normal file
BIN
web/img/favicon.ico
Normal file
BIN
web/img/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
58
web/index.html
Normal file
58
web/index.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>IDFC</title>
|
||||||
|
<link rel="icon" href="img/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<script src="https://unpkg.com/mqtt@5.13.1/dist/mqtt.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<script src="main.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title has-text-primary">The M5 - MQTT Project</h1>
|
||||||
|
<h2 class="subtitle has-text-primary-light">by Patrik, Hazel and Reinhold</h2>
|
||||||
|
</div>
|
||||||
|
<div class="container m-5">
|
||||||
|
<div class="panel">
|
||||||
|
<form id="tabs" class="panel-tabs is-justify-content-flex-start has-text-success-light">
|
||||||
|
<input type="radio" id="chart-tab" name="tabs" data-tab="chart" checked/>
|
||||||
|
<label for="chart-tab" class="m-3">Chart</label>
|
||||||
|
|
||||||
|
<input type="radio" id="value-tab" name="tabs" data-tab="values"/>
|
||||||
|
<label for="value-tab" class="m-3">Values</label>
|
||||||
|
|
||||||
|
<input type="radio" id="connection-tab" name="tabs" data-tab="connection"/>
|
||||||
|
<label for="connection-tab" class="m-3">Connection</label>
|
||||||
|
</form>
|
||||||
|
<div id="block" class="panel-block has-background-grey">
|
||||||
|
<div id="chart" class="container" data-tab="chart">
|
||||||
|
<canvas></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="container" data-tab="values">
|
||||||
|
<div class="smart-grid">
|
||||||
|
<div id="values" class="grid p-6"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container" data-tab="connection" style="display: none;">
|
||||||
|
<div class="fixed-grid has-2-cols">
|
||||||
|
<div id="connection-grid" class="grid pr-6 has-text-success-light has-text-weight-semibold">
|
||||||
|
<label for="connection-broker" class="cell mx-6">Broker:</label>
|
||||||
|
<input id="connection-broker" class="cell input is-primary"/>
|
||||||
|
<label for="connection-topic" class="cell mx-6">Topic:</label>
|
||||||
|
<input id="connection-topic" class="cell input is-primary"/>
|
||||||
|
<button id="connection-submit" class="button cell is-primary is-col-start-2">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div id="toaster"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
66
web/main.css
Normal file
66
web/main.css
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
input[type="radio"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:checked + label {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 2px solid hsl(142, 52%, 96%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-grid {
|
||||||
|
display: inline-grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection-grid {
|
||||||
|
grid-template-columns: max-content auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toaster {
|
||||||
|
position: absolute;
|
||||||
|
translate: -50%;
|
||||||
|
top: 10px;
|
||||||
|
left: 50%;
|
||||||
|
max-height: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
--duration: 3s;
|
||||||
|
background-color: rgba(0, 209, 178, .5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 10px 3px;
|
||||||
|
margin: 3px;
|
||||||
|
border-radius: 10px;
|
||||||
|
min-width: 70px;
|
||||||
|
color: white;
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 40px;
|
||||||
|
animation: blend var(--duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blend {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 40px;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
max-height: 40px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0px;
|
||||||
|
}
|
||||||
|
}
|
168
web/main.js
Normal file
168
web/main.js
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
//const url = 'mqtt://tplinkwifi.net';
|
||||||
|
let client;
|
||||||
|
|
||||||
|
window.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
const tabs = document.querySelector('#tabs');
|
||||||
|
tabs.addEventListener('change', (event) => {
|
||||||
|
if (event.target.type != 'radio') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switchTab();
|
||||||
|
});
|
||||||
|
|
||||||
|
const buttonSubmit = document.querySelector('#connection-submit');
|
||||||
|
buttonSubmit.addEventListener('click', (event) => {
|
||||||
|
if (client) {
|
||||||
|
client.end();
|
||||||
|
}
|
||||||
|
const broker = document.querySelector('#connection-broker').value;
|
||||||
|
const topic = document.querySelector('#connection-topic').value;
|
||||||
|
connect(broker, topic);
|
||||||
|
buttonSubmit.classList.add('is-loading');
|
||||||
|
});
|
||||||
|
|
||||||
|
// start
|
||||||
|
document.querySelector('#connection-broker').value = 'test.mosquitto.org:8081';
|
||||||
|
document.querySelector('#connection-topic').value = 'test/2';
|
||||||
|
document.querySelector('#connection-submit').click();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function createChart(data) {
|
||||||
|
const elm = document.querySelector('#chart');
|
||||||
|
while (elm.childElementCount > 0) {
|
||||||
|
elm.firstElementChild.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = document.createElement('canvas');
|
||||||
|
elm.appendChild(ctx);
|
||||||
|
|
||||||
|
// generate labels
|
||||||
|
const labels = [];
|
||||||
|
for (i = 1; i <= data.length; i++) {
|
||||||
|
labels.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gridColor = '#4f4f4f';
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
data: data,
|
||||||
|
borderColor: '#d1fff8'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
color: gridColor
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: gridColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
grid: {
|
||||||
|
color: gridColor
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: gridColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadValues(data) {
|
||||||
|
let elm = document.querySelector('#values');
|
||||||
|
|
||||||
|
while (elm.childElementCount > 0) {
|
||||||
|
elm.firstElementChild.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const val = document.createElement('span');
|
||||||
|
val.innerText = data[i];
|
||||||
|
val.classList.add('is-align-self-center');
|
||||||
|
|
||||||
|
const cell = document.createElement('div');
|
||||||
|
cell.classList.add('cell', 'tag', 'has-background-dark');
|
||||||
|
cell.appendChild(val);
|
||||||
|
|
||||||
|
elm.appendChild(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchTab() {
|
||||||
|
const buttons = tabs.querySelectorAll('input[type="radio"]');
|
||||||
|
const block = document.querySelector('#block');
|
||||||
|
|
||||||
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
|
const name = buttons[i].getAttribute('data-tab');
|
||||||
|
const container = block.querySelector(`.container[data-tab="${name}"]`);
|
||||||
|
if (buttons[i].checked) {
|
||||||
|
container.style.setProperty('display', 'block');
|
||||||
|
} else {
|
||||||
|
container.style.setProperty('display', 'none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect(broker, topic) {
|
||||||
|
client = mqtt.connect('wss://' + broker);
|
||||||
|
const options = { retain: false, qos: 1 };
|
||||||
|
const submitButton = document.querySelector('#connection-submit');
|
||||||
|
|
||||||
|
client.on('connect', () => {
|
||||||
|
if (client.connected) {
|
||||||
|
toast('connected');
|
||||||
|
submitButton.classList.remove('is-loading');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('close', () => {
|
||||||
|
console.log('close');
|
||||||
|
toast('connection closed');
|
||||||
|
submitButton.classList.remove('is-loading');
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
|
toast('error');
|
||||||
|
submitButton.classList.remove('is-loading');
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('message', (topic, message) => {
|
||||||
|
console.log(message);
|
||||||
|
toast('message received');
|
||||||
|
submitButton.classList.remove('is-loading');
|
||||||
|
const tab = document.querySelector('#chart-tab');
|
||||||
|
tab.checked = true;
|
||||||
|
switchTab();
|
||||||
|
createChart(message);
|
||||||
|
loadValues(message);
|
||||||
|
});
|
||||||
|
client.subscribe(topic, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toast(message) {
|
||||||
|
const duration = 3;
|
||||||
|
const elm = document.createElement('p');
|
||||||
|
elm.innerText = message;
|
||||||
|
elm.classList.add('toast');
|
||||||
|
document.querySelector('#toaster').appendChild(elm);
|
||||||
|
|
||||||
|
elm.style.setProperty('--duration', duration + 's');
|
||||||
|
setTimeout(() => {
|
||||||
|
elm.remove();
|
||||||
|
}, duration * 1000);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user