refactor devices model, improve lightstrip

This commit is contained in:
iou1name 2019-11-17 19:46:57 -05:00
parent 3434fb8f96
commit da825c1564
6 changed files with 97 additions and 76 deletions

View File

@ -104,6 +104,13 @@ async def new_device(request, ws, data):
if device_type == 'RelayDevice': if device_type == 'RelayDevice':
device = models.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: else:
data['ok'] = False data['ok'] = False
data['error'] = "unknown device type" data['error'] = "unknown device type"

View File

@ -29,6 +29,8 @@ async def index(request):
"""The index page.""" """The index page."""
init_state = {} init_state = {}
for device in network: for device in network:
if device.type != "RelayDevice":
continue
device_state = {} device_state = {}
for sub_dev in device.sub_devices: for sub_dev in device.sub_devices:
device_state[sub_dev.id] = sub_dev.state device_state[sub_dev.id] = sub_dev.state
@ -64,6 +66,14 @@ async def websocket_handler(request):
return ws 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): async def send_json_all(self, d):
"""Sends to all connected websockets.""" """Sends to all connected websockets."""
for ws in self['websockets']: for ws in self['websockets']:
@ -74,7 +84,14 @@ async def init_app():
"""Initializes the application.""" """Initializes the application."""
web.Application.send_json_all = send_json_all web.Application.send_json_all = send_json_all
app = web.Application(middlewares=[buckler_aiohttp.buckler_session]) 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['websockets'] = []
app.router.add_routes(routes) app.router.add_routes(routes)

115
models.py
View File

@ -28,6 +28,15 @@ class Device:
""" """
Represents a generic device on the Network. 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): def find(self, sub_device_id):
""" """
Searches through the devices sub_devices and returns the matching Searches through the devices sub_devices and returns the matching
@ -40,6 +49,40 @@ class Device:
return None return None
return sub_device 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): class RelayDevice(Device):
""" """
@ -48,22 +91,8 @@ class RelayDevice(Device):
a relay. Each device only has two outputs, `OUT0` and `OUT2`. a relay. Each device only has two outputs, `OUT0` and `OUT2`.
""" """
def __init__(self): def __init__(self):
self.id = "" super().__init__()
self.type = "RelayDevice" 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): def update(self):
""" """
@ -97,24 +126,8 @@ class RelayDevice(Device):
return state 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(SubDevice):
class RelayOutlet:
""" """
Represents a single outlet on a RelayDevice. Represents a single outlet on a RelayDevice.
""" """
@ -125,42 +138,24 @@ class RelayOutlet:
self.gpio = "" self.gpio = ""
self.state = False 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): class LightStrip(Device):
""" """
Represents an RGB LED light strip controller. Represents an RGB LED light strip controller.
""" """
def __init__(self): def __init__(self):
self.id = "" super().__init__()
self.type = "LightStrip" self.type = "LightStrip"
self.description = ""
self.location = ""
self.ip_address = ""
self.locked = False
def from_dict(self, data):
""" class NeoPixel(SubDevice):
Initializes self with data from JSON dict. """
""" Represents a single NeoPixel in a LightStrip.
for key, value in data.items(): """
setattr(self, key, value) def __init__(self):
self.sub_devices = [] self.id = ""
for sub_dev_dict in data.get('sub_devices'): self.type = "NeoPixel"
if sub_dev_dict.get('type') == 'RelayOutlet': self.color = [0,0,0] # JSON doesn't have tuples
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 DeviceEncoder(json.JSONEncoder): class DeviceEncoder(json.JSONEncoder):

View File

