2019-06-05 13:32:10 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
"""
|
|
|
|
A hub for controlling IOT devices.
|
|
|
|
"""
|
2019-06-13 20:28:47 -04:00
|
|
|
import os
|
2019-06-05 13:32:10 -04:00
|
|
|
import re
|
2019-06-08 03:56:30 -04:00
|
|
|
import copy
|
2019-06-05 13:32:10 -04:00
|
|
|
import json
|
|
|
|
|
2019-06-13 10:46:59 -04:00
|
|
|
from flask import Flask, render_template, request, abort, jsonify
|
2019-06-13 20:28:47 -04:00
|
|
|
from flask import Blueprint
|
|
|
|
|
2019-06-16 18:22:47 -04:00
|
|
|
import auth
|
2019-06-22 18:37:16 -04:00
|
|
|
import config
|
2019-07-02 08:56:17 -04:00
|
|
|
import models
|
2019-06-16 18:22:47 -04:00
|
|
|
from auth import auth_required
|
2019-06-22 18:37:16 -04:00
|
|
|
from tools import make_error
|
2019-06-05 13:32:10 -04:00
|
|
|
|
2019-06-13 20:28:47 -04:00
|
|
|
app_views = Blueprint('app_views', __name__)
|
2019-06-08 03:56:30 -04:00
|
|
|
|
2019-06-13 20:28:47 -04:00
|
|
|
@app_views.route('/')
|
2019-06-16 18:22:47 -04:00
|
|
|
@auth_required
|
2019-06-05 13:32:10 -04:00
|
|
|
def index():
|
|
|
|
"""
|
|
|
|
The index page.
|
|
|
|
"""
|
2019-07-02 08:56:17 -04:00
|
|
|
global network
|
2019-06-19 10:15:08 -04:00
|
|
|
init_state = {}
|
|
|
|
for device in network:
|
|
|
|
device_state = {}
|
|
|
|
for sub_dev in device.sub_devices:
|
|
|
|
device_state[sub_dev.id] = sub_dev.state
|
|
|
|
init_state[device.id] = device_state
|
|
|
|
return render_template('index.html', network=network,init_state=init_state)
|
2019-06-05 13:32:10 -04:00
|
|
|
|
|
|
|
|
2019-06-13 20:28:47 -04:00
|
|
|
@app_views.route('/toggle')
|
2019-06-16 18:22:47 -04:00
|
|
|
@auth_required
|
2019-06-06 13:16:59 -04:00
|
|
|
def toggle():
|
|
|
|
"""
|
|
|
|
Toggles the state of a RelayDevice.
|
|
|
|
"""
|
2019-07-02 08:56:17 -04:00
|
|
|
global network
|
2019-06-06 13:16:59 -04:00
|
|
|
device_id = request.args.get('device_id')
|
|
|
|
sub_dev_id = request.args.get('sub_dev_id')
|
|
|
|
|
2019-06-18 13:44:35 -04:00
|
|
|
for device in network:
|
2019-06-06 13:16:59 -04:00
|
|
|
if device.id == device_id:
|
|
|
|
break
|
|
|
|
else:
|
2019-06-13 10:46:59 -04:00
|
|
|
return make_error(404, "device_id not found")
|
2019-06-06 13:16:59 -04:00
|
|
|
res = device.toggle(sub_dev_id)
|
|
|
|
if not res:
|
2019-06-13 10:46:59 -04:00
|
|
|
return make_error(404, "sub_dev_id not found")
|
2019-07-02 08:56:17 -04:00
|
|
|
models.save_network(network)
|
2019-06-06 13:16:59 -04:00
|
|
|
return res
|
|
|
|
|
2019-06-13 20:28:47 -04:00
|
|
|
@app_views.route('/edit')
|
2019-06-16 18:22:47 -04:00
|
|
|
@auth_required
|
2019-06-13 10:46:59 -04:00
|
|
|
def edit():
|
|
|
|
"""
|
|
|
|
Edits the text of a particular field.
|
|
|
|
"""
|
2019-07-02 08:56:17 -04:00
|
|
|
global network
|
2019-06-13 10:46:59 -04:00
|
|
|
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')
|
|
|
|
|
2019-06-18 13:44:35 -04:00
|
|
|
for device in network:
|
2019-06-13 10:46:59 -04:00
|
|
|
if device.id == device_id:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
return make_error(404, "device_id not found")
|
|
|
|
|
2019-06-20 12:36:19 -04:00
|
|
|
if device.locked:
|
|
|
|
return make_error(400, "device is locked for editing")
|
|
|
|
|
2019-06-13 10:46:59 -04:00
|
|
|
if sub_dev_id:
|
2019-06-18 13:44:35 -04:00
|
|
|
for sub_dev in device.sub_devices:
|
2019-06-13 10:46:59 -04:00
|
|
|
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
|
|
|
|
}
|
2019-07-02 08:56:17 -04:00
|
|
|
models.save_network(network)
|
2019-06-13 10:46:59 -04:00
|
|
|
return json.dumps(data)
|
|
|
|
|
|
|
|
|
2019-06-20 07:49:29 -04:00
|
|
|
@app_views.route('/new_device')
|
2019-06-22 18:37:16 -04:00
|
|
|
@auth_required
|
2019-06-20 07:49:29 -04:00
|
|
|
def new_device():
|
|
|
|
"""
|
|
|
|
Allows adding a new device. Accepts device_type parameter, returns
|
|
|
|
the device_id.
|
|
|
|
"""
|
2019-07-02 08:56:17 -04:00
|
|
|
global network
|
2019-06-20 07:49:29 -04:00
|
|
|
device_type = request.args.get('device_type')
|
|
|
|
|
|
|
|
if device_type == 'RelayDevice':
|
|
|
|
device = 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)
|
2019-07-02 08:56:17 -04:00
|
|
|
models.save_network(network)
|
2019-06-20 07:49:29 -04:00
|
|
|
data = {'device_id': device.id}
|
|
|
|
return json.dumps(data)
|
|
|
|
|
|
|
|
|
2019-06-20 12:36:19 -04:00
|
|
|
@app_views.route('/lock_device')
|
2019-06-22 18:37:16 -04:00
|
|
|
@auth_required
|
2019-06-20 12:36:19 -04:00
|
|
|
def lock_device():
|
|
|
|
"""
|
|
|
|
Locks or unlocks a device to prevent or allow editing it's fields.
|
|
|
|
"""
|
2019-07-02 08:56:17 -04:00
|
|
|
global network
|
2019-06-20 12:36:19 -04:00
|
|
|
device_id = request.args.get('device_id')
|
|
|
|
locked = request.args.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
|
2019-07-02 08:56:17 -04:00
|
|
|
models.save_network(network)
|
2019-06-20 12:36:19 -04:00
|
|
|
|
|
|
|
return jsonify(device_id=device.id, locked=device.locked)
|
|
|
|
|
|
|
|
|
2019-06-20 14:51:00 -04:00
|
|
|
@app_views.route('/delete')
|
2019-06-22 18:37:16 -04:00
|
|
|
@auth_required
|
2019-06-20 14:51:00 -04:00
|
|
|
def delete():
|
|
|
|
"""
|
|
|
|
Deletes a device.
|
|
|
|
"""
|
2019-07-02 08:56:17 -04:00
|
|
|
global network
|
2019-06-20 14:51:00 -04:00
|
|
|
device_id = request.args.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)
|
2019-07-02 08:56:17 -04:00
|
|
|
models.save_network(network)
|
2019-06-20 14:51:00 -04:00
|
|
|
|
|
|
|
return jsonify(True)
|
|
|
|
|
|
|
|
|
2019-06-13 20:28:47 -04:00
|
|
|
app = Flask(__name__)
|
|
|
|
app.register_blueprint(app_views, url_prefix=config.url_prefix)
|
2019-06-16 18:22:47 -04:00
|
|
|
app.register_blueprint(auth.auth_views, url_prefix=config.url_prefix)
|
2019-06-22 18:37:16 -04:00
|
|
|
app.jinja_env.undefined = "StrictUndefined"
|
2019-06-16 18:22:47 -04:00
|
|
|
if os.path.isfile('secret_key'):
|
|
|
|
with open('secret_key', 'rb') as file:
|
|
|
|
app.secret_key = file.read()
|
|
|
|
else:
|
|
|
|
secret_key = os.urandom(32)
|
|
|
|
app.secret_key = secret_key
|
|
|
|
with open('secret_key', 'wb') as file:
|
|
|
|
file.write(secret_key)
|
2019-07-02 08:56:17 -04:00
|
|
|
network = models.init_network()
|
2019-06-13 20:28:47 -04:00
|
|
|
|
|
|
|
|
2019-06-05 13:32:10 -04:00
|
|
|
if __name__ == '__main__':
|
2019-06-22 18:37:16 -04:00
|
|
|
app.run(host='0.0.0.0', port=5300, debug=True)
|