diff --git a/Pipfile b/Pipfile index 01dfe71..84ad01a 100644 --- a/Pipfile +++ b/Pipfile @@ -8,6 +8,7 @@ verify_ssl = true [packages] dash = "*" dash-daq = "*" +dash-bootstrap-components = "*" requests = "*" [requires] diff --git a/homecontrol-dash.py b/homecontrol-dash.py index 8681a1a..a508bc9 100755 --- a/homecontrol-dash.py +++ b/homecontrol-dash.py @@ -1,22 +1,49 @@ #!/usr/bin/env python3 import dash +import dash_bootstrap_components as dbc import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output import time import requests +from math import inf URL_BASE="http://innocence:5000" -x = [1] -y = [2] +def get_sensors(): + ret = {} + try: + url = "%s/sensor/get" % URL_BASE + res = requests.get(url) + ret = res.json() + except Exception as ex: + print('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args)) + return ret -external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] + +def get_values(sensorId, min_ts, max_ts, limit): + ret = {} + if sensorId: + try: + url = "%s/sensor/get_values/%s?min_ts=%s&max_ts=%s&limit=%s" % (URL_BASE, + sensorId, min_ts, max_ts, limit) + res = requests.get(url) + ret = res.json()[sensorId] + except Exception as ex: + print('Exception Type:%s, args:\n%s' % (type(ex).__name__, ex.args)) + return ret + + +sensors = get_sensors() +sensor = next(iter(sensors), None) + +# external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] +external_stylesheets = [dbc.themes.BOOTSTRAP] meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}] app = dash.Dash(__name__, external_stylesheets=external_stylesheets, meta_tags=meta_tags) +app.title = "dashboard.ykonni.de" -available_sensors = ["undefined"] app.layout = html.Div(children=[ html.H1(children='dashboard.ykonni.de'), @@ -24,62 +51,121 @@ app.layout = html.Div(children=[ html.Div(children=''' visualize sensor data '''), - dcc.Graph( - id='example-graph', - figure={ - 'data': [ - {'x': x, 'y': y, 'mode': 'markers', 'name': 'SF'} - ], - 'layout': { - 'title': 'initial values' - } - } - ), - dcc.Dropdown( - id='select-sensor', - options=[{'label': i, 'value': i} for i in available_sensors], - value="undefined" - ), - html.Button(id='refresh-button', n_clicks=0, children='refresh') + dbc.Row([ + dbc.Col( + dcc.Graph( + id='graph-sensor-values', + figure={ + 'data': [ + {'x': [0], 'y': [0], 'mode': 'markers', 'name': 'None'} + ], + 'layout': { + 'title': 'initial values' + } + } + ) + ) + ]), + dbc.Row([ + dbc.Col([ + html.Div(id='select-sensor-txt'), + dcc.Dropdown( + id='select-sensorid', + options=[{'label': sensors[i]["sensorType"], 'value': i} for i in sensors], + value=sensor), + ]), + dbc.Col([ + html.Div(id='slider-min-txt'), + dcc.Slider( + id='slider-min', + min=0, + max=round(time.time()), + step=600, + value=0 + ), + ]), + dbc.Col([ + html.Div(id='slider-max-txt'), + dcc.Slider( + id='slider-max', + min=0, + max=round(time.time()), + step=600, + value=round(time.time()) + ), + ]), + dbc.Col([ + html.Div(id='slider-limit-txt'), + dcc.Slider( + id='slider-limit', + min=0, + max=1000, + step=10, + value=0, + ), + ]), + ]), ]) +@app.callback( + [Output('slider-min', 'min'), Output('slider-min', 'value'), + Output('slider-max', 'min'), Output('select-sensor-txt', 'children')], + [Input('select-sensorid', 'value')]) +def update_slider_min(sensorId): + res = get_values(sensorId, 0, int(time.time()), 1) + min_ts = 0 + sensorType = None + if "values" in res: + min_ts = int(res["values"][0]["ts"]) + sensorType = res["sensorType"] + + return min_ts, min_ts, min_ts, "Sensor: %s" % sensorType @app.callback( - Output('select-sensor', 'options'), - [Input('refresh-button', 'n_clicks')]) -def update_list(n_clicks): - url = "%s/sensor/get" % URL_BASE - print(url) - res = requests.get(url) - content = res.json() - options = [{'label': content[i]["sensorType"], 'value': i} for i in content] - return options + Output('slider-limit-txt', 'children'), + [Input('slider-limit', 'value')]) +def update_slider_limit(value): + return "Limit: %s" % (value if value > 0 else None) @app.callback( - Output('example-graph', 'figure'), - [Input('refresh-button', 'n_clicks'), Input('select-sensor', 'value')]) -def update_graph(n_clicks, sensorId): - if not sensorId == "undefined": - # url = "%s/sensor/get_values/%s?limit=10" % (URL_BASE, sensorId) - url = "%s/sensor/get_values/%s" % (URL_BASE, sensorId) - res = requests.get(url) - content = res.json() - v = content[sensorId]["values"] - sensorType = content[sensorId]["sensorType"] + Output('slider-min-txt', 'children'), + [Input('slider-min', 'value')]) +def update_slider_min(value): + return "From: %s" % (time.strftime("%Y/%m/%d %H:%M", time.localtime(float(value))) if + value > 0 else 0) + + +@app.callback( + Output('slider-max-txt', 'children'), + [Input('slider-max', 'value')]) +def update_slider_max(value): + return "To: %s" % (time.strftime("%Y/%m/%d %H:%M", time.localtime(float(value)))) + + +@app.callback( + Output('graph-sensor-values', 'figure'), + [Input('select-sensorid', 'value'), Input('slider-min', 'value'), + Input('slider-max', 'value'), Input('slider-limit', 'value')]) +def update_graph_sensor_values(sensorId, min_ts, max_ts, limit): + res = get_values(sensorId, min_ts, max_ts, limit) + if "values" in res: + v = res["values"] + s = res["sensorType"] + l = len(v) x = [time.strftime("%m%d-%H%M", time.localtime(float(v[i]["ts"]))) for i in range(len(v))] y = [v[i]["value"] for i in range(len(v))] else: - sensorType = sensorId - x = [1] - y = [1] + s = sensorId + x = [0] + y = [0] return { 'data': [ - {'x': x, 'y': y, 'mode': 'markers', 'name': "%s" % (sensorType)} + {'x': x, 'y': y, 'mode': 'markers', 'name': "%s" % (s)} ], 'layout': { - 'title': 'Data for sensor: %s' % (sensorType) + 'title': 'Data for sensor: %s (%d elements)' % (s, l) } }