Refactor
parent
d86ba8e823
commit
d25308cfc5
121
main.py
121
main.py
|
@ -1,118 +1,23 @@
|
||||||
import flask
|
import flask
|
||||||
import paho.mqtt.client as mqtt
|
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from collections import defaultdict
|
import threading
|
||||||
|
|
||||||
|
from spejsiot.manager import SpejsiotManager
|
||||||
|
from spejsiot.rendering import render_endpoint
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG, format='[%(asctime)-15s] %(name)-10s %(levelname)7s: %(message)s')
|
logging.basicConfig(level=logging.DEBUG, format='[%(asctime)-15s] %(name)-10s %(levelname)7s: %(message)s')
|
||||||
|
|
||||||
|
|
||||||
class SpejsiotDevice(object):
|
|
||||||
node_id = None
|
|
||||||
properties = None
|
|
||||||
endpoints = None
|
|
||||||
|
|
||||||
def __init__(self, node_id):
|
|
||||||
self.node_id = node_id
|
|
||||||
self.properties = {}
|
|
||||||
self.endpoints = defaultdict(dict)
|
|
||||||
|
|
||||||
def dictify(self):
|
|
||||||
return dict(self.properties, **self.endpoints)
|
|
||||||
|
|
||||||
|
|
||||||
class SpejsiotManager(mqtt.Client):
|
|
||||||
devices = dict()
|
|
||||||
logger = logging.getLogger('manager')
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(SpejsiotManager, self).__init__()
|
|
||||||
|
|
||||||
def run(self, broker, port):
|
|
||||||
self.connect(broker, port, 60)
|
|
||||||
self.loop_start()
|
|
||||||
|
|
||||||
def on_connect(self, client, userdata, flags, rc):
|
|
||||||
self.logger.info('Connected, rc: %d', rc)
|
|
||||||
self.subscribe('iot/+/+')
|
|
||||||
self.subscribe('iot/+/+/+')
|
|
||||||
|
|
||||||
def on_message(self, client, userdata, msg):
|
|
||||||
try:
|
|
||||||
self.logger.info('Message received %s: %s', msg.topic, msg.payload)
|
|
||||||
_, node_id, topic = msg.topic.split('/', 2)
|
|
||||||
|
|
||||||
if node_id not in self.devices.keys():
|
|
||||||
self.devices[node_id] = SpejsiotDevice(node_id)
|
|
||||||
|
|
||||||
if msg.payload == 'true':
|
|
||||||
value = True
|
|
||||||
elif msg.payload == 'false':
|
|
||||||
value = False
|
|
||||||
else:
|
|
||||||
value = msg.payload
|
|
||||||
|
|
||||||
if topic.startswith('$'):
|
|
||||||
self.devices[node_id].properties[topic] = value
|
|
||||||
else:
|
|
||||||
endpoint, prop = topic.split('/', 1)
|
|
||||||
self.devices[node_id].endpoints[endpoint][prop] = value
|
|
||||||
except:
|
|
||||||
self.logger.exception('fuckup')
|
|
||||||
|
|
||||||
def find_node(self, node_id):
|
|
||||||
if node_id in self.devices:
|
|
||||||
return self.devices[node_id]
|
|
||||||
|
|
||||||
for n in self.devices.values():
|
|
||||||
if n.properties.get('$name', None) == node_id:
|
|
||||||
return n
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def handle_request(self, node_id, endpoint, prop, value):
|
|
||||||
node = self.find_node(node_id)
|
|
||||||
if not node:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.publish('iot/%s/%s/%s/set' % (
|
|
||||||
node.node_id, endpoint, prop
|
|
||||||
), value, retain=True)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
manager = SpejsiotManager()
|
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
app.config['PORT'] = 5000
|
||||||
|
app.config['BROKER'] = ('mqtt.waw.hackerspace.pl', 1883)
|
||||||
|
app.config['PREFIX'] = 'iot/'
|
||||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||||
|
|
||||||
|
manager = SpejsiotManager(app)
|
||||||
class EndpointRenderer(object):
|
|
||||||
@classmethod
|
|
||||||
def render(cls, device, endpoint):
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultRenderer(EndpointRenderer):
|
|
||||||
@classmethod
|
|
||||||
def render(cls, device, endpoint):
|
|
||||||
return flask.Markup(flask.render_template(
|
|
||||||
'endpoints/%s.html' % endpoint.get('$type'),
|
|
||||||
endpoint=endpoint, device=device))
|
|
||||||
|
|
||||||
|
|
||||||
endpoint_renderers = {
|
|
||||||
None: DefaultRenderer
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def utility_processor():
|
def utility_processor():
|
||||||
def render_endpoint(device, endpoint):
|
|
||||||
t = endpoint.get('$type', None)
|
|
||||||
if t in endpoint_renderers:
|
|
||||||
return endpoint_renderers[t].render(device, endpoint)
|
|
||||||
else:
|
|
||||||
return endpoint_renderers[None].render(device, endpoint)
|
|
||||||
return {
|
return {
|
||||||
'render_endpoint': render_endpoint,
|
'render_endpoint': render_endpoint,
|
||||||
}
|
}
|
||||||
|
@ -128,7 +33,7 @@ def api_devices():
|
||||||
@app.route('/api/1/devices/<node_id>/')
|
@app.route('/api/1/devices/<node_id>/')
|
||||||
def api_device_info(node_id):
|
def api_device_info(node_id):
|
||||||
node = manager.find_node(node_id)
|
node = manager.find_node(node_id)
|
||||||
print node
|
|
||||||
if not node:
|
if not node:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
|
@ -139,7 +44,11 @@ def api_device_info(node_id):
|
||||||
def device_write(node_id, endpoint, prop, value):
|
def device_write(node_id, endpoint, prop, value):
|
||||||
return flask.jsonify(manager.handle_request(node_id, endpoint, prop, value))
|
return flask.jsonify(manager.handle_request(node_id, endpoint, prop, value))
|
||||||
|
|
||||||
|
@app.route('/api/1/devices/<node_id>/<endpoint>/<prop>', methods=['PUT'])
|
||||||
|
def device_put(node_id, endpoint, prop):
|
||||||
|
return flask.jsonify(manager.handle_request(node_id, endpoint, prop, flask.request.json['value']))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
manager.run('127.0.0.1', 1883)
|
manager.run(*app.config['BROKER'])
|
||||||
app.run()
|
app.run(port=app.config['PORT'])
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
Flask==0.11.1
|
click==6.7
|
||||||
|
Flask==0.12
|
||||||
|
itsdangerous==0.24
|
||||||
|
Jinja2==2.9.4
|
||||||
|
MarkupSafe==0.23
|
||||||
paho-mqtt==1.2
|
paho-mqtt==1.2
|
||||||
|
pkg-resources==0.0.0
|
||||||
|
Werkzeug==0.11.15
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
from collections import defaultdict
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class SpejsiotDevice(object):
|
||||||
|
node_id = None
|
||||||
|
properties = None
|
||||||
|
endpoints = None
|
||||||
|
manager = None
|
||||||
|
|
||||||
|
def __init__(self, node_id, manager=None):
|
||||||
|
self.node_id = node_id
|
||||||
|
self.manager = manager
|
||||||
|
self.properties = {}
|
||||||
|
self.endpoints = defaultdict(dict)
|
||||||
|
|
||||||
|
def dictify(self):
|
||||||
|
return dict(self.properties, **self.endpoints)
|
||||||
|
|
||||||
|
def set(self, endpoint, prop, value):
|
||||||
|
self.manager.publish('%s%s/%s/%s/set' % (
|
||||||
|
self.manager.app.config['PREFIX'],
|
||||||
|
self.node_id,
|
||||||
|
endpoint,
|
||||||
|
prop,
|
||||||
|
), value, retain=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
class SpejsiotManager(mqtt.Client):
|
||||||
|
devices = dict()
|
||||||
|
logger = logging.getLogger('manager')
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
super(SpejsiotManager, self).__init__()
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
def on_connect(self, client, userdata, flags, rc):
|
||||||
|
self.logger.info('Connected, rc: %d', rc)
|
||||||
|
self.subscribe(self.app.config['PREFIX'] + '+/+')
|
||||||
|
self.subscribe(self.app.config['PREFIX'] + '+/+/+')
|
||||||
|
|
||||||
|
def run(self, broker, port):
|
||||||
|
self.connect(broker, port, 60)
|
||||||
|
self.loop_start()
|
||||||
|
print(self.on_connect)
|
||||||
|
|
||||||
|
def on_message(self, client, userdata, msg):
|
||||||
|
try:
|
||||||
|
self.logger.info('Message received %s: %s', msg.topic, msg.payload)
|
||||||
|
topic = msg.topic[len(self.app.config['PREFIX']):]
|
||||||
|
node_id, topic = topic.split('/', 1)
|
||||||
|
|
||||||
|
if node_id not in self.devices.keys():
|
||||||
|
self.devices[node_id] = SpejsiotDevice(node_id, self)
|
||||||
|
|
||||||
|
if msg.payload == b'true':
|
||||||
|
value = True
|
||||||
|
elif msg.payload == b'false':
|
||||||
|
value = False
|
||||||
|
else:
|
||||||
|
value = msg.payload.decode('utf-8')
|
||||||
|
|
||||||
|
if topic.startswith('$'):
|
||||||
|
self.devices[node_id].properties[topic] = value
|
||||||
|
else:
|
||||||
|
endpoint, prop = topic.split('/', 1)
|
||||||
|
self.devices[node_id].endpoints[endpoint][prop] = value
|
||||||
|
except:
|
||||||
|
self.logger.exception('fuckup')
|
||||||
|
|
||||||
|
def find_node(self, node_id):
|
||||||
|
if node_id in self.devices:
|
||||||
|
return self.devices[node_id]
|
||||||
|
|
||||||
|
for n in self.devices.values():
|
||||||
|
if n.properties.get('$name', None) == node_id:
|
||||||
|
return n
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def handle_request(self, node_id, endpoint, prop, value):
|
||||||
|
node = self.find_node(node_id)
|
||||||
|
if not node:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return node.set(endpoint, prop, value)
|
||||||
|
|
||||||
|
def on_log(self, client, userdata, level, buf):
|
||||||
|
self.logger.debug('[%r] %r', level, buf)
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
from flask import Markup, render_template
|
||||||
|
|
||||||
|
class EndpointRenderer(object):
|
||||||
|
@classmethod
|
||||||
|
def render(cls, device, endpoint):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
class DefaultRenderer(EndpointRenderer):
|
||||||
|
@classmethod
|
||||||
|
def render(cls, device, endpoint):
|
||||||
|
return Markup(render_template(
|
||||||
|
'endpoints/%s.html' % endpoint.get('$type'),
|
||||||
|
endpoint=endpoint, device=device))
|
||||||
|
|
||||||
|
endpoint_renderers = {
|
||||||
|
None: DefaultRenderer
|
||||||
|
}
|
||||||
|
|
||||||
|
def render_endpoint(device, endpoint):
|
||||||
|
t = endpoint.get('$type', None)
|
||||||
|
if t in endpoint_renderers:
|
||||||
|
return endpoint_renderers[t].render(device, endpoint)
|
||||||
|
else:
|
||||||
|
return endpoint_renderers[None].render(device, endpoint)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{% if endpoint['on'] == 'true' %}
|
{% if endpoint['on'] %}
|
||||||
<button class="btn btn-success btn-lg">ON</button>
|
<button class="btn btn-success btn-lg">ON</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-dange btn-lg">OFF</button>
|
<button class="btn btn-dange btn-lg">OFF</button>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
{% for name, dev in devices.items() %}
|
{% for name, dev in devices.items() %}
|
||||||
{% for name, obj in dev.endpoints.items() %}
|
{% for name, obj in dev.endpoints.items() %}
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="panel {% if dev.online %}panel-primary{% else %}panel-default{% endif %}">
|
<div class="panel {% if dev.properties['$online'] %}panel-primary{% else %}panel-default{% endif %}">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title"><small>{{ dev.node_id }} / {{ dev.properties['$name'] }}</small> {{ name }}</h3>
|
<h3 class="panel-title"><small>{{ dev.node_id }} / {{ dev.properties['$name'] }}</small> {{ name }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue