#!/usr/bin/env python3 """ A hub for controlling IOT devices. """ import os import re import copy import json from flask import Flask, render_template, request, abort, jsonify from flask import Blueprint import auth import config import models from auth import auth_required from tools import make_error app_views = Blueprint('app_views', __name__) @app_views.route('/') @auth_required def index(): """ The index page. """ global network 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) @app_views.route('/toggle') @auth_required def toggle(): """ Toggles the state of a RelayDevice. """ global network device_id = request.args.get('device_id') sub_dev_id = request.args.get('sub_dev_id') for device in network: if device.id == device_id: break else: return make_error(404, "device_id not found") res = device.toggle(sub_dev_id) if not res: return make_error(404, "sub_dev_id not found") models.save_network(network) return res @app_views.route('/edit') @auth_required def edit(): """ Edits the text of a particular field. """ global network 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 network: if device.id == device_id: break else: return make_error(404, "device_id not found") if device.locked: return make_error(400, "device is locked for editing") if sub_dev_id: for sub_dev in device.sub_devices: 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 } models.save_network(network) return json.dumps(data) @app_views.route('/new_device') @auth_required def new_device(): """ Allows adding a new device. Accepts device_type parameter, returns the device_id. """ global network 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) models.save_network(network) data = {'device_id': device.id} return json.dumps(data) @app_views.route('/lock_device') @auth_required def lock_device(): """ Locks or unlocks a device to prevent or allow editing it's fields. """ global network 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 models.save_network(network) return jsonify(device_id=device.id, locked=device.locked) @app_views.route('/delete') @auth_required def delete(): """ Deletes a device. """ global network 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) models.save_network(network) return jsonify(True) app = Flask(__name__) app.register_blueprint(app_views, url_prefix=config.url_prefix) app.register_blueprint(auth.auth_views, url_prefix=config.url_prefix) app.jinja_env.undefined = "StrictUndefined" 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) network = models.init_network() if __name__ == '__main__': app.run(host='0.0.0.0', port=5300, debug=True)