Initial POC
This commit is contained in:
commit
42593dc0ac
93
app.py
Executable file
93
app.py
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from flask import Flask, flash, redirect, render_template_string
|
||||||
|
from flask import render_template, request, session, abort
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def button_widget(topic, label, pub_value):
|
||||||
|
def f():
|
||||||
|
template = '''
|
||||||
|
<div>
|
||||||
|
<input type="button" value="{{label}}" onclick="publish('{{topic}}', '{{pub_value}}');"/>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
return render_template_string(
|
||||||
|
template, topic=topic, label=label, pub_value=pub_value)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
def label_widget(topic, label, unit=''):
|
||||||
|
def f():
|
||||||
|
template = '''
|
||||||
|
<div class="widget subscriber" data-topic="{{topic}}">
|
||||||
|
<div>
|
||||||
|
<span class="{{topic}}-value update-policy-replace-content">-</span><span>{{unit}}</span>
|
||||||
|
</div>
|
||||||
|
<div>{{label}}</div>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
return render_template_string(
|
||||||
|
template, topic=topic, label=label, unit=unit)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
def log_widget(topic, label):
|
||||||
|
def f():
|
||||||
|
template = '''
|
||||||
|
<div class="widget subscriber" data-topic="{{topic}}">
|
||||||
|
<div class="{{topic}}-value update-policy-append-content" style="overflow: auto; height:100px;">
|
||||||
|
</div>
|
||||||
|
<div>{{label}}</div>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
return render_template_string(template, topic=topic, label=label)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
def row_layout(title, *elems):
|
||||||
|
def f():
|
||||||
|
template = '''
|
||||||
|
<div>
|
||||||
|
<div>{{title}}</div>
|
||||||
|
<div style="display: flex;">
|
||||||
|
{% for w in elems %}
|
||||||
|
{{w()|safe}}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
return render_template_string(template, title=title, elems=elems)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
return render_template(
|
||||||
|
'base.html',
|
||||||
|
widgets=[
|
||||||
|
label_widget("test", "test value", "%"),
|
||||||
|
row_layout(
|
||||||
|
"Temperature",
|
||||||
|
label_widget("home/sensors/livingroom/temperature",
|
||||||
|
"livingroom", "°C"),
|
||||||
|
label_widget("home/sensors/office/temperature", "office",
|
||||||
|
"°C"),
|
||||||
|
label_widget("home/sensors/bedroom/temperature", "bedroom",
|
||||||
|
"°C")),
|
||||||
|
row_layout("Device logs", log_widget("test", "test log"),
|
||||||
|
log_widget("test2", "test log 2")),
|
||||||
|
button_widget("test", "set to 1", 1),
|
||||||
|
button_widget("test", "set to foo", 'foo')
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.secret_key = os.urandom(12)
|
||||||
|
app.run(debug=True, host='0.0.0.0', port=4000)
|
131
templates/base.html
Normal file
131
templates/base.html
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
.widget { width: 100%; padding: 5px; margin: 10px; background-color: white; }
|
||||||
|
.widget-unset { color: gray; }
|
||||||
|
#wrapper { width: 100%; height: 100%;}
|
||||||
|
html, body { height: 100%;}
|
||||||
|
.wrapper-ok { background-color: #e9ecef;}
|
||||||
|
.wrapper-error { background-color: #ff8888;}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.2/mqttws31.min.js" type="text/javascript"></script>
|
||||||
|
<script>
|
||||||
|
// Called after form input is processed
|
||||||
|
function startConnect() {
|
||||||
|
// Generate a random client ID
|
||||||
|
clientID = "clientID-" + parseInt(Math.random() * 100);
|
||||||
|
document.getElementById("wrapper").classList.add("wrapper-error");
|
||||||
|
document.getElementById("wrapper").classList.remove("wrapper-ok");
|
||||||
|
|
||||||
|
// Fetch the hostname/IP address and port number from the form
|
||||||
|
host = "bojack"
|
||||||
|
port = 8080;
|
||||||
|
|
||||||
|
// Print output for the user in the messages div
|
||||||
|
document.getElementById("messages").innerHTML += '<span>Connecting to: ' + host + ' on port: ' + port + '</span><br/>';
|
||||||
|
document.getElementById("messages").innerHTML += '<span>Using the following client value: ' + clientID + '</span><br/>';
|
||||||
|
|
||||||
|
// Initialize new Paho client connection
|
||||||
|
client = new Paho.MQTT.Client(host, Number(port), clientID);
|
||||||
|
|
||||||
|
// Set callback handlers
|
||||||
|
client.onConnectionLost = onConnectionLost;
|
||||||
|
client.onMessageArrived = onMessageArrived;
|
||||||
|
|
||||||
|
// Connect the client, if successful, call onConnect function
|
||||||
|
client.connect({
|
||||||
|
onSuccess: onConnect,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the client connects
|
||||||
|
function onConnect() {
|
||||||
|
document.getElementById("wrapper").classList.remove("wrapper-error");
|
||||||
|
document.getElementById("wrapper").classList.add("wrapper-ok");
|
||||||
|
|
||||||
|
var subWidgets = document.getElementsByClassName("subscriber");
|
||||||
|
console.log("Found " + subWidgets.length + " widgets");
|
||||||
|
for (var i = 0; i < subWidgets.length; i++) {
|
||||||
|
var c = subWidgets.item(i);
|
||||||
|
var topic = c.getAttribute('data-topic');
|
||||||
|
console.log("Subscribing to: " + topic);
|
||||||
|
client.subscribe(topic);
|
||||||
|
c.classList.add('widget-unset');
|
||||||
|
}
|
||||||
|
// Subscribe to the requested topic
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the client loses its connection
|
||||||
|
function onConnectionLost(responseObject) {
|
||||||
|
console.log("onConnectionLost: Connection Lost");
|
||||||
|
document.getElementById("wrapper").classList.remove("wrapper-ok");
|
||||||
|
document.getElementById("wrapper").classList.add("wrapper-error");
|
||||||
|
if (responseObject.errorCode !== 0) {
|
||||||
|
console.log("onConnectionLost: " + responseObject.errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a message arrives
|
||||||
|
function onMessageArrived(message) {
|
||||||
|
console.log(message.destinationName + "-value");
|
||||||
|
var views = document.getElementsByClassName(message.destinationName + "-value");
|
||||||
|
for (var i = 0 ; i < views.length; i++) {
|
||||||
|
view = views.item(i);
|
||||||
|
console.log(view);
|
||||||
|
if (view.classList.contains("update-policy-replace-content")) {
|
||||||
|
view.innerHTML = message.payloadString;
|
||||||
|
} else if(view.classList.contains("update-policy-append-content")) {
|
||||||
|
view.innerHTML += message.payloadString + "<br/>";
|
||||||
|
view.scrollTop = view.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var widgets = document.querySelectorAll("[data-topic='" + message.destinationName + "']");
|
||||||
|
for (var i = 0 ; i < widgets.length; i++) {
|
||||||
|
widgets.item(i).classList.remove("widget-unset");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the disconnection button is pressed
|
||||||
|
function startDisconnect() {
|
||||||
|
client.disconnect();
|
||||||
|
document.getElementById("messages").innerHTML += '<span>Disconnected</span><br/>';
|
||||||
|
updateScroll(); // Scroll to bottom of window
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates #messages div to auto-scroll
|
||||||
|
function updateScroll() {
|
||||||
|
var element = document.getElementById("messages");
|
||||||
|
element.scrollTop = element.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish(topic, value) {
|
||||||
|
var message = new Paho.MQTT.Message(value);
|
||||||
|
message.destinationName = topic;
|
||||||
|
client.send(message);
|
||||||
|
}
|
||||||
|
window.onload = startConnect;
|
||||||
|
</script>
|
||||||
|
<title>0xee home</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<br/>
|
||||||
|
<div id="value">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="widgets">
|
||||||
|
{% for w in widgets %}
|
||||||
|
{{w()|safe}}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div id="messages"></div>
|
||||||
|
<input type="button" onclick="startConnect()" value="Connect">
|
||||||
|
<input type="button" onclick="startDisconnect()" value="Disconnect">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user