diff --git a/juice.py b/juice.py index 0852125..fd29590 100644 --- a/juice.py +++ b/juice.py @@ -7,7 +7,7 @@ import copy import json import requests -from flask import Flask, render_template, request, abort +from flask import Flask, render_template, request, abort, jsonify class RelayDevice: @@ -55,7 +55,7 @@ class RelayDevice: ).groups()[0] == 'High' sub_dev.state = state - return json.dumps({sub_dev_id: state}) + return json.dumps({'ok': True, sub_dev_id: state}) def from_dict(self, data): """ @@ -151,13 +151,62 @@ def toggle(): if device.id == device_id: break else: - abort(404) + return make_error(404, "device_id not found") res = device.toggle(sub_dev_id) if not res: - abort(404) + return make_error(404, "sub_dev_id not found") save_network() return res +@app.route('/edit') +def edit(): + """ + Edits the text of a particular field. + """ + device_id = request.args.get('device_id') + sub_dev_id = request.args.get('sub_dev_id') + field = request.args.get('field') + value = request.args.get('value') + + for device in sum(network.values(), []): + if device.id == device_id: + break + else: + return make_error(404, "device_id not found") + + if sub_dev_id: + for sub_dev in sum(device.sub_devices.values(), []): + if sub_dev.id == sub_dev_id: + break + else: + return make_error(404, "sub_dev_id not found") + if hasattr(sub_dev, field): + setattr(sub_dev, field, value) + else: + return make_error(404, "sub_device field not found") + else: + if hasattr(device, field): + setattr(device, field, value) + else: + return make_error(404, "device field not found") + + data = { + 'ok': True, + 'field': field, + 'value': value + } + save_network() + return json.dumps(data) + + +def make_error(code, message): + """ + Returns a JSON error. + """ + res = jsonify(ok=False, status=code, message=message) + res.status_code = code + return res + if __name__ == '__main__': app.run(host='0.0.0.0', port=5300) diff --git a/static/fontawesome-webfont.woff2 b/static/fontawesome-webfont.woff2 new file mode 100644 index 0000000..d2a3f99 Binary files /dev/null and b/static/fontawesome-webfont.woff2 differ diff --git a/static/juice.css b/static/juice.css index 4ada69c..637c48f 100644 --- a/static/juice.css +++ b/static/juice.css @@ -2,15 +2,26 @@ body { padding: 5%; display: flex; justify-content: center; + font-family: Helvetica,sans-serif; + background-color: lightblue; } .device { - border: 1px solid #ccc; - padding: 3%; - margin: 3%; + border: 2px solid darkgray; + border-radius: 0.5em; + padding: 3em; + margin: 3em; display: flex; flex-direction: column; align-items: center; + background-color: whitesmoke; +} + + +.ip_address { + font-size: 0.8em; + color: darkgray; + margin: 0.2em; } .sub_devices { @@ -18,7 +29,8 @@ body { } .sub_device { - border: 1px solid #ccc; + border: 2px solid darkgray; + border-radius: 0.5em; padding: 1em; margin: 1em; display: flex; @@ -42,3 +54,19 @@ path { .off { stroke: black; } + +@font-face { + font-family: FontAwesome; + src: url("/static/fontawesome-webfont.woff2"); +} + +.edit, .save { + font-family: FontAwesome; + font-size: 0.8em; + color: dimgrey; +} + +.edit:hover, .save:hover { + color: red; + cursor: pointer; +} diff --git a/static/juice.js b/static/juice.js index 5f98600..41995a9 100644 --- a/static/juice.js +++ b/static/juice.js @@ -9,10 +9,10 @@ function toggle_outlet(svg) { .join('&'); fetch(window.location.href + 'toggle?' + query) .then(function(response) { - if (!response.ok) { throw new Error('HTTP error, status = ' + response.status); } return response.json(); }) .then(function(json) { + if (!json.ok) { throw new Error('HTTP error, status = ' + json.status + ', message = ' + json.message); } if (json[sub_dev.id]) { svg.classList.remove('off'); svg.classList.add('on'); @@ -22,3 +22,55 @@ function toggle_outlet(svg) { } }); } + +function edit_field(field) { + let value = field.firstElementChild.innerText; + let input = document.createElement('input'); + input.value = value; + field.firstElementChild.replaceWith(input); + + let save = document.createElement('span'); + save.innerHTML = ''; + save.className = 'save'; + save.setAttribute('onclick', 'save_field(this.parentElement)'); + field.children[1].replaceWith(save); +} + +function save_field(field) { + let value = field.firstElementChild.value; + let device_id = field.parentElement.id; + let sub_dev_id; + if (field.parentElement.className.includes('sub_device')) { + sub_dev_id = device_id; + device_id = field.parentElement.parentElement.parentElement.id; + } else { + sub_dev_id = ''; + } + let params = { + device_id: device_id, + sub_dev_id: sub_dev_id, + field: field.className, + value: value + }; + let query = Object.keys(params) + .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) + .join('&'); + + fetch(window.location.href + 'edit?' + query) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + if (!json.ok) { throw new Error('HTTP error, status = ' + json.status + ', message = ' + json.message); } + let span = document.createElement('span'); + span.innerText = json['value']; + span.className = 'field_value'; + field.firstElementChild.replaceWith(span); + + let edit = document.createElement('span'); + edit.innerHTML = ''; + edit.className = 'edit'; + edit.setAttribute('onclick', 'edit_field(this.parentElement)'); + field.children[1].replaceWith(edit); + }); +} diff --git a/templates/index.html b/templates/index.html index ea076b3..41272e4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -10,9 +10,9 @@ {% for device in devices %}
{{ device.id }}
-
{{ device.description }}
-
{{ device.location }}
-
{{ device.ip_address }}
+
{{ device.description }}
+
{{ device.location }}
+
{{ device.ip_address }}
{% for sub_dev_type, sub_devs in device.sub_devices.items() %} {% for sub_dev in sub_devs %} @@ -24,7 +24,7 @@ -
{{ sub_dev.description }}
+
{{ sub_dev.description }}
{% endfor %} {% endfor %}