Update to Homie v2
parent
ec584a804b
commit
bb0d7e02de
85
main.py
85
main.py
|
@ -2,17 +2,23 @@ import flask
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
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):
|
class SpejsiotDevice(object):
|
||||||
metadata = None
|
|
||||||
online = False
|
|
||||||
node_id = None
|
node_id = None
|
||||||
|
properties = None
|
||||||
|
endpoints = None
|
||||||
|
|
||||||
def __init__(self, node_id):
|
def __init__(self, node_id):
|
||||||
self.node_id = 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):
|
class SpejsiotManager(mqtt.Client):
|
||||||
|
@ -28,44 +34,79 @@ class SpejsiotManager(mqtt.Client):
|
||||||
|
|
||||||
def on_connect(self, client, userdata, flags, rc):
|
def on_connect(self, client, userdata, flags, rc):
|
||||||
self.logger.info('Connected, rc: %d', rc)
|
self.logger.info('Connected, rc: %d', rc)
|
||||||
self.subscribe('iot/+/state')
|
self.subscribe('iot/+/+')
|
||||||
self.subscribe('iot/+/metadata')
|
self.subscribe('iot/+/+/+')
|
||||||
|
|
||||||
def on_message(self, client, userdata, msg):
|
def on_message(self, client, userdata, msg):
|
||||||
self.logger.info('Message received %s: %s', msg.topic, msg.payload)
|
try:
|
||||||
_, node_id, topic = msg.topic.split('/', 2)
|
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():
|
if node_id not in self.devices.keys():
|
||||||
self.devices[node_id] = SpejsiotDevice(node_id)
|
self.devices[node_id] = SpejsiotDevice(node_id)
|
||||||
|
|
||||||
if topic == 'state':
|
if topic.startswith('$'):
|
||||||
self.devices[node_id].online = msg.payload == 'online'
|
self.devices[node_id].properties[topic] = msg.payload
|
||||||
elif topic == 'metadata':
|
else:
|
||||||
metadata = json.loads(msg.payload)
|
endpoint, prop = topic.split('/', 1)
|
||||||
self.devices[node_id].metadata = metadata
|
self.devices[node_id].endpoints[endpoint][prop] = msg.payload
|
||||||
self.devices[node_id].endpoints = {
|
|
||||||
e['name']: {'type': e['type'], 'value': None}
|
|
||||||
for e in metadata['endpoints']
|
|
||||||
}
|
|
||||||
|
|
||||||
for e in self.devices[node_id].endpoints.keys():
|
print(topic)
|
||||||
self.subscribe('iot/%s/%s' % (node_id, e))
|
except:
|
||||||
|
self.logger.exception('fuckup')
|
||||||
|
|
||||||
elif topic in self.devices[node_id].endpoints:
|
def handle_request(self, node_id, endpoint, prop, value):
|
||||||
self.devices[node_id].endpoints[topic]['value'] = msg.payload
|
self.publish('iot/%s/%s/%s/set' % (node_id, endpoint, prop), value, retain=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
manager = SpejsiotManager()
|
manager = SpejsiotManager()
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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,
|
||||||
|
}
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
return flask.render_template('index.html', devices=manager.devices)
|
return flask.render_template('index.html', devices=manager.devices)
|
||||||
|
|
||||||
@app.route('/api/1/devices')
|
@app.route('/api/1/devices')
|
||||||
def api_devices():
|
def api_devices():
|
||||||
return flask.jsonify({k: v.metadata for k, v in manager.devices.items()})
|
return flask.jsonify({k: v.dictify() for k, v in manager.devices.items()})
|
||||||
|
|
||||||
|
@app.route('/api/1/device/<node_id>/<endpoint>/<prop>/<value>')
|
||||||
|
def device_write(node_id, endpoint, prop, value):
|
||||||
|
return flask.jsonify(manager.handle_request(node_id, endpoint, prop, value))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% if endpoint['on'] == 'true' %}
|
||||||
|
<button class="btn btn-success btn-lg">ON</button>
|
||||||
|
{% else %}
|
||||||
|
<button class="btn btn-dange btn-lg">OFF</button>
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<h1>{{ endpoint['degrees'] }}<small>°{{ endpoint.get('unit', 'C') }}</small></h1>
|
||||||
|
{% if endpoint['humidity'] %}
|
||||||
|
<small>{{ endpoint['humidity'] }}% RH</small>
|
||||||
|
{% endif %}
|
|
@ -18,18 +18,16 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% 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() %}
|
||||||
{% if obj['type'] != 'control' %}
|
<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.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.metadata['device_type'] }}</small> {{ name }}</h3>
|
</div>
|
||||||
</div>
|
<div class="panel-body" style="text-align:center">
|
||||||
<div class="panel-body">
|
{{ render_endpoint(dev, obj) }}
|
||||||
<h1>{{ obj['value'] }}<small>unit</small></h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue