-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzigbee.py
281 lines (262 loc) · 10.8 KB
/
zigbee.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
#! /usr/bin/python
# This is the an implementation of controlling the Lowe's Iris Smart
# Switch. It will join with a switch and allow you to control the switch
#
# Only ONE switch though. This implementation is a direct port of the
# work I did for an Arduino and illustrates what needs to be done for the
# basic operation of the switch. If you want more than one switch, you can
# adapt this code, or use the ideas in it to make your own control software.
#
# Have fun
from xbee import ZigBee
from apscheduler.scheduler import Scheduler
import logging
import datetime
import time
import serial
import sys
import shlex
#-------------------------------------------------
# the database where I'm storing stuff
DATABASE='/home/pi/database/desert-home'
# on the Raspberry Pi the serial port is ttyAMA0
#XBEEPORT = '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A800HFZH-if00-port0'
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
switchLongAddr = '12'
switchShortAddr = '12'
#-------------------------------------------------
logging.basicConfig()
#------------ 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
# This is a test program, so use global variables and
# save the addresses so they can be used later
global switchLongAddr
global switchShortAddr
switchLongAddr = data['source_addr_long']
switchShortAddr = data['source_addr']
clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1])
sourceAddr = switchLongAddr.encode("hex")
print 'Addr:', sourceAddr, '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 = switchLongAddr,
dest_addr = switchShortAddr,
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 = switchLongAddr,
dest_addr = switchShortAddr,
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 = switchLongAddr,
dest_addr = switchShortAddr,
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 = switchLongAddr,
dest_addr = switchShortAddr,
src_endpoint = '\x00',
dest_endpoint = '\x02',
cluster = '\x00\xf0',
profile = '\xc2\x16',
data = payload4
)
print 'Sent hardware join messages'
elif (clusterId == 0xef):
clusterCmd = ord(data['rf_data'][2])
if (clusterCmd == 0x81):
print 'Instantaneous Power',
print ord(data['rf_data'][3]) + (ord(data['rf_data'][4]) * 256)
elif (clusterCmd == 0x82):
print "Minute Stats:",
print 'Usage, ',
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) )
print usage, 'Watt Seconds ',
print 'Up Time,',
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) )
print upTime, 'Seconds'
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):
print "RSSI value:", ord(data['rf_data'][3])
elif (clusterCmd == 0xfe):
print "Version Information"
else:
print data['rf_data']
elif (clusterId == 0xee):
clusterCmd = ord(data['rf_data'][2])
if (clusterCmd == 0x80):
print "Switch is:",
if (ord(data['rf_data'][3]) & 0x01):
print "ON"
else:
print "OFF"
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
)
#------------------If you want to schedule something to happen -----
#scheditem = Scheduler()
#scheditem.start()
#scheditem.add_interval_job(something, seconds=sometime)
#-----------------------------------------------------------------
# Create XBee library API object, which spawns a new thread
zb = ZigBee(ser, escaped=True, callback=messageReceived)
print "started at ", time.strftime("%A, %B, %d at %H:%M:%S")
print "Enter a number from 0 through 8 to send a command"
while True:
try:
time.sleep(0.001)
str1 = raw_input("")
# Turn Switch Off
if(str1[0] == '0'):
print 'Turn switch off'
databytes1 = '\x01'
databytesOff = '\x00\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xee', '\xc2\x16', '\x01', databytes1)
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xee', '\xc2\x16', '\x02', databytesOff)
# Turn Switch On
if(str1[0] == '1'):
print 'Turn switch on'
databytes1 = '\x01'
databytesOn = '\x01\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xee', '\xc2\x16', '\x01', databytes1)
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xee', '\xc2\x16', '\x02', databytesOn)
# this goes down to the test routine for further hacking
elif (str1[0] == '2'):
#testCommand()
print 'Not Implemented'
# This will get the Version Data, it's a combination of data and text
elif (str1[0] == '3'):
print 'Version Data'
databytes = '\x00\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xf6', '\xc2\x16', '\xfc', databytes)
# This command causes a message return holding the state of the switch
elif (str1[0] == '4'):
print 'Switch Status'
databytes = '\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xee', '\xc2\x16', '\x01', databytes)
# restore normal mode after one of the mode changess that follow
elif (str1[0] == '5'):
print 'Restore Normal Mode'
databytes = '\x00\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xf0', '\xc2\x16', '\xfa', databytes)
# range test - periodic double blink, no control, sends RSSI, no remote control
# remote control works
elif (str1[0] == '6'):
print 'Range Test'
databytes = '\x01\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xf0', '\xc2\x16', '\xfa', databytes)
# locked mode - switch can't be controlled locally, no periodic data
elif (str1[0] == '7'):
print 'Locked Mode'
databytes = '\x02\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xf0', '\xc2\x16', '\xfa', databytes)
# Silent mode, no periodic data, but switch is controllable locally
elif (str1[0] == '8'):
print 'Silent Mode'
databytes = '\x03\x01'
sendSwitch(switchLongAddr, switchShortAddr, '\x00', '\x02', '\x00\xf0', '\xc2\x16', '\xfa', databytes)
# else:
# print 'Unknown Command'
except IndexError:
print "empty line"
except KeyboardInterrupt:
print "Keyboard interrupt"
break
except NameError as e:
print "NameError:",
print e.message.split("'")[1]
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()