#!/usr/bin/env python3 """ A hub for controlling IOT devices. """ import os import re import copy import json from aiohttp import web import jinja2 import aiohttp_jinja2 from aiohttp_jinja2 import render_template #import uvloop import requests import config import models import buckler_aiohttp from tools import make_error #uvloop.install() network = models.init_network() routes = web.RouteTableDef() @routes.get('/', name='index') async def index(request): """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 d = {'network': network, 'init_state': init_state} return render_template('index.html', request, d) @routes.get('/toggle', name='toggle') async def toggle(request): """Toggles the state of a RelayDevice.""" global network device_id = request.query.get('device_id') sub_dev_id = request.query.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 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 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 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(): """Initializes the application.""" app = web.Application(middlewares=[buckler_aiohttp.buckler_session]) 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_wrap = web.Application() app_wrap.add_subapp(config.url_prefix, app) return app_wrap