begin converting AJAX to Websocket
This commit is contained in:
parent
43026fa63a
commit
58f1bd2979
143
events.py
Normal file
143
events.py
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Websocket events."""
|
||||||
|
import types
|
||||||
|
|
||||||
|
import models
|
||||||
|
from models import network
|
||||||
|
|
||||||
|
|
||||||
|
async def toggle_outlet(ws, data):
|
||||||
|
"""Toggles the state of a RelayDevice."""
|
||||||
|
device_id = data.get('device_id')
|
||||||
|
sub_device_id = data.get('sub_device_id')
|
||||||
|
|
||||||
|
device = network.find(device_id)
|
||||||
|
if not device:
|
||||||
|
await ws.send_json({'ok': False, 'message': "device_id not found"})
|
||||||
|
return
|
||||||
|
state = device.toggle(sub_device_id)
|
||||||
|
if state is None:
|
||||||
|
await ws.send_json({'ok': False, 'message': "sub_device_id not found"})
|
||||||
|
return
|
||||||
|
models.save_network(network)
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data['ok'] = True
|
||||||
|
data['device_id'] = device_id
|
||||||
|
data['sub_device_id'] = sub_device_id
|
||||||
|
data['state'] = state
|
||||||
|
res = {'event': 'toggle_outlet', 'data': data}
|
||||||
|
await ws.send_json(res)
|
||||||
|
|
||||||
|
|
||||||
|
async def edit(ws, data):
|
||||||
|
"""Edits the text of a particular field."""
|
||||||
|
device_id = data.get('device_id')
|
||||||
|
sub_device_id = data.get('sub_device_id')
|
||||||
|
field = data.get('field')
|
||||||
|
value = data.get('value')
|
||||||
|
|
||||||
|
device = network.find(device_id)
|
||||||
|
if not device:
|
||||||
|
await ws.send_json({'ok': False, 'message': "device_id not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
if device.locked:
|
||||||
|
await ws.send_json({'ok': False, 'message': "device is locked for editing"})
|
||||||
|
return
|
||||||
|
|
||||||
|
if sub_device_id:
|
||||||
|
sub_device = device.find(sub_device_id)
|
||||||
|
if not sub_device:
|
||||||
|
await ws.send_json({'ok': False, 'message': "sub_device_id not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
if hasattr(sub_device, field):
|
||||||
|
setattr(sub_device, field, value)
|
||||||
|
else:
|
||||||
|
await ws.send_json({'ok': False, 'message': "sub_device field not found"})
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if hasattr(device, field):
|
||||||
|
setattr(device, field, value)
|
||||||
|
else:
|
||||||
|
await ws.send_json({'ok': False, 'message': "device field not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
res = {
|
||||||
|
'ok': True,
|
||||||
|
'field': field,
|
||||||
|
'value': value
|
||||||
|
}
|
||||||
|
models.save_network(network)
|
||||||
|
await ws.send_json(res)
|
||||||
|
|
||||||
|
|
||||||
|
async def new_device(ws, data):
|
||||||
|
"""
|
||||||
|
Allows adding a new device. Accepts device_type parameter, returns
|
||||||
|
the device_id.
|
||||||
|
"""
|
||||||
|
device_type = data.get('device_type')
|
||||||
|
|
||||||
|
if device_type == 'RelayDevice':
|
||||||
|
device = models.RelayDevice()
|
||||||
|
else:
|
||||||
|
await ws.send_json({'ok': False, 'message': "unknown device type"})
|
||||||
|
return
|
||||||
|
|
||||||
|
devices = [dev for dev in network if dev.type == device_type]
|
||||||
|
devices.sort(key=lambda dev: dev.id)
|
||||||
|
if not devices:
|
||||||
|
device.id = device_type + '01'
|
||||||
|
else:
|
||||||
|
num = re.search(r'(\d*)$', devices[-1].id).groups()
|
||||||
|
if not num:
|
||||||
|
device.id = device_type + '01'
|
||||||
|
else:
|
||||||
|
num = str(int(num[0]) + 1).zfill(2)
|
||||||
|
device.id = device_type + num
|
||||||
|
network.append(device)
|
||||||
|
models.save_network(network)
|
||||||
|
res = {'ok': True, 'device_id': device.id}
|
||||||
|
await ws.send_json(res)
|
||||||
|
|
||||||
|
|
||||||
|
async def lock_device(ws, data):
|
||||||
|
"""Locks or unlocks a device to prevent or allow editing it's fields."""
|
||||||
|
device_id = data.get('device_id')
|
||||||
|
locked = data.get('locked') == 'true'
|
||||||
|
|
||||||
|
device = network.find(device_id)
|
||||||
|
if not device:
|
||||||
|
await ws.send_json({'ok': False, 'message': "device_id not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
if locked:
|
||||||
|
device.locked = True
|
||||||
|
else:
|
||||||
|
device.locked = False
|
||||||
|
models.save_network(network)
|
||||||
|
|
||||||
|
await ws.send_json({'ok': True, 'device_id': device.id, 'locked': device.locked})
|
||||||
|
|
||||||
|
|
||||||
|
async def delete(ws, data):
|
||||||
|
"""Deletes a device."""
|
||||||
|
device_id = data.get('device_id')
|
||||||
|
|
||||||
|
device = network.find(device_id)
|
||||||
|
if not device:
|
||||||
|
await ws.send_json({'ok': False, 'message': "device_id not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
network.remove(device)
|
||||||
|
models.save_network(network)
|
||||||
|
|
||||||
|
await ws.send_json({'ok': True})
|
||||||
|
|
||||||
|
|
||||||
|
events = {}
|
||||||
|
for obj in dir():
|
||||||
|
if type(locals()[obj]) == types.FunctionType:
|
||||||
|
events[locals()[obj].__name__] = locals()[obj]
|
162
juice.py
162
juice.py
|
@ -7,177 +7,73 @@ import re
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web, WSMsgType
|
||||||
import jinja2
|
import jinja2
|
||||||
import aiohttp_jinja2
|
import aiohttp_jinja2
|
||||||
from aiohttp_jinja2 import render_template
|
from aiohttp_jinja2 import render_template
|
||||||
#import uvloop
|
import uvloop
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
import events
|
||||||
import models
|
import models
|
||||||
import buckler_aiohttp
|
import buckler_aiohttp
|
||||||
from tools import make_error
|
from tools import make_error
|
||||||
|
from models import network
|
||||||
|
|
||||||
#uvloop.install()
|
uvloop.install()
|
||||||
network = models.init_network()
|
|
||||||
routes = web.RouteTableDef()
|
routes = web.RouteTableDef()
|
||||||
|
|
||||||
@routes.get('/', name='index')
|
@routes.get('/', name='index')
|
||||||
async def index(request):
|
async def index(request):
|
||||||
"""The index page."""
|
"""The index page."""
|
||||||
global network
|
|
||||||
init_state = {}
|
init_state = {}
|
||||||
for device in network:
|
for device in network:
|
||||||
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
|
||||||
init_state[device.id] = device_state
|
init_state[device.id] = device_state
|
||||||
d = {'network': network, 'init_state': init_state}
|
d = {'network': network, 'init_state': init_state, 'request': request}
|
||||||
return render_template('index.html', request, d)
|
return render_template('index.html', request, d)
|
||||||
|
|
||||||
|
|
||||||
@routes.get('/toggle', name='toggle')
|
@routes.get('/ws', name='ws')
|
||||||
async def toggle(request):
|
async def websocket_handler(request):
|
||||||
"""Toggles the state of a RelayDevice."""
|
"""The websocket endpoint."""
|
||||||
global network
|
ws = web.WebSocketResponse(heartbeat=30)
|
||||||
device_id = request.query.get('device_id')
|
ws_ready = ws.can_prepare(request)
|
||||||
sub_dev_id = request.query.get('sub_dev_id')
|
if not ws_ready.ok:
|
||||||
|
return web.Response(text="Cannot start websocket.")
|
||||||
|
await ws.prepare(request)
|
||||||
|
|
||||||
for device in network:
|
async for msg in ws:
|
||||||
if device.id == device_id:
|
if msg.type != WSMsgType.TEXT:
|
||||||
break
|
break
|
||||||
else:
|
try:
|
||||||
return make_error(404, "device_id not found")
|
data = json.loads(msg.data)
|
||||||
res = device.toggle(sub_dev_id)
|
except json.JSONDecodeError:
|
||||||
if not res:
|
|
||||||
return make_error(404, "sub_dev_id not found")
|
|
||||||
models.save_network(network)
|
|
||||||
return web.json_response(res)
|
|
||||||
|
|
||||||
@routes.get('/edit', name='edit')
|
|
||||||
async def edit(request):
|
|
||||||
"""Edits the text of a particular field."""
|
|
||||||
global network
|
|
||||||
device_id = request.query.get('device_id')
|
|
||||||
sub_dev_id = request.query.get('sub_dev_id')
|
|
||||||
field = request.query.get('field')
|
|
||||||
value = request.query.get('value')
|
|
||||||
|
|
||||||
for device in network:
|
|
||||||
if device.id == device_id:
|
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
return make_error(404, "device_id not found")
|
|
||||||
|
|
||||||
if device.locked:
|
event = data.get('event')
|
||||||
return make_error(400, "device is locked for editing")
|
if not event or event not in events.events.keys():
|
||||||
|
|
||||||
if sub_dev_id:
|
|
||||||
for sub_dev in device.sub_devices:
|
|
||||||
if sub_dev.id == sub_dev_id:
|
|
||||||
break
|
break
|
||||||
else:
|
await events.events[event](ws, data.get('data'))
|
||||||
return make_error(404, "sub_dev_id not found")
|
await ws.close()
|
||||||
if hasattr(sub_dev, field):
|
return ws
|
||||||
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
|
|
||||||
}
|
|
||||||
models.save_network(network)
|
|
||||||
return web.json_response(data)
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get('/new_device', name='new_device')
|
|
||||||
async def new_device(request):
|
|
||||||
"""
|
|
||||||
Allows adding a new device. Accepts device_type parameter, returns
|
|
||||||
the device_id.
|
|
||||||
"""
|
|
||||||
global network
|
|
||||||
device_type = request.query.get('device_type')
|
|
||||||
|
|
||||||
if device_type == 'RelayDevice':
|
|
||||||
device = models.RelayDevice()
|
|
||||||
else:
|
|
||||||
return make_error(400, "Unknown device type")
|
|
||||||
|
|
||||||
devices = [dev for dev in network if dev.type == device_type]
|
|
||||||
devices.sort(key=lambda dev: dev.type)
|
|
||||||
if not devices:
|
|
||||||
device.id = device_type + '01'
|
|
||||||
else:
|
|
||||||
num = re.search(r'(\d*)$', devices[-1].id).groups()
|
|
||||||
if not num:
|
|
||||||
device.id = device_type + '01'
|
|
||||||
else:
|
|
||||||
num = str(int(num[0]) + 1).zfill(2)
|
|
||||||
device.id = device_type + num
|
|
||||||
network.append(device)
|
|
||||||
models.save_network(network)
|
|
||||||
data = {'device_id': device.id}
|
|
||||||
return web.json_response(data)
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get('/lock_device', name='lock_device')
|
|
||||||
async def lock_device(request):
|
|
||||||
"""Locks or unlocks a device to prevent or allow editing it's fields."""
|
|
||||||
global network
|
|
||||||
device_id = request.query.get('device_id')
|
|
||||||
locked = request.query.get('locked') == 'true'
|
|
||||||
|
|
||||||
for device in network:
|
|
||||||
if device.id == device_id:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return make_error(404, "device_id not found")
|
|
||||||
|
|
||||||
if locked:
|
|
||||||
device.locked = True
|
|
||||||
else:
|
|
||||||
device.locked = False
|
|
||||||
models.save_network(network)
|
|
||||||
|
|
||||||
return web.json_response({'device_id': device.id, 'locked': device.locked})
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get('/delete', name='delete')
|
|
||||||
async def delete(request):
|
|
||||||
"""Deletes a device."""
|
|
||||||
global network
|
|
||||||
device_id = request.query.get('device_id')
|
|
||||||
|
|
||||||
for device in network:
|
|
||||||
if device.id == device_id:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return make_error(404, "device_id not found")
|
|
||||||
|
|
||||||
network.remove(device)
|
|
||||||
models.save_network(network)
|
|
||||||
|
|
||||||
return web.json_response(True)
|
|
||||||
|
|
||||||
|
|
||||||
async def init_app():
|
async def init_app():
|
||||||
"""Initializes the application."""
|
"""Initializes the application."""
|
||||||
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, loader=jinja2.FileSystemLoader('templates'))
|
||||||
#app.on_startup.append(start_background_tasks)
|
|
||||||
#app.on_cleanup.append(cleanup_background_tasks)
|
|
||||||
|
|
||||||
app.router.add_routes(routes)
|
app.router.add_routes(routes)
|
||||||
|
|
||||||
app_wrap = web.Application()
|
app_wrap = web.Application()
|
||||||
app_wrap.add_subapp(config.url_prefix, app)
|
app_wrap.add_subapp(config.url_prefix, app)
|
||||||
return app_wrap
|
return app_wrap
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = init_app()
|
||||||
|
aiohttp.web.run_app(app, host='0.0.0.0', port=5300)
|
||||||
|
|
59
models.py
59
models.py
|
@ -7,7 +7,41 @@ import json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
class RelayDevice:
|
class Network(list):
|
||||||
|
"""
|
||||||
|
Represents a network of IOT devices.
|
||||||
|
"""
|
||||||
|
def find(self, device_id):
|
||||||
|
"""
|
||||||
|
Searches through the network and returns the matching device,
|
||||||
|
or None.
|
||||||
|
"""
|
||||||
|
for device in self:
|
||||||
|
if device.id == device_id:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return device
|
||||||
|
|
||||||
|
|
||||||
|
class Device:
|
||||||
|
"""
|
||||||
|
Represents a generic device on the Network.
|
||||||
|
"""
|
||||||
|
def find(self, sub_device_id):
|
||||||
|
"""
|
||||||
|
Searches through the devices sub_devices and returns the matching
|
||||||
|
sub_device, or None.
|
||||||
|
"""
|
||||||
|
for sub_device in self.sub_devices:
|
||||||
|
if sub_device.id == sub_device_id:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return sub_device
|
||||||
|
|
||||||
|
|
||||||
|
class RelayDevice(Device):
|
||||||
"""
|
"""
|
||||||
Represents a relay receptacle device controlled by an ESP-01. The
|
Represents a relay receptacle device controlled by an ESP-01. The
|
||||||
ESP-01 acts a simple switch to turn each receptable on or off via
|
ESP-01 acts a simple switch to turn each receptable on or off via
|
||||||
|
@ -43,27 +77,25 @@ class RelayDevice:
|
||||||
gpio0 = re.search(r"GPIO0: (\bLow|\bHigh)", res.text).groups()[0]
|
gpio0 = re.search(r"GPIO0: (\bLow|\bHigh)", res.text).groups()[0]
|
||||||
sub_dev.state = gpio0 == 'High'
|
sub_dev.state = gpio0 == 'High'
|
||||||
|
|
||||||
def toggle(self, sub_dev_id):
|
def toggle(self, sub_device_id):
|
||||||
"""
|
"""
|
||||||
Toggles the state of a sub device.
|
Toggles the state of a sub device.
|
||||||
"""
|
"""
|
||||||
for sub_dev in self.sub_devices:
|
sub_device = self.find(sub_device_id)
|
||||||
if sub_dev.id == sub_dev_id:
|
if not sub_device:
|
||||||
break
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
params = {sub_dev.gpio: 'toggle'}
|
params = {sub_device.gpio: 'toggle'}
|
||||||
res = requests.get('http://' + self.ip_address + '/gpio',params=params)
|
res = requests.get('http://' + self.ip_address + '/gpio',params=params)
|
||||||
res.raise_for_status()
|
res.raise_for_status()
|
||||||
|
|
||||||
state = re.search(
|
state = re.search(
|
||||||
rf"GPIO{sub_dev.gpio}: (\bLow|\bHigh)",
|
rf"GPIO{sub_device.gpio}: (\bLow|\bHigh)",
|
||||||
res.text
|
res.text
|
||||||
).groups()[0] == 'High'
|
).groups()[0] == 'High'
|
||||||
sub_dev.state = state
|
sub_device.state = state
|
||||||
|
|
||||||
return {'ok': True, sub_dev_id: state}
|
return state
|
||||||
|
|
||||||
def from_dict(self, data):
|
def from_dict(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -102,7 +134,7 @@ class RelayOutlet:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class LightStrip:
|
class LightStrip(Device):
|
||||||
"""
|
"""
|
||||||
Represents an RGB LED light strip controller.
|
Represents an RGB LED light strip controller.
|
||||||
"""
|
"""
|
||||||
|
@ -146,7 +178,7 @@ def init_network(filepath="devices.json"):
|
||||||
with open(filepath, 'r') as file:
|
with open(filepath, 'r') as file:
|
||||||
data = file.read()
|
data = file.read()
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
network = []
|
network = Network()
|
||||||
for device_dict in data:
|
for device_dict in data:
|
||||||
if device_dict.get('type') == 'RelayDevice':
|
if device_dict.get('type') == 'RelayDevice':
|
||||||
device = RelayDevice().from_dict(device_dict)
|
device = RelayDevice().from_dict(device_dict)
|
||||||
|
@ -165,3 +197,6 @@ def save_network(network, filepath="devices.json"):
|
||||||
"""
|
"""
|
||||||
with open(filepath, 'w') as file:
|
with open(filepath, 'w') as file:
|
||||||
file.write(json.dumps(network, cls=DeviceEncoder, indent='\t'))
|
file.write(json.dumps(network, cls=DeviceEncoder, indent='\t'))
|
||||||
|
|
||||||
|
|
||||||
|
network = init_network()
|
||||||
|
|
113
static/juice.js
113
static/juice.js
|
@ -1,4 +1,8 @@
|
||||||
|
var socket;
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
|
socket = init_websocket();
|
||||||
|
|
||||||
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_dev_id, state]) => {
|
Object.entries(sub_devs).forEach(([sub_dev_id, state]) => {
|
||||||
|
@ -18,41 +22,74 @@ function load() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_object_from_svg(svg) {
|
/* Websocket setup */
|
||||||
var all_objects = document.getElementsByTagName("object");
|
function init_websocket() {
|
||||||
for (var i=0; i < all_objects.length; i++) {
|
let socket = new Websocket('wss://' + window.location.hostname + ws_uri);
|
||||||
if (svg === all_objects[i].getSVGDocument().firstElementChild) {
|
socket._send = socket.send;
|
||||||
return all_objects[i];
|
socket.send = function(event_title, data) {
|
||||||
|
data = JSON.stringify({event: event_title, data: data});
|
||||||
|
if (socket.readyState == 0) {
|
||||||
|
console.log("Socket is still opening!");
|
||||||
}
|
}
|
||||||
|
socket._send(data);
|
||||||
}
|
}
|
||||||
return null;
|
socket.onmessage = onmessage;
|
||||||
|
socket.onclose = onclose;
|
||||||
|
socket.onerror = onerror;
|
||||||
|
socket.events = {};
|
||||||
|
socket.events['toggle_outlet'] = toggle_outlet_recv;
|
||||||
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle_outlet(svg) {
|
function onmessage (e) {
|
||||||
let sub_dev = get_object_from_svg(svg).parentElement;
|
let data = JSON.parse(e.data);
|
||||||
let params = {
|
let event = data.event;
|
||||||
device_id: sub_dev.parentElement.parentElement.id,
|
data = data.data;
|
||||||
sub_dev_id: sub_dev.querySelector('.id').innerText,
|
if (socket.events[event] === undefined) { return; }
|
||||||
};
|
socket.events[event](data);
|
||||||
let query = Object.keys(params)
|
}
|
||||||
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
|
||||||
.join('&');
|
function onclose(e) {
|
||||||
fetch(window.location.href + 'toggle?' + query)
|
if (e.wasClean) { return; } // no need to reconnect
|
||||||
.then(function(response) {
|
console.log(e);
|
||||||
return response.json();
|
console.log('Websocket lost connection to server. Re-trying...');
|
||||||
})
|
socket = init_websocket();
|
||||||
.then(function(json) {
|
}
|
||||||
if (!json.ok) { throw new Error('HTTP error, status = ' + json.status + ', message = ' + json.message); }
|
|
||||||
if (json[sub_dev.querySelector('.id').innerText]) {
|
function onerror(e) {
|
||||||
|
console.log("Websocket error!")
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Websocket receive */
|
||||||
|
function toggle_outlet_recv(data) {
|
||||||
|
if (!data.ok) {
|
||||||
|
throw new Error('Socket Event Error: toggle_outlet, message = ' + data.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let device = document.querySelector('#' + data.device_id);
|
||||||
|
let sub_device = document.querySelector('.' + data.sub_device_id);
|
||||||
|
let svg = sub_device.querySelector('.outlet_image').getSVGDocument().firstChild;
|
||||||
|
if (data.state === true) {
|
||||||
svg.classList.remove('off');
|
svg.classList.remove('off');
|
||||||
svg.classList.add('on');
|
svg.classList.add('on');
|
||||||
} else {
|
} else {
|
||||||
svg.classList.remove('on');
|
svg.classList.remove('on');
|
||||||
svg.classList.add('off');
|
svg.classList.add('off');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Websocket send */
|
||||||
|
function toggle_outlet(svg) {
|
||||||
|
let sub_dev = get_object_from_svg(svg).parentElement;
|
||||||
|
let data = {
|
||||||
|
device_id: sub_dev.parentElement.parentElement.id,
|
||||||
|
sub_device_id: sub_dev.querySelector('.id').innerText,
|
||||||
|
};
|
||||||
|
socket.send('toggle_outlet', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AJAX */
|
||||||
function edit_field(field) {
|
function edit_field(field) {
|
||||||
let value = field.firstElementChild.innerText;
|
let value = field.firstElementChild.innerText;
|
||||||
let input = document.createElement('input');
|
let input = document.createElement('input');
|
||||||
|
@ -217,25 +254,6 @@ function delete_device(device) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function delete_key(key_id) {
|
|
||||||
if (!window.confirm("Are you sure you want to delete this key? This action may leave you unable to access your account.")) { return; }
|
|
||||||
let params = {
|
|
||||||
key_id: key_id,
|
|
||||||
};
|
|
||||||
let query = Object.keys(params)
|
|
||||||
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
|
||||||
.join('&');
|
|
||||||
|
|
||||||
fetch(url_prefix + '/delete_key?' + query)
|
|
||||||
.then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function(json) {
|
|
||||||
if (!json.ok) { return; }
|
|
||||||
document.querySelector('#key_' + key_id).remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delete_token(token_id) {
|
function delete_token(token_id) {
|
||||||
if (!window.confirm("Are you sure you want to delete this token?")) { return; }
|
if (!window.confirm("Are you sure you want to delete this token?")) { return; }
|
||||||
let params = {
|
let params = {
|
||||||
|
@ -254,3 +272,14 @@ function delete_token(token_id) {
|
||||||
document.querySelector('#token_' + token_id).remove();
|
document.querySelector('#token_' + token_id).remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
function get_object_from_svg(svg) {
|
||||||
|
let all_objects = document.getElementsByTagName("object");
|
||||||
|
for (let i=0; i < all_objects.length; i++) {
|
||||||
|
if (svg === all_objects[i].getSVGDocument().firstElementChild) {
|
||||||
|
return all_objects[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Juice</title>
|
<title>Juice</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/juice.css">
|
<link rel="stylesheet" type="text/css" href="/static/juice.css">
|
||||||
|
<script>
|
||||||
|
const init_state = {{ init_state|tojson|safe }};
|
||||||
|
const ws_uri = "{{ request.app.router['ws'].url_for() }}";
|
||||||
|
</script>
|
||||||
<script type="text/javascript" src="/static/juice.js"></script>
|
<script type="text/javascript" src="/static/juice.js"></script>
|
||||||
<script>var init_state = {{ init_state|tojson|safe }};</script>
|
|
||||||
<script>window.onload = load;</script>
|
<script>window.onload = load;</script>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||||
<meta name="description" content="An IOT hub.">
|
<meta name="description" content="An IOT hub.">
|
||||||
|
@ -43,7 +46,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% elif device.type == 'LightStrip' %}
|
{% elif device.type == 'LightStrip' %}
|
||||||
{% for n in range(48) %}
|
{% for n in range(24) %}
|
||||||
<div class="sub_device led led{{n}}">
|
<div class="sub_device led led{{n}}">
|
||||||
<label class="led_color">
|
<label class="led_color">
|
||||||
<input class="led_color_input" type="color">
|
<input class="led_color_input" type="color">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user