@ -109,18 +109,18 @@ nav span:hover {
flex-wrap: wrap; flex-wrap: wrap;
} }
.led { .NeoPixel {
padding: 0.25em; padding: 0.25em;
margin: 0.125em; margin: 0.125em;
} }
.led_color { .NeoPixel_color {
border-radius: 0.25em; border-radius: 0.25em;
width: 1em; width: 1em;
height: 1em; height: 1em;
} }
.led_color_input { .NeoPixel_color_input {
opacity: 0; opacity: 0;
display: block; display: block;
width: 1em; width: 1em;

View File

@ -3,6 +3,7 @@ var socket;
function load() { function load() {
socket = init_websocket(); socket = init_websocket();
// initialize RelayOutlet SVG color
Object.entries(init_state).forEach(([device_id, sub_devs]) => { Object.entries(init_state).forEach(([device_id, sub_devs]) => {
let device = document.querySelector('#' + device_id); let device = document.querySelector('#' + device_id);
Object.entries(sub_devs).forEach(([sub_device_id, state]) => { Object.entries(sub_devs).forEach(([sub_device_id, state]) => {
@ -60,11 +61,12 @@ function onmessage (e) {
socket.events[event](data); socket.events[event](data);
} }
function onclose(e) { async function onclose(e) {
if (e.wasClean) { return; } // no need to reconnect if (e.wasClean) { return; } // no need to reconnect
console.log(e); console.log(e);
console.log('Websocket lost connection to server. Re-trying...'); console.log('Websocket lost connection to server. Re-trying...');
socket = init_websocket(); socket = init_websocket();
await sleep(5000);
} }
function onerror(e) { function onerror(e) {
@ -247,3 +249,7 @@ function get_object_from_svg(svg) {
} }
return null; return null;
} }
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

View File

@ -5,7 +5,7 @@
<link rel="stylesheet" type="text/css" href="/static/juice.css"> <link rel="stylesheet" type="text/css" href="/static/juice.css">
<script> <script>
const init_state = {{ init_state|tojson|safe }}; const init_state = {{ init_state|tojson|safe }};
const ws_uri = "{{ request.app.router['ws'].url_for() }}"; const ws_uri = "{{ url('ws') }}";
</script> </script>
<script type="text/javascript" src="/static/juice.js"></script> <script type="text/javascript" src="/static/juice.js"></script>
<script>window.onload = load;</script> <script>window.onload = load;</script>
@ -37,23 +37,19 @@
<div class="location editable"><span class="field_value">{{ device.location }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)">&#xe800;</span>{% endif %}</div> <div class="location editable"><span class="field_value">{{ device.location }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)">&#xe800;</span>{% endif %}</div>
<div class="ip_address editable"><span class="field_value">{{ device.ip_address }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)">&#xe800;</span>{% endif %}</div> <div class="ip_address editable"><span class="field_value">{{ device.ip_address }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)">&#xe800;</span>{% endif %}</div>
<div class="sub_devices"> <div class="sub_devices">
{% if device.type == 'RelayDevice' %} {% for sub_device in device.sub_devices %}
{% for sub_device in device.sub_devices %}
<div class="sub_device {{ sub_device.type }} {{ sub_device.id }}"> <div class="sub_device {{ sub_device.type }} {{ sub_device.id }}">
{% if device.type == 'RelayDevice' %}
<div class="id">{{ sub_device.id }}</div> <div class="id">{{ sub_device.id }}</div>
<object class="outlet_image" aria-label="Outlet Image" data="/static/outlet.svg"></object> <object class="outlet_image" aria-label="Outlet Image" data="/static/outlet.svg"></object>
<div class="description editable"><span class="field_value">{{ sub_device.description }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)">&#xe800;</span>{% endif %}</div> <div class="description editable"><span class="field_value">{{ sub_device.description }}</span>{% if not device.locked %}<span class="edit font-awesome" onclick="edit_field(this.parentElement)">&#xe800;</span>{% endif %}</div>
</div> {% elif device.type == 'LightStrip' %}
{% endfor %} <label class="NeoPixel_color" style="background-color: {{ sub_device.color|html_color }}">
{% elif device.type == 'LightStrip' %} <input class="NeoPixel_color_input" type="color" value="{{ sub_device.color|html_color }}">
{% for n in range(24) %}
<div class="sub_device led led{{n}}">
<label class="led_color">
<input class="led_color_input" type="color">
</label> </label>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
{% endif %}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}