208 lines
6.8 KiB
Python
Executable File
208 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from flask import Flask, flash, redirect, render_template_string
|
|
from flask import render_template, request, session, abort
|
|
import os
|
|
import uuid
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
def make_id():
|
|
return uuid.uuid4()
|
|
|
|
|
|
def make_widget(template,
|
|
widget_type,
|
|
sub_topic=None,
|
|
extra_classes=None,
|
|
**kwargs):
|
|
def f():
|
|
_classes = ['widget'] + list(extra_classes or [])
|
|
_attrs = {}
|
|
if sub_topic:
|
|
_classes.append('subscriber')
|
|
_attrs['data-sub-topic'] = sub_topic
|
|
_classes.append(f'{widget_type}-widget')
|
|
kwargs['sub_topic'] = sub_topic
|
|
_attrs['class'] = ' '.join([c for c in _classes if c is not None])
|
|
attr_str = " ".join([f'{k}="{v}"' for k, v in _attrs.items()])
|
|
widget_template = f'<div {attr_str}>\n{template}\n</div>'
|
|
return render_template_string(widget_template, **kwargs)
|
|
|
|
return f
|
|
|
|
|
|
def button_widget(topic, label, pub_value):
|
|
template = '''
|
|
<input type="button" class="button is-large is-info is-outlined" value="{{label}}" onclick="publish('{{topic}}', '{{pub_value}}');"/>
|
|
'''
|
|
return make_widget(template,
|
|
'button',
|
|
label=label,
|
|
pub_value=pub_value,
|
|
topic=topic)
|
|
|
|
|
|
def slider_widget(label,
|
|
topic,
|
|
sub_topic=None,
|
|
value_path=None,
|
|
unit='',
|
|
min_val=0,
|
|
max_val=255):
|
|
id_ = make_id()
|
|
sub_topic = sub_topic or topic
|
|
template = '''
|
|
<div style="display:flex;justify-content:center;align-items:center;height:60px;">
|
|
<input id="{{id_}}" class="slider update-policy-update-value {{sub_topic}}-value"
|
|
data-pub-topic="{{topic}}"
|
|
{% if value_path %} data-value-path="{{value_path}}" {% endif %}
|
|
min="{{min_val}}" type="range" max="{{max_val}}"
|
|
value="{{min_val}}"/>
|
|
<span id="{{id_}}-textual" class="slider-value">-</span><span>{{unit}}</span>
|
|
</div>
|
|
<div>{{label}}</div>
|
|
'''
|
|
return make_widget(template,
|
|
'slider',
|
|
sub_topic=sub_topic,
|
|
label=label,
|
|
min_val=min_val,
|
|
max_val=max_val,
|
|
value_path=value_path,
|
|
topic=topic,
|
|
unit=unit,
|
|
id_=id_)
|
|
|
|
|
|
def label_widget(topic, label, unit=''):
|
|
template = '''
|
|
<div align="center" class="is-size-2">
|
|
<span class="{{topic}}-value update-policy-replace-content">-</span>
|
|
<span>{{unit}}</span>
|
|
</div>
|
|
<div>{{label}}</div>
|
|
'''
|
|
return make_widget(template,
|
|
'label',
|
|
sub_topic=topic,
|
|
topic=topic,
|
|
unit=unit,
|
|
label=label)
|
|
|
|
|
|
def log_widget(topic, label):
|
|
id_ = make_id()
|
|
template = '''
|
|
<div class="table-container">
|
|
<table class="table is-striped is-hoverable is-fullwidth"
|
|
style="overflow: auto; height:100px;">
|
|
<thead><tr>
|
|
<th>Time</th><th>Message</th>
|
|
</tr></thead>
|
|
<tbody id="{{id_}}-content" class="{{topic}}-value update-policy-add-row">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div>{{label}}<button id="{{id_}}" class="delete log-delete"></button></div>
|
|
'''
|
|
return make_widget(template,
|
|
"log",
|
|
sub_topic=topic,
|
|
topic=topic,
|
|
label=label,
|
|
id_=id_)
|
|
|
|
|
|
def row_layout(title, *elems):
|
|
def f():
|
|
template = '''
|
|
<div class="box">
|
|
{% if title %}
|
|
<div class="title">{{title}}</div>
|
|
{% endif %}
|
|
<div class="columns is-tablet">
|
|
{% for w in elems %}
|
|
<div class="column">
|
|
{{w()|safe}}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
'''
|
|
return render_template_string(template, title=title, elems=elems)
|
|
|
|
return f
|
|
|
|
|
|
def column_layout(title, *elems):
|
|
def f():
|
|
template = '''
|
|
{% if title %}
|
|
<div class="title">{{title}}</div>
|
|
{% endif %}
|
|
<div class="rows">
|
|
{% for w in elems %}
|
|
<div class="row">
|
|
{{w()|safe}}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
'''
|
|
return render_template_string(template, title=title, elems=elems)
|
|
|
|
return f
|
|
|
|
|
|
@app.route('/')
|
|
def home():
|
|
return render_template(
|
|
'base.html',
|
|
widgets=[
|
|
column_layout(
|
|
"Light",
|
|
slider_widget("Küche",
|
|
"hue/set/lights/Ku",
|
|
"hue/status/lights/Ku",
|
|
value_path='val'),
|
|
slider_widget("WZ1",
|
|
"hue/set/lights/WZ1",
|
|
"hue/status/lights/WZ1",
|
|
value_path='val'),
|
|
slider_widget("WZ2",
|
|
"hue/set/lights/WZ2",
|
|
"hue/status/lights/WZ2",
|
|
value_path='val'),
|
|
slider_widget("Vorzimmer",
|
|
"hue/set/lights/Vorzimmer",
|
|
"hue/status/lights/Vorzimmer",
|
|
value_path='val'),
|
|
slider_widget("Schlafzimmer",
|
|
"home/devices/192.168.1.214/override",
|
|
"home/devices/192.168.1.214/brightness")),
|
|
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(f"home/devices/{ip}/log", name)
|
|
for ip, name in [("192.168.1.214", "Bedroom lamp"),
|
|
("192.168.1.213", "Sensor office"),
|
|
("192.168.1.212", "Sensor livingroom"),
|
|
("192.168.1.211", "Sensor bedroom")]
|
|
]),
|
|
row_layout("Some buttons", 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)
|