From da825c15649fb8dbbd25a4c4784978d96e5d2359 Mon Sep 17 00:00:00 2001 From: iou1name Date: Sun, 17 Nov 2019 19:46:57 -0500 Subject: [PATCH] refactor devices model, improve lightstrip --- events.py | 7 +++ juice.py | 19 ++++++- models.py | 115 +++++++++++++++++++++---------------------- static/juice.css | 6 +-- static/juice.js | 8 ++- templates/index.html | 18 +++---- 6 files changed, 97 insertions(+), 76 deletions(-) diff --git a/events.py b/events.py index c9305b9..7c175ef 100644 --- a/events.py +++ b/events.py @@ -104,6 +104,13 @@ async def new_device(request, ws, data): if device_type == 'RelayDevice': device = models.RelayDevice() + device.sub_devices.append(models.RelayOutlet()) + device.sub_devices.append(models.RelayOutlet()) + device.sub_devices[0].id = 'OUT0' + device.sub_devices[0].gpio = '0' + device.sub_devices[1].id = 'OUT2' + device.sub_devices[1].gpio = '2' + device.update() else: data['ok'] = False data['error'] = "unknown device type" diff --git a/juice.py b/juice.py index 014a3a4..85c6cbd 100644 --- a/juice.py +++ b/juice.py @@ -29,6 +29,8 @@ async def index(request): """The index page.""" init_state = {} for device in network: + if device.type != "RelayDevice": + continue device_state = {} for sub_dev in device.sub_devices: device_state[sub_dev.id] = sub_dev.state @@ -64,6 +66,14 @@ async def websocket_handler(request): return ws +def html_color(color): + """Converts a decimal color code to HTML format.""" + html_color = "#" + for i in range(3): + html_color += hex(color[i])[2:].zfill(2) + return html_color + + async def send_json_all(self, d): """Sends to all connected websockets.""" for ws in self['websockets']: @@ -74,7 +84,14 @@ async def init_app(): """Initializes the application.""" web.Application.send_json_all = send_json_all app = web.Application(middlewares=[buckler_aiohttp.buckler_session]) - aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('templates')) + aiohttp_jinja2.setup( + app, + trim_blocks=True, + lstrip_blocks=True, + undefined=jinja2.StrictUndefined, + loader=jinja2.FileSystemLoader('templates'), + filters={'html_color': html_color}, + ) app['websockets'] = [] app.router.add_routes(routes) diff --git a/models.py b/models.py index dd1801b..71bb535 100644 --- a/models.py +++ b/models.py @@ -28,6 +28,15 @@ class Device: """ Represents a generic device on the Network. """ + def __init__(self): + self.id = "" + self.type = "" + self.description = "" + self.location = "" + self.ip_address = "" + self.locked = False + self.sub_devices = [] + def find(self, sub_device_id): """ Searches through the devices sub_devices and returns the matching @@ -40,6 +49,40 @@ class Device: return None return sub_device + def from_dict(self, data): + """ + Initializes self with data from dict. + """ + for key, value in data.items(): + if key == 'sub_devices': + continue + else: + setattr(self, key, value) + for sub_device_dict in data.get('sub_devices'): + if sub_device_dict.get('type') == 'RelayOutlet': + sub_device = RelayOutlet().from_dict(sub_device_dict) + self.sub_devices.append(sub_device) + elif sub_device_dict.get('type') == 'NeoPixel': + sub_device = NeoPixel().from_dict(sub_device_dict) + self.sub_devices.append(sub_device) + else: + typ = sub_device_dict.get('type') + raise ValueError(f"Unknown sub_device type {typ}") + return self + + +class SubDevice: + """ + Represents a generic subdevice in a Device. + """ + def from_dict(self, data): + """ + Initializes self with data from dict. + """ + for key, value in data.items(): + setattr(self, key, value) + return self + class RelayDevice(Device): """ @@ -48,22 +91,8 @@ class RelayDevice(Device): a relay. Each device only has two outputs, `OUT0` and `OUT2`. """ def __init__(self): - self.id = "" + super().__init__() self.type = "RelayDevice" - self.description = "" - self.location = "" - self.ip_address = "" - self.locked = False - - self.sub_devices = [] - self.sub_devices.append(RelayOutlet()) - self.sub_devices.append(RelayOutlet()) - self.sub_devices[0].id = 'OUT0' - self.sub_devices[0].gpio = '0' - self.sub_devices[1].id = 'OUT2' - self.sub_devices[1].gpio = '2' - - self.update() def update(self): """ @@ -97,24 +126,8 @@ class RelayDevice(Device): return state - def from_dict(self, data): - """ - Initializes self with data from JSON dict. - """ - for key, value in data.items(): - setattr(self, key, value) - self.sub_devices = [] - for sub_dev_dict in data.get('sub_devices'): - if sub_dev_dict.get('type') == 'RelayOutlet': - sub_dev = RelayOutlet().from_dict(sub_dev_dict) - self.sub_devices.append(sub_dev) - else: - typ = sub_dev.get('type') - raise ValueError(f"Unknown sub_device type {typ}") - return self - -class RelayOutlet: +class RelayOutlet(SubDevice): """ Represents a single outlet on a RelayDevice. """ @@ -125,42 +138,24 @@ class RelayOutlet: self.gpio = "" self.state = False - def from_dict(self, data): - """ - Initializes self with data from JSON dict. - """ - for key, value in data.items(): - setattr(self, key, value) - return self - class LightStrip(Device): """ Represents an RGB LED light strip controller. """ def __init__(self): - self.id = "" + super().__init__() self.type = "LightStrip" - self.description = "" - self.location = "" - self.ip_address = "" - self.locked = False - def from_dict(self, data): - """ - Initializes self with data from JSON dict. - """ - for key, value in data.items(): - setattr(self, key, value) - self.sub_devices = [] - for sub_dev_dict in data.get('sub_devices'): - if sub_dev_dict.get('type') == 'RelayOutlet': - sub_dev = RelayOutlet().from_dict(sub_dev_dict) - self.sub_devices.append(sub_dev) - else: - typ = sub_dev.get('type') - raise ValueError(f"Unknown sub_device type {typ}") - return self + +class NeoPixel(SubDevice): + """ + Represents a single NeoPixel in a LightStrip. + """ + def __init__(self): + self.id = "" + self.type = "NeoPixel" + self.color = [0,0,0] # JSON doesn't have tuples class DeviceEncoder(json.JSONEncoder): diff --git a/static/juice.css b/static/juice.css index d32109e..87a1aa1 100644 --- a/static/juice.css +++ b/static/juice.css @@ -109,18 +109,18 @@ nav span:hover { flex-wrap: wrap; } -.led { +.NeoPixel { padding: 0.25em; margin: 0.125em; } -.led_color { +.NeoPixel_color { border-radius: 0.25em; width: 1em; height: 1em; } -.led_color_input { +.NeoPixel_color_input { opacity: 0; display: block; width: 1em; diff --git a/static/juice.js b/static/juice.js index fdcfbd4..0581505 100644 --- a/static/juice.js +++ b/static/juice.js @@ -3,6 +3,7 @@ var socket; function load() { socket = init_websocket(); + // initialize RelayOutlet SVG color Object.entries(init_state).forEach(([device_id, sub_devs]) => { let device = document.querySelector('#' + device_id); Object.entries(sub_devs).forEach(([sub_device_id, state]) => { @@ -60,11 +61,12 @@ function onmessage (e) { socket.events[event](data); } -function onclose(e) { +async function onclose(e) { if (e.wasClean) { return; } // no need to reconnect console.log(e); console.log('Websocket lost connection to server. Re-trying...'); socket = init_websocket(); + await sleep(5000); } function onerror(e) { @@ -247,3 +249,7 @@ function get_object_from_svg(svg) { } return null; } + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/templates/index.html b/templates/index.html index fe7e8ef..e4273aa 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,7 +5,7 @@ @@ -37,23 +37,19 @@
{{ device.location }}{% if not device.locked %}{% endif %}
{{ device.ip_address }}{% if not device.locked %}{% endif %}
- {% if device.type == 'RelayDevice' %} - {% for sub_device in device.sub_devices %} + {% for sub_device in device.sub_devices %}
+ {% if device.type == 'RelayDevice' %}
{{ sub_device.id }}
{{ sub_device.description }}{% if not device.locked %}{% endif %}
-
- {% endfor %} - {% elif device.type == 'LightStrip' %} - {% for n in range(24) %} -
-
{% endfor %} - {% endif %}
{% endfor %}