commit 0399f9076af524b78a74f5bfd1d2b8e181ac0cdd Author: iou1name Date: Wed Jun 5 13:32:10 2019 -0400 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f187277 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.swp +*.swo +devices.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f2a152 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Juice +A hub for controlling IOT devices. + +## Requirements +Python 3.6+ +Python packages: `flask gunicorn requests` + +## Install +1. Get on the floor +2. Walk the dinosaur + +## Usage +`gunicorn -b localhost:5300 -e SCRIPT_NAME=/juice juice:app` diff --git a/juice.py b/juice.py new file mode 100644 index 0000000..671ed99 --- /dev/null +++ b/juice.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +""" +A hub for controlling IOT devices. +""" +import re +import json + +import requests +from flask import Flask, render_template + + +class RelayDevice: + """ + 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 + a relay. Each device only has two outputs, `OUT0` and `OUT2`. + """ + def __init__(self): + self.id = "" + self.description = "" + self.location = "" + self.ip_address = "" + self.sub_devices = {} + + self.update() + + def update(self): + """ + Queries the physical device and updates the internal model as + appropriate. + """ + for name, device in self.sub_devices.items(): + res = requests.get(self.ip_address) + gpio0 = re.search(r"GPIO0: (\bLow|\bHigh)", res.text).groups()[0] + device.state = gpio0 == 'High' + + def from_dict(self, data): + """ + Initializes self with data from JSON dict. + """ + self.id = data['id'] + self.description = data['description'] + self.location = data['location'] + self.ip_address = data['ip_address'] + self.sub_devices = {} + for sub_dev_type, sub_devs in data['sub_devices'].items(): + if sub_dev_type == 'RelayOutlet': + self.sub_devices[sub_dev_type] = [] + for sub_dev in sub_devs: + sub_dev = RelayOutlet().from_dict(sub_dev) + self.sub_devices[sub_dev_type].append(sub_dev) + else: + raise ValueError(f"Unknown sub_device type {dev_name}") + return self + + +class RelayOutlet: + """ + Represents a single outlet on a RelayDevice. + """ + def __init__(self): + self.id = "" + self.description = "" + self.state = False + + def from_dict(self, data): + """ + Initializes self with data from JSON dict. + """ + self.id = data['id'] + self.description = data['description'] + self.state = data['state'] + return self + + +def init_network(): + """ + Initializes the network of IOT devices. + """ + with open("devices.json", 'r') as file: + data = file.read() + data = json.loads(data) + network = {} + for dev_type, devices in data.items(): + if dev_type == 'RelayDevice': + network[dev_type] = [] + for device in devices: + device = RelayDevice().from_dict(device) + network[dev_type].append(device) + else: + raise ValueError(f"Unknown device type {dev_name}") + return network + + +app = Flask(__name__) +network = init_network() + +@app.route('/') +def index(): + """ + The index page. + """ + return render_template('index.html', network=network) + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5300) diff --git a/static/juice.css b/static/juice.css new file mode 100644 index 0000000..e69de29 diff --git a/static/juice.js b/static/juice.js new file mode 100644 index 0000000..e69de29 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..9588821 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,31 @@ + + + + Juice + + + + + {% for dev_type, devices in network.items() %} + {% for device in devices %} +
+
{{ device.id }}
+
{{ device.description }}
+
{{ device.location }}
+
{{ device.ip_address }}
+
+ {% for sub_dev_type, sub_devs in device.sub_devices.items() %} + {% for sub_dev in sub_devs %} +
+
{{ sub_dev.id }}
+
OUTLET_IMAGE
+
{{ sub_dev.description }}
+
+ {% endfor %} + {% endfor %} +
+
+ {% endfor %} + {% endfor %} + +