master
informatic 2017-01-20 22:26:16 +01:00
parent d86ba8e823
commit d25308cfc5
7 changed files with 140 additions and 110 deletions

121
main.py
View File

@ -1,118 +1,23 @@
import flask
import paho.mqtt.client as mqtt
import logging
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')
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.config['PORT'] = 5000
app.config['BROKER'] = ('mqtt.waw.hackerspace.pl', 1883)
app.config['PREFIX'] = 'iot/'
app.config['TEMPLATES_AUTO_RELOAD'] = True
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
}
manager = SpejsiotManager(app)
@app.context_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 {
'render_endpoint': render_endpoint,
}
@ -128,7 +33,7 @@ def api_devices():
@app.route('/api/1/devices/<node_id>/')
def api_device_info(node_id):
node = manager.find_node(node_id)
print node
if not node:
flask.abort(404)
@ -139,7 +44,11 @@ def api_device_info(node_id):
def device_write(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__":
manager.run('127.0.0.1', 1883)
app.run()
manager.run(*app.config['BROKER'])
app.run(port=app.config['PORT'])

View File

@ -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
pkg-resources==0.0.0
Werkzeug==0.11.15

0
spejsiot/__init__.py Normal file
View File

91
spejsiot/manager.py Normal file
View File

@ -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)

25
spejsiot/rendering.py Normal file
View File

@ -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)

View File

@ -1,4 +1,4 @@
{% if endpoint['on'] == 'true' %}
{% if endpoint['on'] %}
<button class="btn btn-success btn-lg">ON</button>
{% else %}
<button class="btn btn-dange btn-lg">OFF</button>

View File

@ -1,4 +1,3 @@
<!DOCTYPE html>
<html lang="en">
<head>
@ -19,7 +18,7 @@
{% for name, dev in devices.items() %}
{% for name, obj in dev.endpoints.items() %}
<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">
<h3 class="panel-title"><small>{{ dev.node_id }} / {{ dev.properties['$name'] }}</small> {{ name }}</h3>
</div>