-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmultiple.py
309 lines (288 loc) · 10.2 KB
/
multiple.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#!/usr/bin/python
# This is the an implementation of monitoring the Lowe's Iris Smart
# Switch that I use. It will join with a switch and does NOT allow you
# to control the switch
#
# This version has been adapted to support more than one switch and will
# add a new record to my database to hold the data. Adapt it as you need
# to.
#
# Have fun
from xbee import ZigBee
from apscheduler.scheduler import Scheduler
import logging
import datetime
import time
import serial
import sys
import shlex
import sqlite3
#-------------------------------------------------
# the database where I'm storing stuff
#DATABASE='/home/pi/database/desert-home'
DATABASE='/home/volz/xbee-monitor/desert-home.sqlite3'
# on the Raspberry Pi the serial port is ttyAMA0
#XBEEPORT = '/dev/ttyUSB1'
XBEEPORT = '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AD02FMB2-if00-port0'
XBEEBAUD_RATE = 9600
# The XBee addresses I'm dealing with
BROADCAST = '\x00\x00\x00\x00\x00\x00\xff\xff'
UNKNOWN = '\xff\xfe' # This is the 'I don't know' 16 bit address
#-------------------------------------------------
logging.basicConfig()
# this is the only way I could think of to get the address strings to store.
# I take the ord() to get a number, convert to hex, then take the 3 to end
# characters and pad them with zero and finally put the '0x' back on the front
# I put spaces in between each hex character to make it easier to read. This
# left an extra space at the end, so I slice it off in the return statement.
# I hope this makes it easier to grab it out of the database when needed
def addrToString(funnyAddrString):
hexified = ''
for i in funnyAddrString:
hexified += '0x' + hex(ord(i))[2:].zfill(2) + ' '
return hexified[:-1]
#------------ XBee Stuff -------------------------
# Open serial port for use by the XBee
ser = serial.Serial(XBEEPORT, XBEEBAUD_RATE)
# this is a call back function. When a message
# comes in this function will get the data
def messageReceived(data):
#print 'gotta packet'
#print data
clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1])
switchLongAddr = data['source_addr_long']
sourceAddr = switchLongAddr.encode("hex")
print 'Addr:', sourceAddr, 'Cluster ID:', hex(clusterId),
#print 'Cluster ID:', hex(clusterId),
if (clusterId == 0x13):
# This is the device announce message.
# due to timing problems with the switch itself, I don't
# respond to this message, I save the response for later after the
# Match Descriptor request comes in. You'll see it down below.
# if you want to see the data that came in with this message, just
# uncomment the 'print data' comment up above
print 'Device Announce Message'
elif (clusterId == 0x8005):
# this is the Active Endpoint Response This message tells you
# what the device can do, but it isn't constructed correctly to match
# what the switch can do according to the spec. This is another
# message that gets it's response after I receive the Match Descriptor
print 'Active Endpoint Response'
elif (clusterId == 0x0006):
# Match Descriptor Request; this is the point where I finally
# respond to the switch. Several messages are sent to cause the
# switch to join with the controller at a network level and to cause
# it to regard this controller as valid.
#
# First the Active Endpoint Request
payload1 = '\x00\x00'
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x00\x05',
profile = '\x00\x00',
data = payload1
)
print 'sent Active Endpoint'
# Now the Match Descriptor Response
payload2 = '\x00\x00\x00\x00\x01\x02'
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x00',
cluster = '\x80\x06',
profile = '\x00\x00',
data = payload2
)
print 'Sent Match Descriptor'
# Now there are two messages directed at the hardware
# code (rather than the network code. The switch has to
# receive both of these to stay joined.
payload3 = '\x11\x01\x01'
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x02',
cluster = '\x00\xf6',
profile = '\xc2\x16',
data = payload2
)
payload4 = '\x19\x01\xfa\x00\x01'
zb.send('tx_explicit',
dest_addr_long = data['source_addr_long'],
dest_addr = data['source_addr'],
src_endpoint = '\x00',
dest_endpoint = '\x02',
cluster = '\x00\xf0',
profile = '\xc2\x16',
data = payload4
)
print 'Sent hardware join messages'
# now that it should have joined, I'll add a record to the database to
# hold the status. I'll just name the device 'unknown' so it can
# be updated by hand using sqlite3 directly. If the device already exists,
# I'll leave the name alone and just use the existing record
# Yes, this means you'll have to go into the database and assign it a name
#
dbconn = sqlite3.connect(DATABASE)
c = dbconn.cursor()
# See if the device is already in the database
c.execute("select name from smartswitch "
"where longaddress = ?; ",
(addrToString(data['source_addr_long']),))
switchrecord = c.fetchone()
if switchrecord is not None:
print "Device %s is rejoining the network" %(switchrecord[0])
else:
print "Adding new device"
c.execute("insert into smartswitch(name,longaddress, shortaddress, status, watts, twatts, utime)"
"values (?, ?, ?, ?, ?, ?, ?);",
('unknown',
addrToString(data['source_addr_long']),
addrToString(data['source_addr']),
'unknown',
'0',
'0',
time.strftime("%A, %B, %d at %H:%M:%S")))
dbconn.commit()
dbconn.close()
elif (clusterId == 0xef):
clusterCmd = ord(data['rf_data'][2])
if (clusterCmd == 0x81):
usage = ord(data['rf_data'][3]) + (ord(data['rf_data'][4]) * 256)
dbconn = sqlite3.connect(DATABASE)
c = dbconn.cursor()
# This is commented out because I don't need the name
# unless I'm debugging.
# get device name from database
#c.execute("select name from smartswitch "
# "where longaddress = ?; ",
# (addrToString(data['source_addr_long']),))
#name = c.fetchone()[0].capitalize()
#print "%s Instaneous Power, %d Watts" %(name, usage)
# do database updates
c.execute("update smartswitch "
"set watts = ?, "
"shortaddress = ?, "
"utime = ? where longaddress = ?; ",
(usage, addrToString(data['source_addr']),
time.strftime("%A, %B, %d at %H:%M:%S"), addrToString(data['source_addr_long'])))
dbconn.commit()
dbconn.close()
elif (clusterCmd == 0x82):
usage = (ord(data['rf_data'][3]) +
(ord(data['rf_data'][4]) * 256) +
(ord(data['rf_data'][5]) * 256 * 256) +
(ord(data['rf_data'][6]) * 256 * 256 * 256) )
upTime = (ord(data['rf_data'][7]) +
(ord(data['rf_data'][8]) * 256) +
(ord(data['rf_data'][9]) * 256 * 256) +
(ord(data['rf_data'][10]) * 256 * 256 * 256) )
dbconn = sqlite3.connect(DATABASE)
c = dbconn.cursor()
c.execute("select name from smartswitch "
"where longaddress = ?; ",
(addrToString(data['source_addr_long']),))
name = c.fetchone()[0].capitalize()
print "%s Minute Stats: Usage, %d Watt Hours; Uptime, %d Seconds" %(name, usage/3600, upTime)
# update database stuff
c.execute("update smartswitch "
"set twatts = ?, "
"shortaddress = ?, "
"utime = ? where longaddress = ?; ",
(usage, addrToString(data['source_addr']),
time.strftime("%A, %B, %d at %H:%M:%S"), addrToString(data['source_addr_long'])))
dbconn.commit()
dbconn.close()
elif (clusterId == 0xf0):
clusterCmd = ord(data['rf_data'][2])
# print "Cluster Cmd:", hex(clusterCmd),
# if (clusterCmd == 0xfb):
#print "Temperature ??"
# else:
#print "Unimplemented"
elif (clusterId == 0xf6):
clusterCmd = ord(data['rf_data'][2])
# if (clusterCmd == 0xfd):
# pass #print "RSSI value:", ord(data['rf_data'][3])
# elif (clusterCmd == 0xfe):
# pass #print "Version Information"
# else:
# pass #print data['rf_data']
elif (clusterId == 0xee):
clusterCmd = ord(data['rf_data'][2])
status = ''
if (clusterCmd == 0x80):
if (ord(data['rf_data'][3]) & 0x01):
status = "ON"
else:
status = "OFF"
dbconn = sqlite3.connect(DATABASE)
c = dbconn.cursor()
c.execute("select name from smartswitch "
"where longaddress = ?; ",
(addrToString(data['source_addr_long']),))
print c.fetchone()[0].capitalize(),
print "Switch is", status
c.execute("update smartswitch "
"set status = ?, "
"shortaddress = ?, "
"utime = ? where longaddress = ?; ",
(status, addrToString(data['source_addr']),
time.strftime("%A, %B, %d at %H:%M:%S"), addrToString(data['source_addr_long'])))
dbconn.commit()
dbconn.close()
else:
print "Unimplemented Cluster ID", hex(clusterId)
print
def sendSwitch(whereLong, whereShort, srcEndpoint, destEndpoint,
clusterId, profileId, clusterCmd, databytes):
payload = '\x11\x00' + clusterCmd + databytes
# print 'payload',
# for c in payload:
# print hex(ord(c)),
# print
# print 'long address:',
# for c in whereLong:
# print hex(ord(c)),
# print
zb.send('tx_explicit',
dest_addr_long = whereLong,
dest_addr = whereShort,
src_endpoint = srcEndpoint,
dest_endpoint = destEndpoint,
cluster = clusterId,
profile = profileId,
data = payload
)
# This just puts a time stamp in the log file for tracking
def timeInLog():
print time.strftime("%A, %B, %d at %H:%M:%S")
#------------------If you want to schedule something to happen -----
scheditem = Scheduler()
scheditem.start()
scheditem.add_interval_job(timeInLog, minutes=15)
#-----------------------------------------------------------------
# Create XBee library API object, which spawns a new thread
#zb = ZigBee(ser, callback=messageReceived)
zb = ZigBee(ser, escaped=True, callback=messageReceived)
print "started at ", time.strftime("%A, %B, %d at %H:%M:%S")
while True:
try:
time.sleep(0.1)
sys.stdout.flush() # if you're running non interactive, do this
except KeyboardInterrupt:
print "Keyboard interrupt"
break
except:
print "Unexpected error:", sys.exc_info()[0]
break
print "After the while loop"
# halt() must be called before closing the serial
# port in order to ensure proper thread shutdown
zb.halt()
ser.close()