/
mqtt_bokeh.py
136 lines (106 loc) · 4.25 KB
/
mqtt_bokeh.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
Simple bokeh plotter for mqtt
configured to use the first column of the message of the
emonhub/rx/5/values data stream on an emonpi
YMMV
ross feb 25
based on examples and from http://bokeh.pydata.org/en/0.11.0/docs/user_guide/server.html
Works for me using bokeh 0.11.1
run as
bokeh serve --show mqtt_bokeh.py
The --show option will cause a browser to open up a new tab automatically to the address of the running application, which in this case is:
http://localhost:5006/myapp
"""
import numpy as np
from bokeh.models import Button, NumeralTickFormatter
from bokeh.palettes import RdYlBu3
from bokeh.plotting import *
from bokeh.core.properties import value
#from bokeh.transforms import line_downsample
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
from bokeh.io import output_file, show, vform
import paho.mqtt.client as mqtt
import time
import datetime
import sys
OUTFNAME = 'mqtt_emon.xls' ## adjust me
SERVER_IP = "192.168.1.3" ### adjust me
SUBSCRIPTIONS = 'emonhub/rx/5/values' ## and me
class mq():
# adapted from example from https://pypi.python.org/pypi/paho-mqtt
# ross feb 23 2016
# test direct access to emoncms
# will append to outfile so delete to start again
def __init__(self,outfname,subs,server,port,timeout,looptimeout,ds):
self.OUTFNAME = outfname
self.subs = subs
self.server = server
self.port = port
self.timeout = timeout
self.looptimeout = looptimeout
self.ds = ds
self.outf = open(OUTFNAME,'a')
self.client = mqtt.Client()
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.connect(self.server, self.port, self.timeout)
# The callback for when the client receives a CONNACK response from the server.
def on_connect(self,client, userdata, flags, rc):
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
subres = client.subscribe(self.subs)
print("Connected with result code "+str(rc))
# The callback for when a PUBLISH message is received from the server.
def on_message(self,client, userdata, msg):
t = time.time()
tdt = datetime.datetime.fromtimestamp(t)
powr = msg.payload.split(',')[0] # take power as first
self.outf.write('%f\t%s\n' % (t,powr))
self.outf.flush() # use a hard disk, not an SD card...
print >> sys.stdout,'@',t,'y=',powr
self.ds.data['x'].append(tdt)
self.ds.data['y'].append(powr)
self.ds.trigger('data', self.ds.data, self.ds.data)
# create a plot and style its properties
p = figure(plot_width=800, plot_height=600, x_axis_type="datetime")
p.xaxis.axis_label = "Time"
p.xaxis.axis_label_text_color = "darkred"
p.axis.major_label_text_font_size=value("10pt")
p.xaxis.major_label_orientation = "vertical"
p.yaxis.axis_label = "Power (KW)"
p.lod_threshold = 1000
# add a text renderer to out plot (no data yet)
p.line(x=[], y=[], name="power_line",line_width="2",line_color="blue")
renderer = p.select(dict(name="power_line"))
ds = renderer[0].data_source
m = mq(outfname='mqtt_emon.xls',subs=SUBSCRIPTIONS,
server = SERVER_IP,port=1883,timeout=60,looptimeout=1,ds=ds)
def callstop():
m.client.loop_stop()
sys.exit(0)
def update():
m.client.loop(timeout=0.5)
#~ dtall = [datetime.datetime.fromtimestamp(k) for k in ds.data['x']]
#~
#~
#~
#~ daydata = dict(
#~ [datetime.datetime.fromtimestamp(k) for k in ds.data['x']),
#~ [datetime.datetime.fromtimestamp(k) for k in ds.data['y'])
#~ )
#~ source = ColumnDataSource(data)
#~
#~ columns = [
#~ TableColumn(field="dates", title="Date", formatter=DateFormatter()),
#~ TableColumn(field="downloads", title="Downloads"),
#~ ]
#~ data_table = DataTable(source=source, columns=columns, width=400, height=280)
#~
#~ show(vform(data_table))
# add a button widget and configure with the call back
button = Button(label="Stop")
button.on_click(callstop)
# put the button and plot in a layout and add to the document
curdoc().add_root(vplot(button, p))
curdoc().add_periodic_callback(update, 500) # call client.loop to update if data available