diff --git a/main.py b/main.py index 7a3f245..cf3e373 100644 --- a/main.py +++ b/main.py @@ -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//') 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///', 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']) diff --git a/requirements.txt b/requirements.txt index 0db9323..fc85534 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/spejsiot/__init__.py b/spejsiot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/spejsiot/manager.py b/spejsiot/manager.py new file mode 100644 index 0000000..f03cf26 --- /dev/null +++ b/spejsiot/manager.py @@ -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) + diff --git a/spejsiot/rendering.py b/spejsiot/rendering.py new file mode 100644 index 0000000..8ffcb2f --- /dev/null +++ b/spejsiot/rendering.py @@ -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) + diff --git a/templates/endpoints/output.html b/templates/endpoints/output.html index e3b5fdf..b74c2b3 100644 --- a/templates/endpoints/output.html +++ b/templates/endpoints/output.html @@ -1,4 +1,4 @@ -{% if endpoint['on'] == 'true' %} +{% if endpoint['on'] %} {% else %} diff --git a/templates/index.html b/templates/index.html index e98db2c..99eecd5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,4 +1,3 @@ - @@ -19,7 +18,7 @@ {% for name, dev in devices.items() %} {% for name, obj in dev.endpoints.items() %}
-
+

{{ dev.node_id }} / {{ dev.properties['$name'] }} {{ name }}